Remove protobuf stdlib module (#1976)

This commit is contained in:
Jen Basch authored and GitHub committed 2026-11-10 17:03:09 +00:00
1 parent 155fc5a867
commit 2d7a368b48
25 files changed
+4 -1567

No files matched your search

@@ -77,7 +77,6 @@ endif::[]
:uri-stdlib-netModule: {uri-pkl-stdlib-docs}/net :uri-stdlib-netModule: {uri-pkl-stdlib-docs}/net
:uri-stdlib-net-Url: {uri-stdlib-netModule}/Url :uri-stdlib-net-Url: {uri-stdlib-netModule}/Url
:uri-stdlib-xmlModule: {uri-pkl-stdlib-docs}/xml :uri-stdlib-xmlModule: {uri-pkl-stdlib-docs}/xml
:uri-stdlib-protobufModule: {uri-pkl-stdlib-docs}/protobuf
:uri-stdlib-pklbinaryModule: {uri-pkl-stdlib-docs}/pklbinary :uri-stdlib-pklbinaryModule: {uri-pkl-stdlib-docs}/pklbinary
:uri-stdlib-yamlModule: {uri-pkl-stdlib-docs}/yaml :uri-stdlib-yamlModule: {uri-pkl-stdlib-docs}/yaml
:uri-stdlib-YamlParser: {uri-stdlib-yamlModule}/Parser :uri-stdlib-YamlParser: {uri-stdlib-yamlModule}/Parser
@@ -2752,7 +2752,6 @@ The standard library provides these renderers:
* link:{uri-stdlib-baseModule}/PcfRenderer[PcfRenderer] * link:{uri-stdlib-baseModule}/PcfRenderer[PcfRenderer]
* link:{uri-stdlib-baseModule}/PListRenderer[PListRenderer] * link:{uri-stdlib-baseModule}/PListRenderer[PListRenderer]
* link:{uri-stdlib-baseModule}/PropertiesRenderer[PropertiesRenderer] * link:{uri-stdlib-baseModule}/PropertiesRenderer[PropertiesRenderer]
* link:{uri-stdlib-protobufModule}/Renderer[protobuf.Renderer]
* link:{uri-stdlib-xmlModule}/Renderer[xml.Renderer] * link:{uri-stdlib-xmlModule}/Renderer[xml.Renderer]
* link:{uri-stdlib-baseModule}/YamlRenderer[YamlRenderer] * link:{uri-stdlib-baseModule}/YamlRenderer[YamlRenderer]
-1
View File
@@ -307,7 +307,6 @@ The default output renderer for a module supports the following formats:
* `pcf` * `pcf`
* `plist` * `plist`
* `properties` * `properties`
* `textproto` (deprecated in Pkl 0.33)
* `xml` * `xml`
* `yaml` * `yaml`
-1
View File
@@ -152,7 +152,6 @@ The default output renderer for a module supports the following formats:
* `"pcf"` * `"pcf"`
* `"plist"` * `"plist"`
* `"properties"` * `"properties"`
* `"textproto"` (deprecated in Pkl 0.33)
* `"xml"` * `"xml"`
* `"yaml"` * `"yaml"`
==== ====
@@ -52,7 +52,6 @@ data class CliEvaluatorOptions(
* - `"pcf"` (default) * - `"pcf"` (default)
* - `"plist"` * - `"plist"`
* - `"properties"` * - `"properties"`
* - `"textproto"` (deprecated in Pkl 0.33)
* - `"xml"` * - `"xml"`
* - `"yaml"` * - `"yaml"`
*/ */
@@ -709,8 +709,7 @@ result = someLib.x
assertThat(output).isEqualTo("x: 1\n---\nx: 3\n") assertThat(output).isEqualTo("x: 1\n---\nx: 3\n")
} }
// prototext can't render `Dynamic`. @EnumSource
@EnumSource(names = ["TEXTPROTO"], mode = EnumSource.Mode.EXCLUDE)
@ParameterizedTest(name = "{0} console output ends with newline") @ParameterizedTest(name = "{0} console output ends with newline")
fun `console output ends with newline`(outputFormat: OutputFormat) { fun `console output ends with newline`(outputFormat: OutputFormat) {
val sourceFiles = listOf(writePklFile("test0.pkl", "foo = 0\nbar=\"Baz\"")) val sourceFiles = listOf(writePklFile("test0.pkl", "foo = 0\nbar=\"Baz\""))
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ public enum OutputFormat {
PCF("pcf"), PCF("pcf"),
PROPERTIES("properties"), PROPERTIES("properties"),
PLIST("plist"), PLIST("plist"),
TEXTPROTO("textproto"),
XML("xml"), XML("xml"),
YAML("yaml"); YAML("yaml");
@@ -1,663 +0,0 @@
/*
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.stdlib.protobuf;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Optional;
import org.jspecify.annotations.Nullable;
import org.pkl.core.DurationUnit;
import org.pkl.core.ast.member.ClassProperty;
import org.pkl.core.ast.member.PropertyTypeNode;
import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.TypeNode.BooleanTypeNode;
import org.pkl.core.ast.type.TypeNode.IntSlotTypeNode;
import org.pkl.core.ast.type.TypeNode.IntTypeNode;
import org.pkl.core.ast.type.TypeNode.ListTypeNode;
import org.pkl.core.ast.type.TypeNode.ListingOrMappingTypeNode;
import org.pkl.core.ast.type.TypeNode.ListingTypeNode;
import org.pkl.core.ast.type.TypeNode.MapTypeNode;
import org.pkl.core.ast.type.TypeNode.MappingTypeNode;
import org.pkl.core.ast.type.TypeNode.NonFinalClassTypeNode;
import org.pkl.core.ast.type.TypeNode.NullableTypeNode;
import org.pkl.core.ast.type.TypeNode.ObjectSlotTypeNode;
import org.pkl.core.ast.type.TypeNode.SetTypeNode;
import org.pkl.core.ast.type.TypeNode.StringLiteralTypeNode;
import org.pkl.core.ast.type.TypeNode.StringTypeNode;
import org.pkl.core.ast.type.TypeNode.TypeAliasTypeNode;
import org.pkl.core.ast.type.TypeNode.UnionOfStringLiteralsTypeNode;
import org.pkl.core.ast.type.TypeNode.UnionTypeNode;
import org.pkl.core.ast.type.TypeNode.UnionTypeNodeExploded;
import org.pkl.core.ast.type.VmTypeMismatchException;
import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmClass;
import org.pkl.core.runtime.VmDataSize;
import org.pkl.core.runtime.VmDuration;
import org.pkl.core.runtime.VmDynamic;
import org.pkl.core.runtime.VmExceptionBuilder;
import org.pkl.core.runtime.VmIntSeq;
import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmList;
import org.pkl.core.runtime.VmListing;
import org.pkl.core.runtime.VmMap;
import org.pkl.core.runtime.VmMapping;
import org.pkl.core.runtime.VmNull;
import org.pkl.core.runtime.VmPair;
import org.pkl.core.runtime.VmReference;
import org.pkl.core.runtime.VmRegex;
import org.pkl.core.runtime.VmSet;
import org.pkl.core.runtime.VmType;
import org.pkl.core.runtime.VmTyped;
import org.pkl.core.runtime.VmUtils;
import org.pkl.core.runtime.VmValue;
import org.pkl.core.stdlib.AbstractStringRenderer;
import org.pkl.core.stdlib.ExternalMethod1Node;
import org.pkl.core.stdlib.PklConverter;
import org.pkl.core.util.ArrayCharEscaper;
import org.pkl.core.util.json.JsonEscaper;
public final class RendererNodes {
// Defined here because only relevant to this class.
private static final Identifier SECONDS = Identifier.get("seconds");
private static final Identifier NANOS = Identifier.get("nanos");
private static final ArrayCharEscaper fieldNameEscaper =
ArrayCharEscaper.builder().withEscape('.', "_").withEscape('#', "_").build();
private static final StringTypeNode STRING_TYPE =
new StringTypeNode(VmUtils.unavailableSourceSection());
private static final IntTypeNode INT_TYPE = new IntTypeNode(VmUtils.unavailableSourceSection());
private RendererNodes() {}
public abstract static class renderDocument extends ExternalMethod1Node {
@Specialization
@TruffleBoundary
protected String eval(
VmTyped self, Object value, @Cached("create()") IndirectCallNode callNode) {
var builder = new StringBuilder();
createRenderer(self, builder, callNode).renderDocument(value);
return builder.toString();
}
}
public abstract static class renderValue extends ExternalMethod1Node {
@Specialization
@TruffleBoundary
protected String eval(
VmTyped self, Object value, @Cached("create()") IndirectCallNode callNode) {
var builder = new StringBuilder();
createRenderer(self, builder, callNode).renderValue(value);
return builder.toString();
}
}
public abstract static class renderType extends ExternalMethod1Node {
@SuppressWarnings("unused")
@Specialization
protected String eval(VmTyped self, Object value) {
return renderType(value);
}
}
@TruffleBoundary
private static String renderType(Object value) {
var clazz =
value instanceof VmValue vmValue
? vmValue.getVmClass()
: value instanceof TypeNode typeNode
? typeNode.getType().getVmClass()
: value.getClass();
if (clazz == null) {
throw new VmExceptionBuilder()
.evalError("cannotResolveTypeForProtobuf")
.withProgramValue("Value", value)
.build();
}
var name =
clazz instanceof VmClass vmClass
? vmClass.getSimpleName()
: ((Class<?>) clazz).getSimpleName();
return fieldNameEscaper.escape(name);
}
@TruffleBoundary
private static ProtobufRenderer createRenderer(
VmTyped self, StringBuilder builder, IndirectCallNode callNode) {
var indent = (String) VmUtils.readMember(self, Identifier.INDENT, callNode);
return new ProtobufRenderer(builder, indent, PklConverter.fromRenderer(self, callNode));
}
private static final class ProtobufRenderer extends AbstractStringRenderer {
private final Deque<Identifier> propertyPath = new ArrayDeque<>();
private final Deque<Boolean> wrapperRequirement = new ArrayDeque<>();
private final JsonEscaper jsonEscaper = new JsonEscaper(false);
private @Nullable TypeNode collectionType = null;
public ProtobufRenderer(StringBuilder builder, String indent, PklConverter converter) {
super("Protobuf", builder, indent, converter, true, true);
}
@Override
protected void visitDocument(Object value) {
visit(value);
startNewLine();
}
@Override
protected void visitTopLevelValue(Object value) {
if (value instanceof VmValue && !(value instanceof VmTyped || value instanceof VmDuration)) {
var name = value.getClass().getSimpleName();
if (name.startsWith("Vm")) {
name = name.substring(2);
}
throw new VmExceptionBuilder().evalError("invalidProtobufTopLevelValue", name).build();
}
assert propertyPath.isEmpty() && wrapperRequirement.isEmpty() : "Corrupted traversal stack.";
wrapperRequirement.push(false);
visit(value);
var wrap = wrapperRequirement.pop();
assert !wrap && propertyPath.isEmpty() && wrapperRequirement.isEmpty()
: "Corrupted traversal stack.";
}
@Override
protected void visitRenderDirective(VmTyped value) {
writePropertyName();
// append verbatim
builder.append(VmUtils.readTextProperty(value));
}
@Override
protected void startDynamic(VmDynamic value) {
throw new VmExceptionBuilder()
.evalError("cannotRenderTypeAddConverter", "Dynamic", "Protobuf")
.withProgramValue("Value", value)
.build();
}
@Override
protected void endDynamic(VmDynamic value, boolean isEmpty) {}
@Override
protected void startTyped(VmTyped value) {
wrapperRequirement.push(false);
if (!propertyPath.isEmpty()) {
writePropertyName();
startMessage();
}
}
@Override
protected void endTyped(VmTyped value, boolean isEmpty) {
var popped = wrapperRequirement.pop();
assert !popped : "Corrupted traversal stack.";
if (!propertyPath.isEmpty()) {
endMessage();
}
}
@Override
protected void startListing(VmListing value) {
startWrapper();
}
@Override
protected void endListing(VmListing value, boolean isEmpty) {
endWrapper();
}
@Override
protected void startMapping(VmMapping value) {
startMaplike();
}
@Override
protected void endMapping(VmMapping value, boolean isEmpty) {
endMaplike();
}
private void startMaplike() {
if (requiresWrapper()) {
writePropertyName();
propertyPath.push(Identifier.IT);
startMessage();
}
wrapperRequirement.push(true);
}
private void endMaplike() {
wrapperRequirement.pop();
if (requiresWrapper()) {
endMessage();
var popped = propertyPath.pop();
assert popped == Identifier.IT : "Corrupted traversal stack.";
}
}
@Override
protected void startList(VmList value) {
startWrapper();
}
@Override
protected void endList(VmList value) {
endWrapper();
}
@Override
protected void startSet(VmSet value) {
startWrapper();
}
@Override
protected void endSet(VmSet value) {
endWrapper();
}
@Override
protected void startMap(VmMap value) {
startMaplike();
}
@Override
protected void endMap(VmMap value) {
endMaplike();
}
@Override
protected void visitElement(long index, Object value, boolean isFirst) {
if (requiresWrapper()) {
propertyPath.push(Identifier.IT);
}
var typeName = computeName(collectionType, value);
var addedType = false;
if (typeName != null) {
addedType = true;
writePropertyName();
startMessage();
propertyPath.push(Identifier.get("it_" + typeName));
}
wrapperRequirement.push(true);
visit(value);
var popped = wrapperRequirement.pop();
assert popped : "Corrupted traversal stack.";
if (addedType) {
propertyPath.pop();
endMessage();
}
if (requiresWrapper()) {
propertyPath.pop();
}
}
@Override
protected void visitEntryKey(Object key, boolean isFirst) {
var isDirective = isRenderDirective(key);
var isValidKey =
isDirective || key instanceof Long || key instanceof Boolean || key instanceof String;
if (!isValidKey) {
throw new VmExceptionBuilder()
.evalError("cannotRenderProtobufMapKey")
.withProgramValue("Key", key)
.build();
}
writePropertyName();
startMessage();
startNewLine();
builder.append("key: ");
if (isDirective) {
builder.append(VmUtils.readTextProperty(key));
} else if (key instanceof String string) {
builder.append('"').append(jsonEscaper.escape(string)).append('"');
} else {
builder.append(key);
}
}
@Override
protected void visitEntryValue(Object value) {
propertyPath.push(Identifier.VALUE);
visit(value);
propertyPath.pop();
endMessage();
}
private @Nullable String computeName(@Nullable TypeNode it, Object value) {
if (it instanceof UnionTypeNode unionTypeNode) {
for (var type : unionTypeNode.getElementTypeNodes()) {
if (type instanceof UnionTypeNode) {
var computedName = computeName(type, value);
if (computedName != null) {
return computedName;
}
} else if (type instanceof NonFinalClassTypeNode) {
var clazz = type.getType().getVmClass();
if (value instanceof VmValue vmValue) {
if (clazz == vmValue.getVmClass()) {
return clazz.getSimpleName();
}
}
}
try {
type.execute(VmUtils.createEmptyMaterializedFrame(), value);
} catch (VmTypeMismatchException e) {
continue;
}
return renderType(type);
}
}
return null;
}
@Override
protected void visitProperty(Identifier name, Object value, boolean isFirst) {
var hasWrapper = false;
var hasCollection = false;
var prevCollectionType = collectionType;
Identifier expectedName = null;
propertyPath.push(name);
if (enclosingValue instanceof VmTyped) {
var clazz = value instanceof VmValue vmValue ? vmValue.getVmClass() : value.getClass();
// Compute the name of the field in the wrapper, based on the type, but only if a wrapper
// is required.
var optionalType =
Optional.of(name)
.map(enclosingValue.getVmClass()::getProperty)
.map(ClassProperty::getTypeNode)
.map(PropertyTypeNode::getTypeNode)
.map(this::resolveType);
if (optionalType.isPresent()) {
var type = optionalType.get();
var computedName = computeName(type, value);
if (computedName != null) {
hasWrapper = true;
writePropertyName();
expectedName = Identifier.get("it_" + computedName);
propertyPath.push(expectedName);
startMessage();
} else if (type instanceof ListingOrMappingTypeNode listingOrMappingType) {
hasCollection = true;
collectionType = listingOrMappingType.getValueTypeNode();
} else if (type instanceof ListTypeNode listType) {
hasCollection = true;
collectionType = listType.getElementTypeNode();
} else if (type instanceof MapTypeNode mapType) {
hasCollection = true;
collectionType = mapType.getValueTypeNode();
} else if (type instanceof SetTypeNode setType) {
hasCollection = true;
collectionType = setType.getElementTypeNode();
} else if (type instanceof NonFinalClassTypeNode) {
if (type.getType().getVmClass() != clazz) {
throw new VmExceptionBuilder()
.evalError("cannotRenderSubtypeForProtobuf", type.getType().getVmClass(), clazz)
.build();
}
}
}
}
visit(value);
if (hasWrapper) {
endMessage();
var popped = propertyPath.pop();
assert popped == expectedName : "Corrupted traversal stack.";
} else if (hasCollection) {
collectionType = prevCollectionType;
}
var popped = propertyPath.pop();
assert name == popped : "Corrupted traversal stack.";
}
private void startNewLine() {
if (indent.isEmpty() || builder.isEmpty() || builder.charAt(builder.length() - 1) == '\n')
return;
builder.append('\n');
builder.append(currIndent);
}
private void startWrapper() {
if (requiresWrapper()) {
writePropertyName();
startMessage();
}
}
private void endWrapper() {
if (requiresWrapper()) {
endMessage();
}
}
private boolean requiresWrapper() {
var result = wrapperRequirement.peek();
assert result != null : "Corrupted traversal stack.";
return result;
}
private void startMessage() {
builder.append('{');
increaseIndent();
}
private void endMessage() {
decreaseIndent();
if (builder.charAt(builder.length() - 1) != '{') {
startNewLine();
}
builder.append('}');
}
private void writePropertyName() {
if (propertyPath.isEmpty()) return;
startNewLine();
var name = propertyPath.peek();
assert name != null : "Corrupted traversal stack.";
builder.append(name);
builder.append(": ");
}
@Override
public void visitString(String value) {
writePropertyName();
builder.append('"').append(jsonEscaper.escape(value)).append('"');
}
@Override
public void visitBoolean(Boolean value) {
writePropertyName();
builder.append(value);
}
@Override
public void visitInt(Long value) {
writePropertyName();
builder.append(value);
}
@Override
public void visitFloat(Double value) {
writePropertyName();
builder.append(value);
}
@Override
public void visitDuration(VmDuration value) {
writePropertyName();
var nanos = (long) value.convertTo(DurationUnit.NANOS).getValue() % 1000000000;
var seconds = (long) Math.floor(value.convertTo(DurationUnit.SECONDS).getValue());
startMessage();
propertyPath.push(SECONDS);
writePropertyName();
builder.append(seconds);
propertyPath.pop();
propertyPath.push(NANOS);
writePropertyName();
builder.append(nanos);
propertyPath.pop();
endMessage();
}
@Override
public void visitDataSize(VmDataSize value) {
throw new VmExceptionBuilder()
.evalError("cannotRenderTypeAddConverter", "DataSize", "Protobuf")
.withProgramValue("Value", value)
.build();
}
@Override
public void visitBytes(VmBytes value) {
throw new VmExceptionBuilder()
.evalError("cannotRenderTypeAddConverter", "Bytes", "Protobuf")
.withProgramValue("Value", value)
.build();
}
@Override
public void visitIntSeq(VmIntSeq value) {
writePropertyName();
builder.append(value.toPklString());
}
@Override
public void visitPair(VmPair value) {
writePropertyName();
builder.append(value.toPklString());
}
@Override
public void visitRegex(VmRegex value) {
writePropertyName();
builder.append(value.toPklString());
}
@Override
public void visitNull(VmNull value) {
writePropertyName();
builder.append(value.toPklString());
}
@Override
public void visitReference(VmReference value) {
writePropertyName();
builder.append(value.toPklString());
}
/**
* Resolves types for the purpose of protobuf rendering. "Sees through" nullable types and type
* aliases, simplifies variations of {@code Int} and {@code String} types (literate string
* types, {@code (U)Int_X_} type aliases, etc.), and checks that map key types are legal for
* protobuf.
*/
// TODO: Memoize?
private @Nullable TypeNode resolveType(@Nullable TypeNode type) {
if (type instanceof IntTypeNode
|| type instanceof StringTypeNode
|| type instanceof BooleanTypeNode) {
return type;
} else if (type instanceof IntSlotTypeNode) {
return INT_TYPE;
} else if (type instanceof UnionOfStringLiteralsTypeNode
|| type instanceof StringLiteralTypeNode) {
return STRING_TYPE;
} else if (type instanceof NullableTypeNode nullableType) {
return resolveType(nullableType.getElementTypeNode());
} else if (type instanceof TypeAliasTypeNode typeAliasType) {
return resolveType(
((VmType.AliasType) typeAliasType.getType()).getVmTypeAlias().getTypeNode());
} else if (type instanceof ListingTypeNode listingType) {
var valueType = resolveType(listingType.getValueTypeNode());
assert valueType != null : "Failed to resolve type node.";
type =
requiresWrapper()
? null
: new ListingTypeNode(
VmUtils.unavailableSourceSection(), VmLanguage.get(null), valueType);
return type;
} else if (type instanceof MappingTypeNode mappingType) {
var keyType = resolveType(mappingType.getKeyTypeNode());
if (!(keyType instanceof IntTypeNode
|| keyType instanceof StringTypeNode
|| keyType instanceof BooleanTypeNode)) {
throw new VmExceptionBuilder()
.evalError("cannotRenderProtobufMapKeyType")
.withSourceSection(type.getSourceSection())
.build();
}
var valueType = resolveType(mappingType.getValueTypeNode());
assert valueType != null : "Incomplete or malformed Mapping type";
mappingType =
new MappingTypeNode(
VmUtils.unavailableSourceSection(), VmLanguage.get(null), keyType, valueType);
type = requiresWrapper() ? null : mappingType;
return type;
} else if (type instanceof UnionTypeNode) {
// Some non-obvious normalization going on here:
// - A union type resolves to a union type, unless all element types are string or string
// literal types.
// - All element types are resolved also.
// - All string literal types are combined into a single String case.
var hasString = false;
var elements = new ArrayList<@Nullable TypeNode>();
for (var t : ((UnionTypeNode) type).getElementTypeNodes()) {
var resolved = resolveType(t);
if (resolved instanceof StringLiteralTypeNode || resolved instanceof StringTypeNode) {
if (!hasString) {
hasString = true;
elements.add(new StringTypeNode(VmUtils.unavailableSourceSection()));
}
} else {
elements.add(resolved);
}
}
// Sort classes with lowest-in-hierarchy first, to correctly derive names for oneOf.
elements.sort(
(o1, o2) -> {
if (o1 instanceof ObjectSlotTypeNode && o2 instanceof ObjectSlotTypeNode) {
var t1 = o1.getType().getVmClass();
var t2 = o2.getType().getVmClass();
if (t1 == null || t2 == null) return 0;
return t1.isSubclassOf(t2) ? -1 : t2.isSubclassOf(t1) ? 1 : 0;
}
return 0;
});
type =
hasString && elements.size() == 1
? elements.get(0)
: new UnionTypeNodeExploded(
VmUtils.unavailableSourceSection(), -1, elements.toArray(new TypeNode[0]));
return type;
}
return type;
}
}
}
@@ -1,4 +0,0 @@
@NullMarked
package org.pkl.core.stdlib.protobuf;
import org.jspecify.annotations.NullMarked;
@@ -619,12 +619,6 @@ Cannot render map with non-scalar key as {0}.
cannotRenderObjectWithNonScalarKey=\ cannotRenderObjectWithNonScalarKey=\
Cannot render object with non-scalar key as {0}. Cannot render object with non-scalar key as {0}.
cannotRenderProtobufMapKey=\
Cannot render map with a non-scalar, floating point or byte array key.
cannotRenderProtobufMapKeyType=\
Cannot render map with a non-scalar, floating point or byte array key type.
cannotRenderObjectWithElementsAndOtherMembers=\ cannotRenderObjectWithElementsAndOtherMembers=\
Cannot render object with both elements and properties/entries as {0}. Cannot render object with both elements and properties/entries as {0}.
@@ -759,17 +753,6 @@ The top-level value of an XML property list must have type `Typed`, `Dynamic`, `
invalidPropertiesTopLevelValue=\ invalidPropertiesTopLevelValue=\
The top-level value of a Java properties file must have type `Typed`, `Dynamic`, `Mapping`, or `Map`, but got type `{0}`. The top-level value of a Java properties file must have type `Typed`, `Dynamic`, `Mapping`, or `Map`, but got type `{0}`.
invalidProtobufTopLevelValue=\
The top-level value of a protobuf file must have type `Typed`, but got type `{0}`.
cannotResolveTypeForProtobuf=\
Failed to resolve type required for rendering protobuf.\n\
\n\
Consider adding a type annotation.
cannotRenderSubtypeForProtobuf=\
Cannot render subtype {0} of specified type {1} in protobuf.
cannotExportValue=\ cannotExportValue=\
A value of type `{0}` cannot be exported. A value of type `{0}` cannot be exported.
@@ -1,265 +0,0 @@
amends "../snippetTest.pkl"
import "pkl:protobuf"
local renderer = new protobuf.Renderer {}
local function renderValue(it: Any): String =
renderer.renderValue(it)
/// For testing inheritance; [Foo] is the _base_ class.
local open class Foo {
foo: String
}
/// For testing inheritance; [Bar] is the _derived_ class.
local class Bar extends Foo {
foo = "Bar"
}
/// For testing the rendering of types inside a `VmTyped`.
local class Baz {
foo: Foo
bar: Bar
foos: Listing<Foo>
bars: Mapping<String, Bar>
}
/// For testing how rendering deals with _annotated_ (outer) collection types.
local class CollectionTypes {
emptyList: List<Foo>
list: List<Foo> = List(new Bar {}, new Foo { foo = "test foo" })
emptyListing: Listing<Foo>
listing: Listing<Foo> = new {
new Bar { foo = "listing bar" }
new Foo { foo = "listing foo" }
}
emptyMap: Map<Int, String>
map: Map<Int, Foo> = Map(
1, new Bar { foo = "first" },
2, new Foo { foo = "second" }
)
emptyMapping: Mapping<Int, Bar>
mapping: String =
module.catch(() ->
renderValue(new Mapping<Foo, Bar> {
[new Foo { foo = "key foo" }] = new Bar { foo = "value bar" }
})
)
emptySet: Set<Foo>
set: Set<Foo> = Set(
new Bar { foo = "setOne" },
new Bar { foo = "setTwo" }
)
mappingWithUnannotatedValueType: Mapping<String, Listing> = new {
["some"] {
new Bar {}
new Bar {}
}
}
}
/// For testing _admissable_ mapping key types.
local class MappingKeys {
string: Mapping<String, Bar>
long: Mapping<Int, Bar>
int: Mapping<Int32, Bar>
short: Mapping<Int16, Bar>
boolean: Mapping<Boolean, Bar>
}
/// For testing that using _inadmissable_ mapping key types does indeed throw.
local class InvalidMappingKey {
foo: Mapping<Bar, Bar>
}
local typealias StringLiteralUnion
= *"Qux"
| "quux"
| "QUUX"
local typealias OpenEndedStringUnion
= *"corge"
| String // Not a union of string literals, although fully String-y.
local typealias HeterogeneousUnion
= "Grault"
| "GRAULT"
| Int
| Foo
local typealias SynonymPureEnum = StringLiteralUnion
local class TypeAliasBehavior {
doubleAlias: SynonymPureEnum
}
local class StringLiteralUnionBehavior {
theProperty: StringLiteralUnion
}
local class OpenEndedStringUnionBehavior {
theProperty: OpenEndedStringUnion
}
local class HeterogeneousUnionBehavior {
theHeterogeneousUnion: Listing<HeterogeneousUnion>
}
local class HeterogeneousUnionBehaviorX {
theHeterogeneousUnion: HeterogeneousUnion
}
local class InvalidUnionBehavior {
myMapping: Mapping<HeterogeneousUnion, Int> = new {
[5] = 5
}
}
local class DynamicBehavior {
myDynamic: Dynamic
}
local class DurationBehavior {
myDuration: Duration = 42.ns
}
local class DataSizeBehavior {
myDataSize: DataSize
}
local class SubtypingBehavior {
myFoo: Foo
myFooOrBar: (Foo|Bar)?
}
examples {
["Rendering primitive values"] {
for (v in new Listing {
true
false
-42 as Int8
-42 as Int16
-42 as Int32
-42 as Int
42 as UInt8
42 as UInt16
42 as UInt32
42 as UInt
-13.37 as Float
-13.37 as Number
13.37 as Number
-42 as Number
42 as Number
"Hello world"
10.min
2.ms
120.ns
5.s + 120.ns
}) {
renderValue(v)
}
}
["Collection types"] {
renderValue(new CollectionTypes {})
}
["Typed objects rendered as protobuf.ProtoMessage"] {
renderValue(new Foo { foo = "new" })
renderValue(new Bar {})
renderValue(new Bar { foo = "Baz" })
renderValue(
new Baz {
foo {
foo = "FOO"
}
foos {
foo
outer.bar
}
bars {
["default"] {}
["amended"] {
foo = "Bars"
}
}
}
)
renderValue(new DurationBehavior {})
}
["Unsupported types"] {
module.catch(() -> throw(renderValue(new DynamicBehavior {})))
module.catch(() -> throw(renderValue(new DataSizeBehavior { myDataSize = 10.kb })))
}
["Types not supported at top-level"] {
module.catch(() -> throw(renderValue(Map(3,4))))
module.catch(() -> throw(renderValue(new Mapping {[1] = 2})))
}
["Keys in maps"] {
renderValue(new MappingKeys {
string {
["Hello world"] {}
}
long {
[1337] {}
}
int {
[42] {}
}
short {
[1] {}
}
boolean {
[false] {}
}
})
module.catch(() -> throw(renderValue(new InvalidMappingKey {
foo {
[new Bar {}] {}
}
})))
}
["typealiases"] {
renderValue(new TypeAliasBehavior {})
}
["unions"] {
renderValue(new StringLiteralUnionBehavior {})
renderValue(new OpenEndedStringUnionBehavior {})
renderValue(new HeterogeneousUnionBehaviorX {
theHeterogeneousUnion = "Grault"
})
renderValue(new HeterogeneousUnionBehavior {
theHeterogeneousUnion {
"Grault"
0
new Bar {}
}
})
module.catch(() -> renderValue(new InvalidUnionBehavior {}))
}
["subtypes"] {
renderValue(new SubtypingBehavior {
myFoo = new Foo { foo = "renders" }
})
module.catch(() ->
renderValue(new SubtypingBehavior {
myFoo = new Bar { foo = "shouldn't render" }
})
)
renderValue(new SubtypingBehavior {
myFoo = new Foo { foo = "renders" }
myFooOrBar = new Bar { foo = "also renders" }
})
}
}
@@ -1,75 +0,0 @@
amends "../snippetTest.pkl"
import "pkl:protobuf"
local renderer = new protobuf.Renderer {}
local class NestedStructures {
plainString: String = "plain string"
nestedListing: Listing<Listing<String>>
nestedListing2: Listing<Listing<Listing<Listing<String>>>>
nestedMapping: Mapping<String, Mapping<Int, Boolean>>
listingInMapping: Mapping<String, Listing<String>>
mappingInListing: Listing<Mapping<Int, Boolean>>
nestedList: List<List<String>>
nestedMap: Map<String, Map<Int, Boolean>>
}
examples {
["some"] {
renderer.renderDocument(new NestedStructures {
nestedListing {
new {
"foo"
"bar"
}
new {
"baz"
"qux"
}
}
nestedListing2 {
new {
new {
new {
"foo"
"bar"
"baz"
}
}
}
}
nestedMapping {
["quux"] {
[42] = true
[1337] = false
}
["corge"] {
[0] = false
}
}
listingInMapping {
["grault"] {
"grault1"
"grault2"
}
["garply"] {
"garply1"
}
["waldo"] {}
}
mappingInListing {
new {
[-1] = false
[1] = true
}
new {
[-2] = true
[2] = false
}
}
nestedList = List(List("pigeon", "plugh"), List(), List("xyzzy"))
nestedMap = Map("thud", Map(), "wibble", Map(1, false, 2, true), "wobble", Map(0, false))
})
}
}
@@ -1,28 +0,0 @@
import "pkl:protobuf"
import ".../input-helper/api/annotationConverter.pkl"
class Comment extends Annotation
@protobuf.Property { name = "FOO" }
foo: String = "a"
@annotationConverter.LineComment { text = "bar" }
bar: String = "b"
baz: Nested
class Nested {
@annotationConverter.LineComment { text = "qux" }
qux: String = "c"
quux: String = "c"
}
output {
renderer = new protobuf.Renderer {
convertPropertyTransformers {
[annotationConverter.LineComment] { prefix = " # " }
}
}
}
@@ -2,7 +2,6 @@ amends ".../snippetTest.pkl"
import "pkl:xml" import "pkl:xml"
import "pkl:jsonnet" import "pkl:jsonnet"
import "pkl:protobuf"
examples { examples {
for (format, renderer in new Mapping { for (format, renderer in new Mapping {
@@ -11,7 +10,6 @@ examples {
["jsonnet"] = new jsonnet.Renderer {} ["jsonnet"] = new jsonnet.Renderer {}
["plist"] = new PListRenderer {} ["plist"] = new PListRenderer {}
["properties"] = new PropertiesRenderer {} ["properties"] = new PropertiesRenderer {}
["textproto"] = new protobuf.Renderer {}
["xml"] = new xml.Renderer {} ["xml"] = new xml.Renderer {}
["yaml"] = new YamlRenderer {} ["yaml"] = new YamlRenderer {}
}) { }) {
@@ -23,9 +21,6 @@ examples {
listing = null listing = null
mapping = null mapping = null
} }
when (format == "textproto") {
dynamic = null
}
}) })
renderer.renderDocument(new RenderDirective { text = #"✅\❌ /-#;)<>"# }) renderer.renderDocument(new RenderDirective { text = #"✅\❌ /-#;)<>"# })
} }
@@ -2,13 +2,6 @@ amends ".../snippetTest.pkl"
import "pkl:xml" import "pkl:xml"
import "pkl:jsonnet" import "pkl:jsonnet"
import "pkl:protobuf"
local class TypedTestCase {
property: String
elements: Listing<String>
entries: Mapping<String, String>
}
examples { examples {
for (format, renderer in new Mapping { for (format, renderer in new Mapping {
@@ -26,16 +19,6 @@ examples {
[Listing] = (it: Listing) -> it.join(",") [Listing] = (it: Listing) -> it.join(",")
} }
} |> converters } |> converters
["textproto"] = new protobuf.Renderer {
converters {
[Dynamic] = (it) ->
(it) {
elements = it.elements.toList().toListing()
entries = it.entries.toMap().toMapping()
}.toTyped(TypedTestCase)
[String] = (it) -> new RenderDirective { text = "(\(it))"}
}
}
["xml"] = new xml.Renderer {} |> converters ["xml"] = new xml.Renderer {} |> converters
["yaml"] = new YamlRenderer {} |> converters ["yaml"] = new YamlRenderer {} |> converters
}) { }) {
@@ -1,214 +0,0 @@
examples {
["Rendering primitive values"] {
"true"
"false"
"-42"
"-42"
"-42"
"-42"
"42"
"42"
"42"
"42"
"-13.37"
"-13.37"
"13.37"
"-42"
"42"
"\"Hello world\""
"""
{
seconds: 600
nanos: 0
}
"""
"""
{
seconds: 0
nanos: 2000000
}
"""
"""
{
seconds: 0
nanos: 120
}
"""
"""
{
seconds: 5
nanos: 120
}
"""
}
["Collection types"] {
"""
list: {
foo: "Bar"
}
list: {
foo: "test foo"
}
listing: {
foo: "listing bar"
}
listing: {
foo: "listing foo"
}
map: {
key: 1
value: {
foo: "first"
}
}
map: {
key: 2
value: {
foo: "second"
}
}
mapping: "Cannot call method `renderValue` from here because it is not `const`."
set: {
foo: "setOne"
}
set: {
foo: "setTwo"
}
mappingWithUnannotatedValueType: {
key: "some"
value: {
it: {
foo: "Bar"
}
it: {
foo: "Bar"
}
}
}
"""
}
["Typed objects rendered as protobuf.ProtoMessage"] {
"foo: \"new\""
"foo: \"Bar\""
"foo: \"Baz\""
"""
foo: {
foo: "FOO"
}
bar: {
foo: "Bar"
}
foos: {
foo: "FOO"
}
foos: {
foo: "Bar"
}
bars: {
key: "default"
value: {
foo: "Bar"
}
}
bars: {
key: "amended"
value: {
foo: "Bars"
}
}
"""
"""
myDuration: {
seconds: 0
nanos: 42
}
"""
}
["Unsupported types"] {
"Cannot render value of type `Dynamic` as Protobuf. Value: new Dynamic {}"
"Cannot render value of type `DataSize` as Protobuf. Value: 10.kb"
}
["Types not supported at top-level"] {
"The top-level value of a protobuf file must have type `Typed`, but got type `Map`."
"The top-level value of a protobuf file must have type `Typed`, but got type `Mapping`."
}
["Keys in maps"] {
"""
string: {
key: "Hello world"
value: {
foo: "Bar"
}
}
long: {
key: 1337
value: {
foo: "Bar"
}
}
int: {
key: 42
value: {
foo: "Bar"
}
}
short: {
key: 1
value: {
foo: "Bar"
}
}
boolean: {
key: false
value: {
foo: "Bar"
}
}
"""
"Cannot render map with a non-scalar, floating point or byte array key type."
}
["typealiases"] {
"doubleAlias: \"Qux\""
}
["unions"] {
"theProperty: \"Qux\""
"theProperty: \"corge\""
"""
theHeterogeneousUnion: {
it_String: "Grault"
}
"""
"""
theHeterogeneousUnion: {
it_String: "Grault"
}
theHeterogeneousUnion: {
it_Int: 0
}
theHeterogeneousUnion: {
it_Foo: {
foo: "Bar"
}
}
"""
"Cannot render map with a non-scalar, floating point or byte array key type."
}
["subtypes"] {
"""
myFoo: {
foo: "renders"
}
"""
"Cannot render subtype protobuf#Foo of specified type protobuf#Bar in protobuf."
"""
myFoo: {
foo: "renders"
}
myFooOrBar: {
it_Bar: {
foo: "also renders"
}
}
"""
}
}
@@ -1,118 +0,0 @@
examples {
["some"] {
"""
plainString: "plain string"
nestedListing: {
it: "foo"
it: "bar"
}
nestedListing: {
it: "baz"
it: "qux"
}
nestedListing2: {
it: {
it: {
it: "foo"
it: "bar"
it: "baz"
}
}
}
nestedMapping: {
key: "quux"
value: {
it: {
key: 42
value: true
}
it: {
key: 1337
value: false
}
}
}
nestedMapping: {
key: "corge"
value: {
it: {
key: 0
value: false
}
}
}
listingInMapping: {
key: "grault"
value: {
it: "grault1"
it: "grault2"
}
}
listingInMapping: {
key: "garply"
value: {
it: "garply1"
}
}
listingInMapping: {
key: "waldo"
value: {}
}
mappingInListing: {
it: {
key: -1
value: false
}
it: {
key: 1
value: true
}
}
mappingInListing: {
it: {
key: -2
value: true
}
it: {
key: 2
value: false
}
}
nestedList: {
it: "pigeon"
it: "plugh"
}
nestedList: {}
nestedList: {
it: "xyzzy"
}
nestedMap: {
key: "thud"
value: {}
}
nestedMap: {
key: "wibble"
value: {
it: {
key: 1
value: false
}
it: {
key: 2
value: true
}
}
}
nestedMap: {
key: "wobble"
value: {
it: {
key: 0
value: false
}
}
}
"""
}
}
@@ -1,6 +0,0 @@
FOO: "a"
bar: "b" # bar
baz: {
qux: "c" # qux
quux: "c"
}
@@ -176,37 +176,6 @@ examples {
""" """
"✅\\❌ /-#;)<>" "✅\\❌ /-#;)<>"
} }
["textproto"] {
"""
1"2
3\t4'
"""
"+* /\\-;)❌#✅<>"
"""
directiveProperty: ✅/&\\;)*❌
normalProperty: 42
listing: "foo"
listing: 💰
listing: "baz"
mapping: {
key: "key"
value: "value"
}
mapping: {
key: 🔑
value: 42
}
mapping: {
key: "other"
value: 💰
}
"""
"""
✅\\❌ /-#;)<>
"""
}
["xml"] { ["xml"] {
""" """
1"2 1"2
@@ -79,22 +79,6 @@ examples {
""" """
} }
["textproto"] {
"""
property: (value)
elements: (element1)
elements: (element2)
entries: {
key: (key1)
value: (value1)
}
entries: {
key: (key2)
value: (value2)
}
"""
}
["xml"] { ["xml"] {
""" """
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@@ -20,7 +20,6 @@ pkl:net
pkl:pklbinary pkl:pklbinary
pkl:platform pkl:platform
pkl:Project pkl:Project
pkl:protobuf
pkl:ref pkl:ref
pkl:reflect pkl:reflect
pkl:release pkl:release
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -30,7 +30,6 @@ class ReflectModuleTest {
"pkl:json", "pkl:json",
"pkl:jsonnet", "pkl:jsonnet",
"pkl:math", "pkl:math",
"pkl:protobuf",
"pkl:reflect", "pkl:reflect",
"pkl:settings", "pkl:settings",
"pkl:shell", "pkl:shell",
@@ -1,26 +0,0 @@
start_timestamp_ms: 0
end_timestamp_ms: 100
people: {
key: "Pigeon"
value: {
name: "Pigeon"
profile: {
isExtinct: false
}
fictional: true
height: 1.77
hobbies: "cooing"
role: Lead
}
}
people: {
key: "Parrot"
value: {
name: "Parrot"
fictional: true
height: 1.76
hobbies: "singing"
role: Lead
}
}
owner: "Pigeon"
+1 -4
View File
@@ -26,7 +26,6 @@ import "pkl:math"
import "pkl:net" import "pkl:net"
import "pkl:pklbinary" import "pkl:pklbinary"
// noinspection Deprecated // noinspection Deprecated
import "pkl:protobuf"
import "pkl:xml" import "pkl:xml"
import "pkl:yaml" import "pkl:yaml"
@@ -116,8 +115,6 @@ abstract external class Module {
new PListRenderer {} new PListRenderer {}
else if (format == "properties") else if (format == "properties")
new PropertiesRenderer {} new PropertiesRenderer {}
else if (format == "textproto")
new protobuf.Renderer {}
else if (format == "xml") else if (format == "xml")
new xml.Renderer {} new xml.Renderer {}
else if (format == "yaml") else if (format == "yaml")
@@ -126,7 +123,7 @@ abstract external class Module {
new pklbinary.Renderer {} new pklbinary.Renderer {}
else else
throw( throw(
"Unknown output format: `\(format)`. Supported formats are `json`, `jsonnet`, `pcf`, `plist`, `properties`, `textproto`, `xml`, `yaml`, `pkl-binary`." "Unknown output format: `\(format)`. Supported formats are `json`, `jsonnet`, `pcf`, `plist`, `properties`, `xml`, `yaml`, `pkl-binary`."
) )
text = text =
if (renderer is ValueRenderer) if (renderer is ValueRenderer)
-66
View File
@@ -1,66 +0,0 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
/// A renderer for [Protocol Buffers](https://developers.google.com/protocol-buffers).
///
/// WARNING:
/// This module is deprecated, and scheduled for removal without replacement.
/// For rendering into a protobuf wire format, consider rendering to
/// [ProtoJSON](https://protobuf.dev/programming-guides/json/) using [json.Renderer].
@ModuleInfo { minPklVersion = "0.34.0" }
@Deprecated { since = "0.33.0"; message = "Scheduled for removal without replacement" }
module pkl.protobuf
import "pkl:json"
import "pkl:reflect"
/// A renderer for [Protocol Buffers](https://developers.google.com/protocol-buffers).
/// Note: This class is _experimental_ and not ready for production use.
///
/// As of this release, only Protocol Buffers' text format is supported.
///
/// The [Property] annotation can be used to change how a property name renders into Protobuf.
///
/// Example:
/// ```
/// import "pkl:protobuf"
///
/// @protobuf.Property { name = "wing_span" }
/// wingSpan: Int
/// ```
class Renderer extends ValueRenderer {
/// The characters to use for indenting output.
///
/// Defaults to two spaces.
indent: String = " "
external function renderDocument(value: Any): String
external function renderValue(value: Any): String
/// Returns the canonical name for [type].
external function renderType(type: reflect.Type): String
}
/// Annotate properties of classes and modules with this class to override how a [Renderer]
/// interprets a property's name.
@Since { version = "0.31.0" }
class Property extends ConvertProperty {
/// The new name to use for the annotated property when rendered by [Renderer].
name: String
render = (prop, renderer) -> if (renderer is Renderer) Pair(name, prop.value) else prop
}