Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions buff-json-protoc-plugin/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ For each non-WKT, non-map-entry message type:
- **Cross-file nested encoder calls** — `protoToEncoderClass` only contains messages from `filesToGenerate`. If a nested message is defined in a non-generated file, the fallback to `writer.writeMessage(jsonWriter, nested)` is used (which still finds the encoder at runtime via `instanceof BuffJsonCodecHolder`)
- **Insertion point file paths** — for `java_multiple_files = true`, message insertion points target `package/MessageName.java`; for `false`, they target `package/OuterClassName.java`. The `outer_class_scope` insertion point always targets the outer class file
- **Block comments** (`/** */`) — the `*` prefix on each line is stripped by `CommentGenerator.stripLines()`, producing clean multiline text
- **Generated decoders route fallible parses through `FieldReader`** — `DecoderGenerator` emits `FieldReader.readBytes(reader)` for bytes, `FieldReader.enumNumber(reader, EnumClass.getDescriptor(), name)` for enum names, and `FieldReader.parseIntKey`/`parseUnsignedIntKey`/`parseLongKey`/`parseUnsignedLongKey(reader, keyStr)` for numeric map keys — never inline `BASE64.decode`/`Enum.valueOf`/`Long.parseLong`. This gives generated code the same `JSONException`-for-bad-input contract as the runtime path (see buff-json `Error Contract`); the helpers are `public` because generated code lives in the user's package

## Build

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,9 @@ private static void generateMapFieldRead(StringBuilder sb, FieldDescriptor fd, M
String valuePutter = putter + "Value(" + keyExpr + ", ";
String enumClass = protoToJavaClass.get(valueFd.getEnumType().getFullName());
sb.append(indent).append(" if (reader.isString()) {\n");
sb.append(indent).append(" ").append(valuePutter).append(enumClass)
.append(".valueOf(reader.readString()).getNumber());\n");
sb.append(indent).append(" ").append(valuePutter)
.append("io.suboptimal.buffjson.internal.FieldReader.enumNumber(reader, ").append(enumClass)
.append(".getDescriptor(), reader.readString()));\n");
sb.append(indent).append(" } else {\n");
sb.append(indent).append(" ").append(valuePutter).append("reader.readInt32Value());\n");
sb.append(indent).append(" }\n");
Expand Down Expand Up @@ -223,17 +224,18 @@ private static void emitValueRead(StringBuilder sb, FieldDescriptor fd, String p
sb.append(indent).append(prefix).append("(reader.readBoolValue()").append(closeSuffix).append(");\n");
case STRING ->
sb.append(indent).append(prefix).append("(reader.readString()").append(closeSuffix).append(");\n");
case BYTE_STRING -> sb.append(indent).append(prefix).append(
"(com.google.protobuf.ByteString.copyFrom(io.suboptimal.buffjson.internal.FieldReader.BASE64.decode(reader.readString()))")
.append(closeSuffix).append(");\n");
case BYTE_STRING -> sb.append(indent).append(prefix)
.append("(io.suboptimal.buffjson.internal.FieldReader.readBytes(reader)").append(closeSuffix)
.append(");\n");
case ENUM -> {
// Enum fields use the Value variant: setFoo -> setFooValue, addFoo ->
// addFooValue
String valueName = prefix + "Value";
String enumClass = protoToJavaClass.get(fd.getEnumType().getFullName());
sb.append(indent).append("if (reader.isString()) {\n");
sb.append(indent).append(" ").append(valueName).append("(").append(enumClass)
.append(".valueOf(reader.readString()).getNumber()").append(closeSuffix).append(");\n");
sb.append(indent).append(" ").append(valueName)
.append("(io.suboptimal.buffjson.internal.FieldReader.enumNumber(reader, ").append(enumClass)
.append(".getDescriptor(), reader.readString())").append(closeSuffix).append(");\n");
sb.append(indent).append("} else {\n");
sb.append(indent).append(" ").append(valueName).append("(reader.readInt32Value()")
.append(closeSuffix).append(");\n");
Expand Down Expand Up @@ -301,16 +303,16 @@ private static String mapKeyExpr(FieldDescriptor keyFd) {
case INT -> {
var type = keyFd.getType();
if (type == FieldDescriptor.Type.UINT32 || type == FieldDescriptor.Type.FIXED32)
yield "(int) Long.parseLong(keyStr)";
yield "Integer.parseInt(keyStr)";
yield "io.suboptimal.buffjson.internal.FieldReader.parseUnsignedIntKey(reader, keyStr)";
yield "io.suboptimal.buffjson.internal.FieldReader.parseIntKey(reader, keyStr)";
}
case LONG -> {
var type = keyFd.getType();
if (type == FieldDescriptor.Type.UINT64 || type == FieldDescriptor.Type.FIXED64)
yield "Long.parseUnsignedLong(keyStr)";
yield "Long.parseLong(keyStr)";
yield "io.suboptimal.buffjson.internal.FieldReader.parseUnsignedLongKey(reader, keyStr)";
yield "io.suboptimal.buffjson.internal.FieldReader.parseLongKey(reader, keyStr)";
}
case BOOLEAN -> "Boolean.parseBoolean(keyStr)";
case BOOLEAN -> "io.suboptimal.buffjson.internal.FieldReader.parseBoolKey(reader, keyStr)";
default -> throw new IllegalArgumentException("Unsupported map key type: " + keyFd.getJavaType());
};
}
Expand Down
Loading