diff --git a/bzl/deps.bzl b/bzl/deps.bzl index f39e1185..6be68d1e 100644 --- a/bzl/deps.bzl +++ b/bzl/deps.bzl @@ -47,3 +47,18 @@ def djinni_deps(): url = "https://github.com/bazelbuild/rules_jvm_external/archive/{}.zip".format(rules_jvm_external_tag), sha256 = "62133c125bf4109dfd9d2af64830208356ce4ef8b165a6ef15bbff7460b35c3a", ) + + # Override zlib to use custom BUILD file that excludes problematic gz* files on macOS + zlib_version = "1.2.11" + maybe( + http_archive, + name = "zlib", + build_file = "//third_party/zlib:BUILD.bazel", + sha256 = "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1", + strip_prefix = "zlib-{}".format(zlib_version), + urls = [ + "https://mirror.bazel.build/zlib.net/zlib-{}.tar.gz".format(zlib_version), + "https://zlib.net/zlib-{}.tar.gz".format(zlib_version), + ], + ) + diff --git a/src/source/CppGenerator.scala b/src/source/CppGenerator.scala index 1426d598..6522b7b7 100644 --- a/src/source/CppGenerator.scala +++ b/src/source/CppGenerator.scala @@ -57,7 +57,7 @@ class CppGenerator(spec: Spec) extends Generator(spec) { } } - override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { + override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum): Unit = { val refs = new CppRefs(ident.name) val self = marshal.typename(ident, e) @@ -125,6 +125,10 @@ class CppGenerator(spec: Spec) extends Generator(spec) { }) } + override def generateEnum(origin: String, ident: Ident, doc: Doc, pe: djinni.ast.ProtobufEnum): Unit = { + // C++ protobuf enums are already defined by the protobuf library + } + def shouldConstexpr(c: Const) = { // Make sure we don't constexpr optionals as some might not support it val canConstexpr = c.ty.resolved.base match { diff --git a/src/source/CppMarshal.scala b/src/source/CppMarshal.scala index d3a60f08..a3432207 100644 --- a/src/source/CppMarshal.scala +++ b/src/source/CppMarshal.scala @@ -36,6 +36,7 @@ class CppMarshal(spec: Spec) extends Marshal(spec) { case i: Interface => idCpp.ty(name) case r: Record => idCpp.ty(name) case p: ProtobufMessage => idCpp.ty(name) + case p: ProtobufEnum => idCpp.enumType(name) } override def fqTypename(tm: MExpr): String = toCppType(tm, Some(spec.cppNamespace), Seq()) @@ -44,6 +45,7 @@ class CppMarshal(spec: Spec) extends Marshal(spec) { case i: Interface => withNs(Some(spec.cppNamespace), idCpp.ty(name)) case r: Record => withNs(Some(spec.cppNamespace), idCpp.ty(name)) case p: ProtobufMessage => withNs(Some(p.cpp.ns), idCpp.ty(name)) + case p: ProtobufEnum => withNs(Some(p.cpp.ns), p.cpp.typename) } def paramType(tm: MExpr, scopeSymbols: Seq[String]): String = toCppParamType(tm, None, scopeSymbols) @@ -113,6 +115,8 @@ class CppMarshal(spec: Spec) extends Marshal(spec) { } case p: ProtobufMessage => List(ImportRef(p.cpp.header)) + case p: ProtobufEnum => + List(ImportRef(p.cpp.header)) } case e: MExtern => e.defType match { // Do not forward declare extern types, they might be in arbitrary namespaces. @@ -127,6 +131,8 @@ class CppMarshal(spec: Spec) extends Marshal(spec) { } case p: MProtobuf => List(ImportRef(p.body.cpp.header)) + case p: MProtobufEnum => + List(ImportRef(p.body.cpp.header)) case p: MParam => List() case MVoid => List() } @@ -202,6 +208,7 @@ class CppMarshal(spec: Spec) extends Marshal(spec) { } case p: MParam => idCpp.typeParam(p.name) case p: MProtobuf => withNs(Some(p.body.cpp.ns), p.name) + case p: MProtobufEnum => withNs(Some(p.body.cpp.ns), p.body.cpp.typename) case MVoid => "void" } def expr(tm: MExpr): String = { @@ -269,6 +276,7 @@ class CppMarshal(spec: Spec) extends Marshal(spec) { case r: Record => false case e: Enum => true case p: ProtobufMessage => false + case p: ProtobufEnum => true } // this can be used in c++ generation to know whether a const& should be applied to the parameter or not diff --git a/src/source/JNIGenerator.scala b/src/source/JNIGenerator.scala index 7a839348..13168937 100644 --- a/src/source/JNIGenerator.scala +++ b/src/source/JNIGenerator.scala @@ -37,12 +37,18 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { def writeJniHppFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {})) = writeHppFileGeneric(spec.jniHeaderOutFolder.get, spec.jniNamespace, spec.jniFileIdentStyle)(name, origin, includes, fwds, f, f2) - class JNIRefs(name: String, cppPrefixOverride: Option[String]=None) { + class JNIRefs(name: String, cppPrefixOverride: Option[String]=None, customCppHeader: Option[String]=None) { var jniHpp = mutable.TreeSet[String]() var jniCpp = mutable.TreeSet[String]() - val cppPrefix = cppPrefixOverride.getOrElse(spec.jniIncludeCppPrefix) - jniHpp.add("#include " + q(cppPrefix + spec.cppFileIdentStyle(name) + "." + spec.cppHeaderExt)) + // Add the C++ header - either custom (for protobuf enums) or auto-generated + customCppHeader match { + case Some(header) => jniHpp.add("#include " + header) + case None => + val cppPrefix = cppPrefixOverride.getOrElse(spec.jniIncludeCppPrefix) + jniHpp.add("#include " + q(cppPrefix + spec.cppFileIdentStyle(name) + "." + spec.cppHeaderExt)) + } + jniHpp.add("#include " + q(spec.jniBaseLibIncludePrefix + "djinni_support.hpp")) spec.cppNnHeader match { case Some(nnHdr) => jniHpp.add("#include " + nnHdr) @@ -60,37 +66,56 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { } } - override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { - val refs = new JNIRefs(ident.name) + // Helper method to generate JNI enum from either protobuf or regular enum + private def generateJniEnum(origin: String, ident: Ident, typeDef: TypeDef, customHeader: Option[String], + baseClass: String, isFlags: Boolean, enumCount: Int) { + val refs = new JNIRefs(ident.name, customCppHeader = customHeader) val jniHelper = jniMarshal.helperClass(ident) - val cppSelf = cppMarshal.fqTypename(ident, e) + val cppSelf = cppMarshal.fqTypename(ident.name, typeDef) writeJniHppFile(ident, origin, Iterable.concat(refs.jniHpp, refs.jniCpp), Nil, w => { - val base = if(e.flags) "JniFlags" else "JniEnum" - val count = normalEnumOptions(e).length - w.w(s"class $jniHelper final : ::djinni::$base").bracedSemi { + w.w(s"class $jniHelper final : ::djinni::$baseClass").bracedSemi { w.wlOutdent("public:") w.wl(s"using CppType = $cppSelf;") w.wl(s"using JniType = jobject;") w.wl w.wl(s"using Boxed = $jniHelper;") w.wl - if(e.flags) { + if(isFlags) { w.wl(s"static CppType toCpp(JNIEnv* jniEnv, JniType j) { return static_cast(::djinni::JniClass<$jniHelper>::get().flags(jniEnv, j)); }") - w.wl(s"static ::djinni::LocalRef fromCpp(JNIEnv* jniEnv, CppType c) { return ::djinni::JniClass<$jniHelper>::get().create(jniEnv, static_cast(c), $count); }") + w.wl(s"static ::djinni::LocalRef fromCpp(JNIEnv* jniEnv, CppType c) { return ::djinni::JniClass<$jniHelper>::get().create(jniEnv, static_cast(c), $enumCount); }") } else { w.wl(s"static CppType toCpp(JNIEnv* jniEnv, JniType j) { return static_cast(::djinni::JniClass<$jniHelper>::get().ordinal(jniEnv, j)); }") w.wl(s"static ::djinni::LocalRef fromCpp(JNIEnv* jniEnv, CppType c) { return ::djinni::JniClass<$jniHelper>::get().create(jniEnv, static_cast(c)); }") } w.wl w.wlOutdent("private:") - val classLookup = q(jniMarshal.undecoratedTypename(ident, e)) - w.wl(s"$jniHelper() : $base($classLookup) {}") + val classLookup = typeDef match { + case pe: ProtobufEnum => + // For protobuf enums: baseClass + $ + typename (with $ separators) + val baseClassPath = pe.java.baseClass.replaceAllLiterally(".", "/") + val nestedClassPath = pe.java.typename.replaceAllLiterally(".", "$") + q(s"${baseClassPath}$$${nestedClassPath}") + case _ => + // For regular enums, use the standard naming + q(jniMarshal.undecoratedTypename(ident.name, typeDef)) + } + w.wl(s"$jniHelper() : $baseClass($classLookup) {}") w.wl(s"friend ::djinni::JniClass<$jniHelper>;") } }) } + override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum): Unit = { + val baseClass = if(e.flags) "JniFlags" else "JniEnum" + val enumCount = normalEnumOptions(e).length + generateJniEnum(origin, ident, e, None, baseClass, e.flags, enumCount) + } + + override def generateEnum(origin: String, ident: Ident, doc: Doc, pe: djinni.ast.ProtobufEnum): Unit = { + generateJniEnum(origin, ident, pe, Some(pe.cpp.header), "JniEnum", false, 0) + } + override def generateRecord(origin: String, ident: Ident, doc: Doc, params: Seq[TypeParam], r: Record) { val prefixOverride: Option[String] = if (r.ext.cpp) { Some(spec.cppExtendedRecordIncludePrefix) diff --git a/src/source/JNIMarshal.scala b/src/source/JNIMarshal.scala index 2becd48a..1e429bd8 100644 --- a/src/source/JNIMarshal.scala +++ b/src/source/JNIMarshal.scala @@ -63,6 +63,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) { case _ => headers } } + case pe: MProtobufEnum => List(ImportRef(include(pe.name))) case d: MDef => List(ImportRef(include(d.name))) case e: MExtern => List(ImportRef(resolveExtJniHdr(e.jni.header))) case _ => List() @@ -118,6 +119,12 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) { val prefix = p.body.java.pkg.replaceAllLiterally(".", "/") s"L${prefix}$$${p.name};" } + case p: MProtobufEnum => { + // For protobuf enums: baseClass + $ + typename (with $ separators) + val baseClassPath = p.body.java.baseClass.replaceAllLiterally(".", "/") + val nestedClassPath = p.body.java.typename.replaceAllLiterally(".", "$") + s"L${baseClassPath}$$${nestedClassPath};" + } } // Regexp shortcut @@ -142,6 +149,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) { def helperName(tm: MExpr): String = tm.base match { case d: MDef => withNs(Some(spec.jniNamespace), helperClass(d.name)) + case pe: MProtobufEnum => withNs(Some(spec.jniNamespace), helperClass(pe.name)) case e: MExtern => e.jni.translator case o => withNs(Some("djinni"), o match { case p: MPrimitive => p.idlName match { @@ -161,6 +169,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) { case MSet => "Set" case MMap => "Map" case MProtobuf(_,_,_) => "Protobuf" + case MProtobufEnum(_,_,_) => throw new AssertionError("unreachable") case MArray => "Array" case d: MDef => throw new AssertionError("unreachable") case e: MExtern => throw new AssertionError("unreachable") diff --git a/src/source/JavaGenerator.scala b/src/source/JavaGenerator.scala index 6481a841..470d49d9 100644 --- a/src/source/JavaGenerator.scala +++ b/src/source/JavaGenerator.scala @@ -125,7 +125,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { } } - override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { + override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum): Unit = { val refs = new JavaRefs() writeJavaFile(ident, origin, refs.java, w => { @@ -141,6 +141,10 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { }) } + override def generateEnum(origin: String, ident: Ident, doc: Doc, pe: djinni.ast.ProtobufEnum): Unit = { + // Java protobuf enums are already defined by the protobuf library + } + override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) { val refs = new JavaRefs() diff --git a/src/source/JavaMarshal.scala b/src/source/JavaMarshal.scala index b07ac96b..9a35cb65 100644 --- a/src/source/JavaMarshal.scala +++ b/src/source/JavaMarshal.scala @@ -55,6 +55,7 @@ class JavaMarshal(spec: Spec) extends Marshal(spec) { case _ => List() } case p: MProtobuf => List(ImportRef(withPackage(Some(p.body.java.pkg), p.name))) + case p: MProtobufEnum => List(ImportRef(p.body.java.baseClass + "." + p.body.java.typename)) case e if isEnumFlags(e) => List(ImportRef("java.util.EnumSet")) case _ => List() } @@ -85,6 +86,7 @@ class JavaMarshal(spec: Spec) extends Marshal(spec) { case r: Record => true case e: Enum => true case p: ProtobufMessage => true + case p: djinni.ast.ProtobufEnum => true } def isEnumFlags(m: Meta): Boolean = m match { @@ -118,6 +120,7 @@ class JavaMarshal(spec: Spec) extends Marshal(spec) { case MArray => toJavaType(tm.args.head, packageName) + "[]" case e: MExtern => (if(needRef) e.java.boxed else e.java.typename) + (if(e.java.generic) args(tm) else "") case p: MProtobuf => p.name + case p: MProtobufEnum => p.body.java.baseClass + "." + p.body.java.typename case o => val base = o match { case p: MPrimitive => if (needRef) p.jBoxed else p.jName @@ -132,6 +135,7 @@ class JavaMarshal(spec: Spec) extends Marshal(spec) { case d: MDef => withPackage(packageName, idJava.ty(d.name)) case e: MExtern => throw new AssertionError("unreachable") case e: MProtobuf => throw new AssertionError("unreachable") + case e: MProtobufEnum => throw new AssertionError("unreachable") case p: MParam => idJava.typeParam(p.name) case MVoid => "Void" } diff --git a/src/source/ObjcGenerator.scala b/src/source/ObjcGenerator.scala index dd0cd334..4c27d9b4 100644 --- a/src/source/ObjcGenerator.scala +++ b/src/source/ObjcGenerator.scala @@ -45,7 +45,7 @@ class ObjcGenerator(spec: Spec) extends BaseObjcGenerator(spec) { } } - override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { + override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum): Unit = { val refs = new ObjcRefs() refs.header.add("#import ") @@ -70,6 +70,10 @@ class ObjcGenerator(spec: Spec) extends BaseObjcGenerator(spec) { }) } + override def generateEnum(origin: String, ident: Ident, doc: Doc, pe: djinni.ast.ProtobufEnum): Unit = { + // Objective-C protobuf enums are already defined by the protobuf library + } + def bodyName(ident: String): String = idObjc.ty(ident) + "." + spec.objcppExt // Must be a Obj-C++ file in case the constants are not compile-time constant expressions def writeObjcConstMethDecl(c: Const, w: IndentWriter) { diff --git a/src/source/ObjcMarshal.scala b/src/source/ObjcMarshal.scala index 92aa0d94..44843084 100644 --- a/src/source/ObjcMarshal.scala +++ b/src/source/ObjcMarshal.scala @@ -35,6 +35,10 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) { def cppProtoType(tm: MExpr):Option[String] = tm.base match { case MProtobuf(name, _, ProtobufMessage(cpp, _, None, _)) => Some(cpp.ns + "::" + name) + case MProtobufEnum(name, _, enum) => enum.objc match { + case Some(_) => None // Use ObjC type if available + case None => throw new AssertionError(s"Protobuf enum '$name' requires Objective-C definition in yaml") + } case _ => None } @@ -56,6 +60,7 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) { case DRecord => if(e.objc.pointer) nonnull else None } case MProtobuf(_, _, ProtobufMessage(_, _, None, _)) => None + case MProtobufEnum(_, _, _) => None case _ => nonnull } } @@ -108,6 +113,10 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) { case Some(o) => List(ImportRef(o.header)) case None => List(ImportRef(p.body.cpp.header)) } + case p: MProtobufEnum => p.body.objc match { + case Some(o) => List(ImportRef(o.header)) + case None => List(ImportRef(p.body.cpp.header)) + } case p: MParam => List() } @@ -124,6 +133,7 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) { case r: Record => true case e: Enum => false case p: ProtobufMessage => true + case p: djinni.ast.ProtobufEnum => false } def boxedTypename(td: TypeDecl) = td.body match { @@ -131,6 +141,7 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) { case r: Record => typename(td.ident, r) case e: Enum => "NSNumber" case p: ProtobufMessage => typename(td.ident, p) + case p: djinni.ast.ProtobufEnum => "NSNumber" } // Return value: (Type_Name, Is_Class_Or_Not) @@ -184,6 +195,10 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) { case Some(o) => (o.prefix + p.name, true) case None => (p.body.cpp.ns + "::" + p.name, true) } + case p: MProtobufEnum => p.body.objc match { + case Some(o) => (o.typename, false) + case None => throw new AssertionError(s"Protobuf enum '${p.name}' requires Objective-C definition in yaml") + } case p: MParam => throw new AssertionError("Parameter should not happen at Obj-C top level") case MVoid => ("NSNull", true) } diff --git a/src/source/ObjcppGenerator.scala b/src/source/ObjcppGenerator.scala index ad1d4fe5..4a2e291f 100644 --- a/src/source/ObjcppGenerator.scala +++ b/src/source/ObjcppGenerator.scala @@ -50,7 +50,7 @@ class ObjcppGenerator(spec: Spec) extends BaseObjcGenerator(spec) { private def arcAssert(w: IndentWriter) = w.wl("static_assert(__has_feature(objc_arc), " + q("Djinni requires ARC to be enabled for this file") + ");") - override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { + override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum): Unit = { var imports = mutable.TreeSet[String]() imports.add("#import " + q(spec.objcBaseLibIncludePrefix + "DJIMarshal+Private.h")) imports.add("!#include " + q(spec.objcppIncludeCppPrefix + spec.cppFileIdentStyle(ident) + "." + spec.cppHeaderExt)) @@ -58,6 +58,10 @@ class ObjcppGenerator(spec: Spec) extends BaseObjcGenerator(spec) { writeObjcFile(objcppMarshal.privateHeaderName(ident.name), origin, imports, w => {} ) } + override def generateEnum(origin: String, ident: Ident, doc: Doc, pe: djinni.ast.ProtobufEnum): Unit = { + // Objective-C++ protobuf enums are already defined by the protobuf library + } + def headerName(ident: String): String = idObjc.ty(ident) + "." + spec.objcHeaderExt def privateBodyName(ident: String): String = idObjc.ty(ident) + "+Private." + spec.objcppExt diff --git a/src/source/ObjcppMarshal.scala b/src/source/ObjcppMarshal.scala index 0b1b7d67..fbf9fe8c 100644 --- a/src/source/ObjcppMarshal.scala +++ b/src/source/ObjcppMarshal.scala @@ -55,6 +55,10 @@ class ObjcppMarshal(spec: Spec) extends Marshal(spec) { case Some(o) => List(ImportRef(q(spec.objcBaseLibIncludePrefix + "DJIMarshal+Private.h")), ImportRef(o.header)) case None => List(ImportRef(q(spec.objcBaseLibIncludePrefix + "DJIMarshal+Private.h"))) } + case p: MProtobufEnum => p.body.objc match { + case Some(o) => List(ImportRef(q(spec.objcBaseLibIncludePrefix + "DJIMarshal+Private.h")), ImportRef(o.header)) + case None => List(ImportRef(q(spec.objcBaseLibIncludePrefix + "DJIMarshal+Private.h"))) + } case d: MDef => d.defType match { case DEnum | DInterface => List(ImportRef(include(m))) @@ -98,6 +102,12 @@ class ObjcppMarshal(spec: Spec) extends Marshal(spec) { case None => withNs(Some("djinni"), "ProtobufPassthrough") + "<" + withNs(Some(p.body.cpp.ns), p.name) + ">" } + case p: MProtobufEnum => p.body.objc match { + case Some(o) => withNs(Some("djinni"), "Enum") + "<" + + withNs(Some(p.body.cpp.ns), p.body.cpp.typename) + ", " + o.typename + ">" + case None => withNs(Some("djinni"), "ProtobufEnumPassthrough") + "<" + + withNs(Some(p.body.cpp.ns), p.body.cpp.typename) + ">" + } case o => withNs(Some("djinni"), o match { case p: MPrimitive => p.idlName match { case "i8" => "I8" @@ -120,6 +130,7 @@ class ObjcppMarshal(spec: Spec) extends Marshal(spec) { case e: MExtern => throw new AssertionError("unreachable") case p: MParam => throw new AssertionError("not applicable") case p: MProtobuf => throw new AssertionError("not applicable") + case p: MProtobufEnum => throw new AssertionError("not applicable") case MVoid => "Void" }) } diff --git a/src/source/SwiftBridgingHeaderGenerator.scala b/src/source/SwiftBridgingHeaderGenerator.scala index c1ac3589..e5ebd6b9 100644 --- a/src/source/SwiftBridgingHeaderGenerator.scala +++ b/src/source/SwiftBridgingHeaderGenerator.scala @@ -24,7 +24,14 @@ import djinni.generatorTools._ class SwiftBridgingHeaderGenerator(spec: Spec) extends Generator(spec) { val marshal = new ObjcMarshal(spec) - override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { + override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum): Unit = { + // Swift bridging header not needed for enums + } + override def generateEnum(origin: String, ident: Ident, doc: Doc, pe: djinni.ast.ProtobufEnum): Unit = { + // Swift bridging header not needed for protobuf enums + } + + private def generateRegularEnum(ident: Ident): Unit = { spec.objcSwiftBridgingHeaderWriter.get.write("#import \"" + marshal.headerName(ident) + "\"\n") } diff --git a/src/source/TsGenerator.scala b/src/source/TsGenerator.scala index f743505f..9c48d415 100644 --- a/src/source/TsGenerator.scala +++ b/src/source/TsGenerator.scala @@ -85,6 +85,10 @@ class TsGenerator(spec: Spec) extends Generator(spec) { case MArray => tsArrayType(tm.args.head) case e: MExtern => e.ts.typename + (if (e.ts.generic) args(tm) else "") case p: MProtobuf => p.name + case p: MProtobufEnum => p.body.ts match { + case Some(ts) => ts.typename + case None => p.name + } case o => val base = o match { case p: MPrimitive => tsPrimitiveType(p) @@ -99,6 +103,7 @@ class TsGenerator(spec: Spec) extends Generator(spec) { case d: MDef => idJs.ty(d.name) case e: MExtern => throw new AssertionError("unreachable") case e: MProtobuf => throw new AssertionError("unreachable") + case e: MProtobufEnum => throw new AssertionError("unreachable") case p: MParam => idJs.typeParam(p.name) case MVoid => "void" } @@ -112,6 +117,10 @@ class TsGenerator(spec: Spec) extends Generator(spec) { def references(m: Meta): Seq[TsSymbolRef] = m match { case e: MExtern => List(TsSymbolRef(idJs.ty(e.name), e.ts.module)) case MProtobuf(name, _, ProtobufMessage(_,_,_,Some(ts))) => List(TsSymbolRef(name, ts.module)) + case MProtobufEnum(name, _, enum) => enum.ts match { + case Some(ts) => List(TsSymbolRef(ts.typename, ts.module)) + case None => List() + } case _ => List() } class TsRefs() { @@ -296,7 +305,8 @@ class TsGenerator(spec: Spec) extends Generator(spec) { } }) } - override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) {} + override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum): Unit = {} + override def generateEnum(origin: String, ident: Ident, doc: Doc, pe: djinni.ast.ProtobufEnum): Unit = {} override def generateRecord(origin: String, ident: Ident, doc: Doc, params: Seq[TypeParam], r: Record) {} override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) {} } diff --git a/src/source/WasmGenerator.scala b/src/source/WasmGenerator.scala index bee87e5b..6bf0f23a 100644 --- a/src/source/WasmGenerator.scala +++ b/src/source/WasmGenerator.scala @@ -60,6 +60,7 @@ class WasmGenerator(spec: Spec) extends Generator(spec) { case MSet => "Set" case MMap => "Map" case MProtobuf(_,_,_) => "Protobuf" + case MProtobufEnum(_,_,_) => "Protobuf" case MArray => "Array" case d: MDef => throw new AssertionError("unreachable") case e: MExtern => throw new AssertionError("unreachable") @@ -230,7 +231,7 @@ class WasmGenerator(spec: Spec) extends Generator(spec) { //------------------------------------------------------------------------------ - override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { + override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum): Unit = { val refs = new WasmRefs(ident.name) refs.cpp.add("#include ") val cls = cppMarshal.fqTypename(ident, e) @@ -273,6 +274,10 @@ class WasmGenerator(spec: Spec) extends Generator(spec) { } } + override def generateEnum(origin: String, ident: Ident, doc: Doc, pe: djinni.ast.ProtobufEnum): Unit = { + // WASM protobuf enums are already defined by the protobuf library + } + override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) { val refs = new WasmRefs(ident.name) i.consts.foreach(c => refs.find(c.ty)) diff --git a/src/source/YamlGenerator.scala b/src/source/YamlGenerator.scala index 91d160ce..54d0f519 100644 --- a/src/source/YamlGenerator.scala +++ b/src/source/YamlGenerator.scala @@ -131,6 +131,7 @@ class YamlGenerator(spec: Spec) extends Generator(spec) { case i: Interface => "interface" + ext(i.ext) case r: Record => "record" + ext(r.ext) + deriving(r) case p: ProtobufMessage => "protobuf" + case p: djinni.ast.ProtobufEnum => "protobuf_enum" case Enum(_, false) => "enum" case Enum(_, true) => "flags" } @@ -200,13 +201,17 @@ class YamlGenerator(spec: Spec) extends Generator(spec) { private def meta(td: TypeDecl) = { td match { - case p: ProtobufTypeDecl => MProtobuf(p.ident.name, 0, p.body.asInstanceOf[ProtobufMessage]) + case p: ProtobufTypeDecl => p.body match { + case proto: ProtobufMessage => MProtobuf(p.ident.name, 0, proto) + case enum: djinni.ast.ProtobufEnum => MProtobufEnum(p.ident.name, 0, enum) + } case _ => val defType = td.body match { case i: Interface => DInterface case r: Record => DRecord case e: Enum => DEnum case p: ProtobufMessage => throw new AssertionError("unreachable") + case p: djinni.ast.ProtobufEnum => throw new AssertionError("unreachable") } MDef(td.ident, 0, defType, td.body) } @@ -223,9 +228,8 @@ class YamlGenerator(spec: Spec) extends Generator(spec) { } } - override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { - // unused - } + override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum): Unit = {} + override def generateEnum(origin: String, ident: Ident, doc: Doc, pe: djinni.ast.ProtobufEnum): Unit = {} override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) { // unused @@ -307,5 +311,6 @@ object YamlGenerator { case r: Record => DRecord case e: Enum => DEnum case p: ProtobufMessage => throw new AssertionError("unreachable") + case p: djinni.ast.ProtobufEnum => throw new AssertionError("unreachable") } } diff --git a/src/source/ast.scala b/src/source/ast.scala index 353c2a8b..2356b53e 100644 --- a/src/source/ast.scala +++ b/src/source/ast.scala @@ -97,3 +97,11 @@ object ProtobufMessage { case class Objc(header: String, prefix: String) case class Ts(module: String, ns: String) } + +case class ProtobufEnum(cpp: ProtobufEnum.Cpp, java: ProtobufEnum.Java, objc: Option[ProtobufEnum.Objc], ts: Option[ProtobufEnum.Ts]) extends TypeDef +object ProtobufEnum { + case class Cpp(header: String, ns: String, typename: String) + case class Java(typename: String, baseClass: String) + case class Objc(header: String, typename: String) + case class Ts(module: String, typename: String) +} diff --git a/src/source/generator.scala b/src/source/generator.scala index 907041d4..d72525fd 100644 --- a/src/source/generator.scala +++ b/src/source/generator.scala @@ -417,6 +417,8 @@ abstract class Generator(spec: Spec) def generate(idl: Seq[TypeDecl]) { val decls = idl.collect { case itd: InternTypeDecl => itd } + val protobufDecls = idl.collect { case ptd: ProtobufTypeDecl => ptd } + for (td <- decls) td.body match { case e: Enum => assert(td.params.isEmpty) @@ -424,12 +426,20 @@ abstract class Generator(spec: Spec) case r: Record => generateRecord(td.origin, td.ident, td.doc, td.params, r) case i: Interface => generateInterface(td.origin, td.ident, td.doc, td.params, i) case p: ProtobufMessage => // never need to generate files for protobuf types + case pe: djinni.ast.ProtobufEnum => generateEnum(td.origin, td.ident, td.doc, pe) + } + + for (ptd <- protobufDecls) ptd.body match { + case p: ProtobufMessage => // never need to generate files for protobuf message types + case pe: djinni.ast.ProtobufEnum => generateEnum(ptd.origin, ptd.ident, Doc(Nil), pe) } + generateModule(decls.filter(td => td.body.isInstanceOf[Interface])) } def generateModule(decls: Seq[InternTypeDecl]) {} def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) + def generateEnum(origin: String, ident: Ident, doc: Doc, pe: djinni.ast.ProtobufEnum) def generateRecord(origin: String, ident: Ident, doc: Doc, params: Seq[TypeParam], r: Record) def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) diff --git a/src/source/meta.scala b/src/source/meta.scala index 28e79f12..daef48c0 100644 --- a/src/source/meta.scala +++ b/src/source/meta.scala @@ -85,6 +85,7 @@ object MExtern { ) } case class MProtobuf(name: String, override val numParams: Int, body: ProtobufMessage) extends Meta +case class MProtobufEnum(name: String, override val numParams: Int, body: djinni.ast.ProtobufEnum) extends Meta abstract sealed class MOpaque extends Meta { val idlName: String } diff --git a/src/source/parser.scala b/src/source/parser.scala index 9f267710..f73b758d 100644 --- a/src/source/parser.scala +++ b/src/source/parser.scala @@ -339,6 +339,8 @@ def parseProtobufManifest(origin: String, in: java.io.Reader): Either[Error, Seq // - if `ts` is present then `ts.module` must be present // - `messages` key must be present // - `messages` must be a string list + // - `enums` key is optional + // - `enums` must be a string list val c = Option(doc.get("cpp")) match { case Some(properties) => properties.asInstanceOf[JMap[String, String]].toMap case None => return Left(Error(Loc(fileStack.top, 1, 1), "'cpp' properties not found")) @@ -371,6 +373,40 @@ def parseProtobufManifest(origin: String, in: java.io.Reader): Either[Error, Seq val ident = Ident(message, fileStack.top, Loc(fileStack.top, 1, 1)) tds += ProtobufTypeDecl(ident, Seq.empty[TypeParam], proto, origin); } + + // Handle enums if present + Option(doc.get("enums")) match { + case Some(enumList) => { + for(enumItem <- enumList.asInstanceOf[java.util.List[String]]) { + val ident = Ident(enumItem.replaceAllLiterally(".", "_"), fileStack.top, Loc(fileStack.top, 1, 1)) + + // Extract full java class and use enum item directly as typename + val fullJavaClass = j("class") // Outer Java class determined by filename + val javaTypename = enumItem // Actual (nested) typename, e.g. "OuterMessage.EnumName" + + val enumDef = ProtobufEnum( + ProtobufEnum.Cpp(c("header"), c("namespace"), enumItem.replaceAllLiterally(".", "_")), + ProtobufEnum.Java(javaTypename, fullJavaClass), + Option(doc.get("objc")) match { + case Some(properties) => { + val p = properties.asInstanceOf[JMap[String, String]].toMap + Some(ProtobufEnum.Objc(p("header"), p("prefix") + enumItem.replaceAllLiterally(".", "_"))) + } + case None => None + }, + Option(doc.get("ts")) match { + case Some(properties) => { + val p = properties.asInstanceOf[JMap[String, String]].toMap + Some(ProtobufEnum.Ts(p("module"), enumItem.replaceAllLiterally(".", "_"))) + } + case None => None + } + ) + tds += ProtobufTypeDecl(ident, Seq.empty[TypeParam], enumDef, origin); + } + } + case None => // No enums, continue + } Right(tds) } diff --git a/src/source/resolver.scala b/src/source/resolver.scala index bd63d25b..7997baf1 100644 --- a/src/source/resolver.scala +++ b/src/source/resolver.scala @@ -53,11 +53,15 @@ def resolve(metas: Scope, idl: Seq[TypeDecl]): Option[Error] = { case r: Record => DRecord case i: Interface => DInterface case p: ProtobufMessage => throw new AssertionError("unreachable") + case p: djinni.ast.ProtobufEnum => DEnum } topScope = topScope.updated(typeDecl.ident.name, typeDecl match { case td: InternTypeDecl => MDef(typeDecl.ident.name, typeDecl.params.length, defType, typeDecl.body) case td: ExternTypeDecl => YamlGenerator.metaFromYaml(td) - case td: ProtobufTypeDecl => MProtobuf(td.ident.name, 0, td.body.asInstanceOf[ProtobufMessage]) + case td: ProtobufTypeDecl => td.body match { + case proto: ProtobufMessage => MProtobuf(td.ident.name, 0, proto) + case enum: djinni.ast.ProtobufEnum => MProtobufEnum(td.ident.name, 0, enum) + } }) } @@ -94,6 +98,7 @@ private def resolve(scope: Scope, typeDef: TypeDef) { case r: Record => resolveRecord(scope, r) case i: Interface => resolveInterface(scope, i) case p: ProtobufMessage=> + case p: djinni.ast.ProtobufEnum=> } } @@ -122,6 +127,7 @@ private def resolveConst(typeDef: TypeDef) { case r: Record => f(r.consts) case i: Interface => f(i.consts) case p: ProtobufMessage=> + case p: djinni.ast.ProtobufEnum=> } } @@ -260,6 +266,7 @@ private def resolveRecord(scope: Scope, r: Record) { case DEnum => } case p: MProtobuf => + case p: MProtobufEnum => case _ => throw new AssertionError("Type cannot be resolved") } } diff --git a/test-suite/djinni/vendor/third-party/proto.djinni b/test-suite/djinni/vendor/third-party/proto.djinni index 27022f8e..b091b31d 100644 --- a/test-suite/djinni/vendor/third-party/proto.djinni +++ b/test-suite/djinni/vendor/third-party/proto.djinni @@ -10,6 +10,11 @@ RecordWithEmbeddedCppProto = record { state: PersistingState; } +RecordWithProtobufEnum = record { + phone_type: Person_PhoneType; + priority: Priority; +} + proto_tests = interface +c { static protoToStrings(x: AddressBook): list; static stringsToProto(x: list): AddressBook; @@ -30,4 +35,13 @@ proto_tests = interface +c { static stringToOptionalProto(x: string): optional; static stringToProtoOutcome(x: string): outcome; + + static phoneTypeToString(x: Person_PhoneType): string; + static stringToPhoneType(x: string): Person_PhoneType; + + static enumRecordToString(x: RecordWithProtobufEnum): string; + static stringToEnumRecord(x: string): RecordWithProtobufEnum; + + static priorityToString(x: Priority): string; + static stringToPriority(x: string): Priority; } diff --git a/test-suite/djinni/vendor/third-party/proto.yaml b/test-suite/djinni/vendor/third-party/proto.yaml index b14f0aa6..2d2a3d84 100644 --- a/test-suite/djinni/vendor/third-party/proto.yaml +++ b/test-suite/djinni/vendor/third-party/proto.yaml @@ -13,3 +13,7 @@ ts: messages: - Person - AddressBook + +enums: + - Person.PhoneType + - Priority diff --git a/test-suite/djinni/vendor/third-party/proto/cpp/test.pb.cc b/test-suite/djinni/vendor/third-party/proto/cpp/test.pb.cc index 18280bec..0ac3a4cf 100644 --- a/test-suite/djinni/vendor/third-party/proto/cpp/test.pb.cc +++ b/test-suite/djinni/vendor/third-party/proto/cpp/test.pb.cc @@ -77,7 +77,7 @@ ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Person_PhoneNumber_test_2 {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_Person_PhoneNumber_test_2eproto}, {}}; static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_test_2eproto[3]; -static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_test_2eproto[1]; +static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_test_2eproto[2]; static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_test_2eproto = nullptr; const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_test_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { @@ -130,7 +130,9 @@ const char descriptor_table_protodef_test_2eproto[] PROTOBUF_SECTION_VARIABLE(pr "\004type\030\002 \001(\0162\035.djinni.test.Person.PhoneTy" "pe:\004HOME\"+\n\tPhoneType\022\n\n\006MOBILE\020\000\022\010\n\004HOM" "E\020\001\022\010\n\004WORK\020\002\"2\n\013AddressBook\022#\n\006people\030\001" - " \003(\0132\023.djinni.test.PersonB\t\242\002\006DJTest" + " \003(\0132\023.djinni.test.Person*5\n\010Priority\022\007\n" + "\003LOW\020\000\022\n\n\006MEDIUM\020\001\022\010\n\004HIGH\020\002\022\n\n\006URGENT\020\003" + "B\t\242\002\006DJTest" ; static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_test_2eproto_deps[1] = { }; @@ -141,7 +143,7 @@ static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_tes }; static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_test_2eproto_once; const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_test_2eproto = { - false, false, descriptor_table_protodef_test_2eproto, "test.proto", 316, + false, false, descriptor_table_protodef_test_2eproto, "test.proto", 371, &descriptor_table_test_2eproto_once, descriptor_table_test_2eproto_sccs, descriptor_table_test_2eproto_deps, 3, 0, schemas, file_default_instances, TableStruct_test_2eproto::offsets, file_level_metadata_test_2eproto, 3, file_level_enum_descriptors_test_2eproto, file_level_service_descriptors_test_2eproto, @@ -174,6 +176,22 @@ constexpr Person_PhoneType Person::PhoneType_MIN; constexpr Person_PhoneType Person::PhoneType_MAX; constexpr int Person::PhoneType_ARRAYSIZE; #endif // (__cplusplus < 201703) && (!defined(_MSC_VER) || _MSC_VER >= 1900) +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Priority_descriptor() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_test_2eproto); + return file_level_enum_descriptors_test_2eproto[1]; +} +bool Priority_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + case 3: + return true; + default: + return false; + } +} + // =================================================================== diff --git a/test-suite/djinni/vendor/third-party/proto/cpp/test.pb.h b/test-suite/djinni/vendor/third-party/proto/cpp/test.pb.h index 95eae3d4..2e4a63e6 100644 --- a/test-suite/djinni/vendor/third-party/proto/cpp/test.pb.h +++ b/test-suite/djinni/vendor/third-party/proto/cpp/test.pb.h @@ -100,6 +100,31 @@ inline bool Person_PhoneType_Parse( return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( Person_PhoneType_descriptor(), name, value); } +enum Priority : int { + LOW = 0, + MEDIUM = 1, + HIGH = 2, + URGENT = 3 +}; +bool Priority_IsValid(int value); +constexpr Priority Priority_MIN = LOW; +constexpr Priority Priority_MAX = URGENT; +constexpr int Priority_ARRAYSIZE = Priority_MAX + 1; + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Priority_descriptor(); +template +inline const std::string& Priority_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function Priority_Name."); + return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( + Priority_descriptor(), enum_t_value); +} +inline bool Priority_Parse( + const std::string& name, Priority* value) { + return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( + Priority_descriptor(), name, value); +} // =================================================================== class Person_PhoneNumber PROTOBUF_FINAL : @@ -1154,6 +1179,11 @@ template <> inline const EnumDescriptor* GetEnumDescriptor< ::djinni::test::Person_PhoneType>() { return ::djinni::test::Person_PhoneType_descriptor(); } +template <> struct is_proto_enum< ::djinni::test::Priority> : ::std::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::djinni::test::Priority>() { + return ::djinni::test::Priority_descriptor(); +} PROTOBUF_NAMESPACE_CLOSE diff --git a/test-suite/djinni/vendor/third-party/proto/java/djinni/test/Test.java b/test-suite/djinni/vendor/third-party/proto/java/djinni/test/Test.java index 99691fa7..d61ee0c9 100644 --- a/test-suite/djinni/vendor/third-party/proto/java/djinni/test/Test.java +++ b/test-suite/djinni/vendor/third-party/proto/java/djinni/test/Test.java @@ -14,6 +14,120 @@ public static void registerAllExtensions( registerAllExtensions( (com.google.protobuf.ExtensionRegistryLite) registry); } + /** + * Protobuf enum {@code djinni.test.Priority} + */ + public enum Priority + implements com.google.protobuf.ProtocolMessageEnum { + /** + * LOW = 0; + */ + LOW(0), + /** + * MEDIUM = 1; + */ + MEDIUM(1), + /** + * HIGH = 2; + */ + HIGH(2), + /** + * URGENT = 3; + */ + URGENT(3), + ; + + /** + * LOW = 0; + */ + public static final int LOW_VALUE = 0; + /** + * MEDIUM = 1; + */ + public static final int MEDIUM_VALUE = 1; + /** + * HIGH = 2; + */ + public static final int HIGH_VALUE = 2; + /** + * URGENT = 3; + */ + public static final int URGENT_VALUE = 3; + + + public final int getNumber() { + return value; + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static Priority valueOf(int value) { + return forNumber(value); + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + */ + public static Priority forNumber(int value) { + switch (value) { + case 0: return LOW; + case 1: return MEDIUM; + case 2: return HIGH; + case 3: return URGENT; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static final com.google.protobuf.Internal.EnumLiteMap< + Priority> internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Priority findValueByNumber(int number) { + return Priority.forNumber(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(ordinal()); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return djinni.test.Test.getDescriptor().getEnumTypes().get(0); + } + + private static final Priority[] VALUES = values(); + + public static Priority valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int value; + + private Priority(int value) { + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:djinni.test.Priority) + } + public interface PersonOrBuilder extends // @@protoc_insertion_point(interface_extends:djinni.test.Person) com.google.protobuf.MessageOrBuilder { @@ -2961,7 +3075,9 @@ public djinni.test.Test.AddressBook getDefaultInstanceForType() { "\004type\030\002 \001(\0162\035.djinni.test.Person.PhoneTy" + "pe:\004HOME\"+\n\tPhoneType\022\n\n\006MOBILE\020\000\022\010\n\004HOM" + "E\020\001\022\010\n\004WORK\020\002\"2\n\013AddressBook\022#\n\006people\030\001" + - " \003(\0132\023.djinni.test.PersonB\t\242\002\006DJTest" + " \003(\0132\023.djinni.test.Person*5\n\010Priority\022\007\n" + + "\003LOW\020\000\022\n\n\006MEDIUM\020\001\022\010\n\004HIGH\020\002\022\n\n\006URGENT\020\003" + + "B\t\242\002\006DJTest" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, diff --git a/test-suite/djinni/vendor/third-party/proto/objc/Test.pbobjc.h b/test-suite/djinni/vendor/third-party/proto/objc/Test.pbobjc.h index 783b2231..56b7923b 100644 --- a/test-suite/djinni/vendor/third-party/proto/objc/Test.pbobjc.h +++ b/test-suite/djinni/vendor/third-party/proto/objc/Test.pbobjc.h @@ -32,6 +32,23 @@ CF_EXTERN_C_BEGIN NS_ASSUME_NONNULL_BEGIN +#pragma mark - Enum DJTestPriority + +typedef GPB_ENUM(DJTestPriority) { + DJTestPriority_Low = 0, + DJTestPriority_Medium = 1, + DJTestPriority_High = 2, + DJTestPriority_Urgent = 3, +}; + +GPBEnumDescriptor *DJTestPriority_EnumDescriptor(void); + +/** + * Checks to see if the given value is defined by the enum or was not known at + * the time this source was generated. + **/ +BOOL DJTestPriority_IsValidValue(int32_t value); + #pragma mark - Enum DJTestPerson_PhoneType typedef GPB_ENUM(DJTestPerson_PhoneType) { diff --git a/test-suite/djinni/vendor/third-party/proto/objc/Test.pbobjc.m b/test-suite/djinni/vendor/third-party/proto/objc/Test.pbobjc.m index b2ae9826..8601f5ba 100644 --- a/test-suite/djinni/vendor/third-party/proto/objc/Test.pbobjc.m +++ b/test-suite/djinni/vendor/third-party/proto/objc/Test.pbobjc.m @@ -53,6 +53,45 @@ @implementation DJTestTestRoot return descriptor; } +#pragma mark - Enum DJTestPriority + +GPBEnumDescriptor *DJTestPriority_EnumDescriptor(void) { + static _Atomic(GPBEnumDescriptor*) descriptor = nil; + if (!descriptor) { + static const char *valueNames = + "Low\000Medium\000High\000Urgent\000"; + static const int32_t values[] = { + DJTestPriority_Low, + DJTestPriority_Medium, + DJTestPriority_High, + DJTestPriority_Urgent, + }; + GPBEnumDescriptor *worker = + [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(DJTestPriority) + valueNames:valueNames + values:values + count:(uint32_t)(sizeof(values) / sizeof(int32_t)) + enumVerifier:DJTestPriority_IsValidValue]; + GPBEnumDescriptor *expected = nil; + if (!atomic_compare_exchange_strong(&descriptor, &expected, worker)) { + [worker release]; + } + } + return descriptor; +} + +BOOL DJTestPriority_IsValidValue(int32_t value__) { + switch (value__) { + case DJTestPriority_Low: + case DJTestPriority_Medium: + case DJTestPriority_High: + case DJTestPriority_Urgent: + return YES; + default: + return NO; + } +} + #pragma mark - DJTestPerson @implementation DJTestPerson diff --git a/test-suite/djinni/vendor/third-party/proto/test.proto b/test-suite/djinni/vendor/third-party/proto/test.proto index e95b8a0b..60a919f0 100644 --- a/test-suite/djinni/vendor/third-party/proto/test.proto +++ b/test-suite/djinni/vendor/third-party/proto/test.proto @@ -4,6 +4,13 @@ package djinni.test; option objc_class_prefix = "DJTest"; +enum Priority { + LOW = 0; + MEDIUM = 1; + HIGH = 2; + URGENT = 3; +} + message Person { required string name = 1; required int32 id = 2; diff --git a/test-suite/djinni/vendor/third-party/proto/ts/test.ts b/test-suite/djinni/vendor/third-party/proto/ts/test.ts index 6a943e1f..8038a414 100644 --- a/test-suite/djinni/vendor/third-party/proto/ts/test.ts +++ b/test-suite/djinni/vendor/third-party/proto/ts/test.ts @@ -18,6 +18,14 @@ export enum Person_PhoneType { UNRECOGNIZED = -1, } +export enum Priority { + LOW = 0, + MEDIUM = 1, + HIGH = 2, + URGENT = 3, + UNRECOGNIZED = -1, +} + export function person_PhoneTypeFromJSON(object: any): Person_PhoneType { switch (object) { case 0: @@ -49,6 +57,42 @@ export function person_PhoneTypeToJSON(object: Person_PhoneType): string { } } +export function priorityFromJSON(object: any): Priority { + switch (object) { + case 0: + case "LOW": + return Priority.LOW; + case 1: + case "MEDIUM": + return Priority.MEDIUM; + case 2: + case "HIGH": + return Priority.HIGH; + case 3: + case "URGENT": + return Priority.URGENT; + case -1: + case "UNRECOGNIZED": + default: + return Priority.UNRECOGNIZED; + } +} + +export function priorityToJSON(object: Priority): string { + switch (object) { + case Priority.LOW: + return "LOW"; + case Priority.MEDIUM: + return "MEDIUM"; + case Priority.HIGH: + return "HIGH"; + case Priority.URGENT: + return "URGENT"; + default: + return "UNKNOWN"; + } +} + export interface Person_PhoneNumber { number: string; type: Person_PhoneType; diff --git a/test-suite/generated-src/cpp/RecordWithProtobufEnum.hpp b/test-suite/generated-src/cpp/RecordWithProtobufEnum.hpp new file mode 100644 index 00000000..e77cb016 --- /dev/null +++ b/test-suite/generated-src/cpp/RecordWithProtobufEnum.hpp @@ -0,0 +1,22 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from proto.djinni + +#pragma once + +#include "proto/cpp/test.pb.h" +#include + +namespace testsuite { + +struct RecordWithProtobufEnum final { + ::djinni::test::Person_PhoneType phone_type; + ::djinni::test::Priority priority; + + RecordWithProtobufEnum(::djinni::test::Person_PhoneType phone_type_, + ::djinni::test::Priority priority_) + : phone_type(std::move(phone_type_)) + , priority(std::move(priority_)) + {} +}; + +} // namespace testsuite diff --git a/test-suite/generated-src/cpp/proto_tests.hpp b/test-suite/generated-src/cpp/proto_tests.hpp index 833d0be8..b1a58644 100644 --- a/test-suite/generated-src/cpp/proto_tests.hpp +++ b/test-suite/generated-src/cpp/proto_tests.hpp @@ -15,6 +15,7 @@ namespace testsuite { struct RecordWithEmbeddedCppProto; struct RecordWithEmbeddedProto; +struct RecordWithProtobufEnum; class ProtoTests { public: @@ -45,6 +46,18 @@ class ProtoTests { static std::experimental::optional<::djinni::test::Person> stringToOptionalProto(const std::string & x); static djinni::expected<::djinni::test::Person, int32_t> stringToProtoOutcome(const std::string & x); + + static std::string phoneTypeToString(const ::djinni::test::Person_PhoneType & x); + + static ::djinni::test::Person_PhoneType stringToPhoneType(const std::string & x); + + static std::string enumRecordToString(const RecordWithProtobufEnum & x); + + static RecordWithProtobufEnum stringToEnumRecord(const std::string & x); + + static std::string priorityToString(const ::djinni::test::Priority & x); + + static ::djinni::test::Priority stringToPriority(const std::string & x); }; } // namespace testsuite diff --git a/test-suite/generated-src/java/com/dropbox/djinni/test/ProtoTests.java b/test-suite/generated-src/java/com/dropbox/djinni/test/ProtoTests.java index 809cfa8b..a279ef15 100644 --- a/test-suite/generated-src/java/com/dropbox/djinni/test/ProtoTests.java +++ b/test-suite/generated-src/java/com/dropbox/djinni/test/ProtoTests.java @@ -6,6 +6,8 @@ import com.snapchat.djinni.NativeObjectManager; import djinni.test.Test.AddressBook; import djinni.test.Test.Person; +import djinni.test.Test.Person.PhoneType; +import djinni.test.Test.Priority; import djinni.test2.Test2.PersistingState; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; @@ -52,6 +54,24 @@ public abstract class ProtoTests { @Nonnull public static native com.snapchat.djinni.Outcome stringToProtoOutcome(@Nonnull String x); + @Nonnull + public static native String phoneTypeToString(@Nonnull djinni.test.Test.Person.PhoneType x); + + @Nonnull + public static native djinni.test.Test.Person.PhoneType stringToPhoneType(@Nonnull String x); + + @Nonnull + public static native String enumRecordToString(@Nonnull RecordWithProtobufEnum x); + + @Nonnull + public static native RecordWithProtobufEnum stringToEnumRecord(@Nonnull String x); + + @Nonnull + public static native String priorityToString(@Nonnull djinni.test.Test.Priority x); + + @Nonnull + public static native djinni.test.Test.Priority stringToPriority(@Nonnull String x); + public static final class CppProxy extends ProtoTests { private final long nativeRef; diff --git a/test-suite/generated-src/java/com/dropbox/djinni/test/RecordWithProtobufEnum.java b/test-suite/generated-src/java/com/dropbox/djinni/test/RecordWithProtobufEnum.java new file mode 100644 index 00000000..c7fb5f10 --- /dev/null +++ b/test-suite/generated-src/java/com/dropbox/djinni/test/RecordWithProtobufEnum.java @@ -0,0 +1,43 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from proto.djinni + +package com.dropbox.djinni.test; + +import djinni.test.Test.Person.PhoneType; +import djinni.test.Test.Priority; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +public class RecordWithProtobufEnum { + + + /*package*/ final djinni.test.Test.Person.PhoneType mPhoneType; + + /*package*/ final djinni.test.Test.Priority mPriority; + + public RecordWithProtobufEnum( + @Nonnull djinni.test.Test.Person.PhoneType phoneType, + @Nonnull djinni.test.Test.Priority priority) { + this.mPhoneType = phoneType; + this.mPriority = priority; + } + + @Nonnull + public djinni.test.Test.Person.PhoneType getPhoneType() { + return mPhoneType; + } + + @Nonnull + public djinni.test.Test.Priority getPriority() { + return mPriority; + } + + @Override + public String toString() { + return "RecordWithProtobufEnum{" + + "mPhoneType=" + mPhoneType + + "," + "mPriority=" + mPriority + + "}"; + } + +} diff --git a/test-suite/generated-src/jni/NativePersonPhoneType.hpp b/test-suite/generated-src/jni/NativePersonPhoneType.hpp new file mode 100644 index 00000000..9735cfff --- /dev/null +++ b/test-suite/generated-src/jni/NativePersonPhoneType.hpp @@ -0,0 +1,26 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from proto.yaml + +#pragma once + +#include "djinni_support.hpp" +#include "proto/cpp/test.pb.h" + +namespace djinni_generated { + +class NativePersonPhoneType final : ::djinni::JniEnum { +public: + using CppType = ::djinni::test::Person_PhoneType; + using JniType = jobject; + + using Boxed = NativePersonPhoneType; + + static CppType toCpp(JNIEnv* jniEnv, JniType j) { return static_cast(::djinni::JniClass::get().ordinal(jniEnv, j)); } + static ::djinni::LocalRef fromCpp(JNIEnv* jniEnv, CppType c) { return ::djinni::JniClass::get().create(jniEnv, static_cast(c)); } + +private: + NativePersonPhoneType() : JniEnum("djinni/test/Test$Person$PhoneType") {} + friend ::djinni::JniClass; +}; + +} // namespace djinni_generated diff --git a/test-suite/generated-src/jni/NativePriority.hpp b/test-suite/generated-src/jni/NativePriority.hpp new file mode 100644 index 00000000..ccdfa55a --- /dev/null +++ b/test-suite/generated-src/jni/NativePriority.hpp @@ -0,0 +1,26 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from proto.yaml + +#pragma once + +#include "djinni_support.hpp" +#include "proto/cpp/test.pb.h" + +namespace djinni_generated { + +class NativePriority final : ::djinni::JniEnum { +public: + using CppType = ::djinni::test::Priority; + using JniType = jobject; + + using Boxed = NativePriority; + + static CppType toCpp(JNIEnv* jniEnv, JniType j) { return static_cast(::djinni::JniClass::get().ordinal(jniEnv, j)); } + static ::djinni::LocalRef fromCpp(JNIEnv* jniEnv, CppType c) { return ::djinni::JniClass::get().create(jniEnv, static_cast(c)); } + +private: + NativePriority() : JniEnum("djinni/test/Test$Priority") {} + friend ::djinni::JniClass; +}; + +} // namespace djinni_generated diff --git a/test-suite/generated-src/jni/NativeProtoTests.cpp b/test-suite/generated-src/jni/NativeProtoTests.cpp index 61f46df4..16599c0f 100644 --- a/test-suite/generated-src/jni/NativeProtoTests.cpp +++ b/test-suite/generated-src/jni/NativeProtoTests.cpp @@ -3,8 +3,11 @@ #include "NativeProtoTests.hpp" // my header #include "Marshal.hpp" +#include "NativePersonPhoneType.hpp" +#include "NativePriority.hpp" #include "NativeRecordWithEmbeddedCppProto.hpp" #include "NativeRecordWithEmbeddedProto.hpp" +#include "NativeRecordWithProtobufEnum.hpp" #include "Outcome_jni.hpp" namespace djinni_generated { @@ -125,4 +128,52 @@ CJNIEXPORT ::djinni::Outcome<::djinni::Protobuf<::djinni::test::Person, ::djinni } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) } +CJNIEXPORT jstring JNICALL Java_com_dropbox_djinni_test_ProtoTests_phoneTypeToString(JNIEnv* jniEnv, jobject /*this*/, jobject j_x) +{ + try { + auto r = ::testsuite::ProtoTests::phoneTypeToString(::djinni_generated::NativePersonPhoneType::toCpp(jniEnv, j_x)); + return ::djinni::release(::djinni::String::fromCpp(jniEnv, r)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) +} + +CJNIEXPORT jobject JNICALL Java_com_dropbox_djinni_test_ProtoTests_stringToPhoneType(JNIEnv* jniEnv, jobject /*this*/, jstring j_x) +{ + try { + auto r = ::testsuite::ProtoTests::stringToPhoneType(::djinni::String::toCpp(jniEnv, j_x)); + return ::djinni::release(::djinni_generated::NativePersonPhoneType::fromCpp(jniEnv, r)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) +} + +CJNIEXPORT jstring JNICALL Java_com_dropbox_djinni_test_ProtoTests_enumRecordToString(JNIEnv* jniEnv, jobject /*this*/, jobject j_x) +{ + try { + auto r = ::testsuite::ProtoTests::enumRecordToString(::djinni_generated::NativeRecordWithProtobufEnum::toCpp(jniEnv, j_x)); + return ::djinni::release(::djinni::String::fromCpp(jniEnv, r)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) +} + +CJNIEXPORT jobject JNICALL Java_com_dropbox_djinni_test_ProtoTests_stringToEnumRecord(JNIEnv* jniEnv, jobject /*this*/, jstring j_x) +{ + try { + auto r = ::testsuite::ProtoTests::stringToEnumRecord(::djinni::String::toCpp(jniEnv, j_x)); + return ::djinni::release(::djinni_generated::NativeRecordWithProtobufEnum::fromCpp(jniEnv, r)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) +} + +CJNIEXPORT jstring JNICALL Java_com_dropbox_djinni_test_ProtoTests_priorityToString(JNIEnv* jniEnv, jobject /*this*/, jobject j_x) +{ + try { + auto r = ::testsuite::ProtoTests::priorityToString(::djinni_generated::NativePriority::toCpp(jniEnv, j_x)); + return ::djinni::release(::djinni::String::fromCpp(jniEnv, r)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) +} + +CJNIEXPORT jobject JNICALL Java_com_dropbox_djinni_test_ProtoTests_stringToPriority(JNIEnv* jniEnv, jobject /*this*/, jstring j_x) +{ + try { + auto r = ::testsuite::ProtoTests::stringToPriority(::djinni::String::toCpp(jniEnv, j_x)); + return ::djinni::release(::djinni_generated::NativePriority::fromCpp(jniEnv, r)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) +} + } // namespace djinni_generated diff --git a/test-suite/generated-src/jni/NativeRecordWithProtobufEnum.cpp b/test-suite/generated-src/jni/NativeRecordWithProtobufEnum.cpp new file mode 100644 index 00000000..cf376819 --- /dev/null +++ b/test-suite/generated-src/jni/NativeRecordWithProtobufEnum.cpp @@ -0,0 +1,31 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from proto.djinni + +#include "NativeRecordWithProtobufEnum.hpp" // my header +#include "NativePersonPhoneType.hpp" +#include "NativePriority.hpp" + +namespace djinni_generated { + +NativeRecordWithProtobufEnum::NativeRecordWithProtobufEnum() = default; + +NativeRecordWithProtobufEnum::~NativeRecordWithProtobufEnum() = default; + +auto NativeRecordWithProtobufEnum::fromCpp(JNIEnv* jniEnv, const CppType& c) -> ::djinni::LocalRef { + const auto& data = ::djinni::JniClass::get(); + auto r = ::djinni::LocalRef{jniEnv->NewObject(data.clazz.get(), data.jconstructor, + ::djinni::get(::djinni_generated::NativePersonPhoneType::fromCpp(jniEnv, c.phone_type)), + ::djinni::get(::djinni_generated::NativePriority::fromCpp(jniEnv, c.priority)))}; + ::djinni::jniExceptionCheck(jniEnv); + return r; +} + +auto NativeRecordWithProtobufEnum::toCpp(JNIEnv* jniEnv, JniType j) -> CppType { + ::djinni::JniLocalScope jscope(jniEnv, 3); + assert(j != nullptr); + const auto& data = ::djinni::JniClass::get(); + return {::djinni_generated::NativePersonPhoneType::toCpp(jniEnv, jniEnv->GetObjectField(j, data.field_mPhoneType)), + ::djinni_generated::NativePriority::toCpp(jniEnv, jniEnv->GetObjectField(j, data.field_mPriority))}; +} + +} // namespace djinni_generated diff --git a/test-suite/generated-src/jni/NativeRecordWithProtobufEnum.hpp b/test-suite/generated-src/jni/NativeRecordWithProtobufEnum.hpp new file mode 100644 index 00000000..be146eca --- /dev/null +++ b/test-suite/generated-src/jni/NativeRecordWithProtobufEnum.hpp @@ -0,0 +1,33 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from proto.djinni + +#pragma once + +#include "RecordWithProtobufEnum.hpp" +#include "djinni_support.hpp" + +namespace djinni_generated { + +class NativeRecordWithProtobufEnum final { +public: + using CppType = ::testsuite::RecordWithProtobufEnum; + using JniType = jobject; + + using Boxed = NativeRecordWithProtobufEnum; + + ~NativeRecordWithProtobufEnum(); + + static CppType toCpp(JNIEnv* jniEnv, JniType j); + static ::djinni::LocalRef fromCpp(JNIEnv* jniEnv, const CppType& c); + +private: + NativeRecordWithProtobufEnum(); + friend ::djinni::JniClass; + + const ::djinni::GlobalRef clazz { ::djinni::jniFindClass("com/dropbox/djinni/test/RecordWithProtobufEnum") }; + const jmethodID jconstructor { ::djinni::jniGetMethodID(clazz.get(), "", "(Ldjinni/test/Test$Person$PhoneType;Ldjinni/test/Test$Priority;)V") }; + const jfieldID field_mPhoneType { ::djinni::jniGetFieldID(clazz.get(), "mPhoneType", "Ldjinni/test/Test$Person$PhoneType;") }; + const jfieldID field_mPriority { ::djinni::jniGetFieldID(clazz.get(), "mPriority", "Ldjinni/test/Test$Priority;") }; +}; + +} // namespace djinni_generated diff --git a/test-suite/generated-src/objc/DBProtoTests+Private.mm b/test-suite/generated-src/objc/DBProtoTests+Private.mm index 2bd89cd9..c4a48484 100644 --- a/test-suite/generated-src/objc/DBProtoTests+Private.mm +++ b/test-suite/generated-src/objc/DBProtoTests+Private.mm @@ -5,6 +5,7 @@ #import "DBProtoTests.h" #import "DBRecordWithEmbeddedCppProto+Private.h" #import "DBRecordWithEmbeddedProto+Private.h" +#import "DBRecordWithProtobufEnum+Private.h" #import "DJICppWrapperCache+Private.h" #import "DJIError.h" #import "DJIMarshal+Private.h" @@ -125,6 +126,48 @@ + (nullable DJTestPerson *)stringToOptionalProto:(nonnull NSString *)x { } DJINNI_TRANSLATE_EXCEPTIONS() } ++ (nonnull NSString *)phoneTypeToString:(DJTestPerson_PhoneType)x { + try { + auto objcpp_result_ = ::testsuite::ProtoTests::phoneTypeToString(::djinni::Enum<::djinni::test::Person_PhoneType, DJTestPerson_PhoneType>::toCpp(x)); + return ::djinni::String::fromCpp(objcpp_result_); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + ++ (DJTestPerson_PhoneType)stringToPhoneType:(nonnull NSString *)x { + try { + auto objcpp_result_ = ::testsuite::ProtoTests::stringToPhoneType(::djinni::String::toCpp(x)); + return ::djinni::Enum<::djinni::test::Person_PhoneType, DJTestPerson_PhoneType>::fromCpp(objcpp_result_); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + ++ (nonnull NSString *)enumRecordToString:(nonnull DBRecordWithProtobufEnum *)x { + try { + auto objcpp_result_ = ::testsuite::ProtoTests::enumRecordToString(::djinni_generated::RecordWithProtobufEnum::toCpp(x)); + return ::djinni::String::fromCpp(objcpp_result_); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + ++ (nonnull DBRecordWithProtobufEnum *)stringToEnumRecord:(nonnull NSString *)x { + try { + auto objcpp_result_ = ::testsuite::ProtoTests::stringToEnumRecord(::djinni::String::toCpp(x)); + return ::djinni_generated::RecordWithProtobufEnum::fromCpp(objcpp_result_); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + ++ (nonnull NSString *)priorityToString:(DJTestPriority)x { + try { + auto objcpp_result_ = ::testsuite::ProtoTests::priorityToString(::djinni::Enum<::djinni::test::Priority, DJTestPriority>::toCpp(x)); + return ::djinni::String::fromCpp(objcpp_result_); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + ++ (DJTestPriority)stringToPriority:(nonnull NSString *)x { + try { + auto objcpp_result_ = ::testsuite::ProtoTests::stringToPriority(::djinni::String::toCpp(x)); + return ::djinni::Enum<::djinni::test::Priority, DJTestPriority>::fromCpp(objcpp_result_); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + namespace djinni_generated { auto ProtoTests::toCpp(ObjcType objc) -> CppType diff --git a/test-suite/generated-src/objc/DBProtoTests.h b/test-suite/generated-src/objc/DBProtoTests.h index 58d8fa74..4a248499 100644 --- a/test-suite/generated-src/objc/DBProtoTests.h +++ b/test-suite/generated-src/objc/DBProtoTests.h @@ -3,6 +3,7 @@ #import "DBRecordWithEmbeddedCppProto.h" #import "DBRecordWithEmbeddedProto.h" +#import "DBRecordWithProtobufEnum.h" #import "DJOutcome.h" #import "proto/cpp/test2.pb.h" #import "proto/objc/test.pbobjc.h" @@ -37,4 +38,16 @@ + (nonnull DJOutcome *)stringToProtoOutcome:(nonnull NSString *)x; ++ (nonnull NSString *)phoneTypeToString:(DJTestPerson_PhoneType)x; + ++ (DJTestPerson_PhoneType)stringToPhoneType:(nonnull NSString *)x; + ++ (nonnull NSString *)enumRecordToString:(nonnull DBRecordWithProtobufEnum *)x; + ++ (nonnull DBRecordWithProtobufEnum *)stringToEnumRecord:(nonnull NSString *)x; + ++ (nonnull NSString *)priorityToString:(DJTestPriority)x; + ++ (DJTestPriority)stringToPriority:(nonnull NSString *)x; + @end diff --git a/test-suite/generated-src/objc/DBRecordWithProtobufEnum+Private.h b/test-suite/generated-src/objc/DBRecordWithProtobufEnum+Private.h new file mode 100644 index 00000000..307c90c4 --- /dev/null +++ b/test-suite/generated-src/objc/DBRecordWithProtobufEnum+Private.h @@ -0,0 +1,24 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from proto.djinni + +#import "DBRecordWithProtobufEnum.h" +#include "RecordWithProtobufEnum.hpp" + +static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file"); + +@class DBRecordWithProtobufEnum; + +namespace djinni_generated { + +struct RecordWithProtobufEnum +{ + using CppType = ::testsuite::RecordWithProtobufEnum; + using ObjcType = DBRecordWithProtobufEnum*; + + using Boxed = RecordWithProtobufEnum; + + static CppType toCpp(ObjcType objc); + static ObjcType fromCpp(const CppType& cpp); +}; + +} // namespace djinni_generated diff --git a/test-suite/generated-src/objc/DBRecordWithProtobufEnum+Private.mm b/test-suite/generated-src/objc/DBRecordWithProtobufEnum+Private.mm new file mode 100644 index 00000000..c7865d54 --- /dev/null +++ b/test-suite/generated-src/objc/DBRecordWithProtobufEnum+Private.mm @@ -0,0 +1,24 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from proto.djinni + +#import "DBRecordWithProtobufEnum+Private.h" +#import "DJIMarshal+Private.h" +#import "proto/objc/test.pbobjc.h" +#include + +namespace djinni_generated { + +auto RecordWithProtobufEnum::toCpp(ObjcType obj) -> CppType +{ + assert(obj); + return {::djinni::Enum<::djinni::test::Person_PhoneType, DJTestPerson_PhoneType>::toCpp(obj.phoneType), + ::djinni::Enum<::djinni::test::Priority, DJTestPriority>::toCpp(obj.priority)}; +} + +auto RecordWithProtobufEnum::fromCpp(const CppType& cpp) -> ObjcType +{ + return [[DBRecordWithProtobufEnum alloc] initWithPhoneType:(::djinni::Enum<::djinni::test::Person_PhoneType, DJTestPerson_PhoneType>::fromCpp(cpp.phone_type)) + priority:(::djinni::Enum<::djinni::test::Priority, DJTestPriority>::fromCpp(cpp.priority))]; +} + +} // namespace djinni_generated diff --git a/test-suite/generated-src/objc/DBRecordWithProtobufEnum.h b/test-suite/generated-src/objc/DBRecordWithProtobufEnum.h new file mode 100644 index 00000000..28836f70 --- /dev/null +++ b/test-suite/generated-src/objc/DBRecordWithProtobufEnum.h @@ -0,0 +1,19 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from proto.djinni + +#import "proto/objc/test.pbobjc.h" +#import + +@interface DBRecordWithProtobufEnum : NSObject +- (nonnull instancetype)init NS_UNAVAILABLE; ++ (nonnull instancetype)new NS_UNAVAILABLE; +- (nonnull instancetype)initWithPhoneType:(DJTestPerson_PhoneType)phoneType + priority:(DJTestPriority)priority NS_DESIGNATED_INITIALIZER; ++ (nonnull instancetype)RecordWithProtobufEnumWithPhoneType:(DJTestPerson_PhoneType)phoneType + priority:(DJTestPriority)priority; + +@property (nonatomic, readonly) DJTestPerson_PhoneType phoneType; + +@property (nonatomic, readonly) DJTestPriority priority; + +@end diff --git a/test-suite/generated-src/objc/DBRecordWithProtobufEnum.mm b/test-suite/generated-src/objc/DBRecordWithProtobufEnum.mm new file mode 100644 index 00000000..af62a69a --- /dev/null +++ b/test-suite/generated-src/objc/DBRecordWithProtobufEnum.mm @@ -0,0 +1,33 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from proto.djinni + +#import "DBRecordWithProtobufEnum.h" + + +@implementation DBRecordWithProtobufEnum + +- (nonnull instancetype)initWithPhoneType:(DJTestPerson_PhoneType)phoneType + priority:(DJTestPriority)priority +{ + if (self = [super init]) { + _phoneType = phoneType; + _priority = priority; + } + return self; +} + ++ (nonnull instancetype)RecordWithProtobufEnumWithPhoneType:(DJTestPerson_PhoneType)phoneType + priority:(DJTestPriority)priority +{ + return [[self alloc] initWithPhoneType:phoneType + priority:priority]; +} + +#ifndef DJINNI_DISABLE_DESCRIPTION_METHODS +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ %p phoneType:%@ priority:%@>", self.class, (void *)self, self.phoneType, self.priority]; +} + +#endif +@end diff --git a/test-suite/generated-src/outFileList.txt b/test-suite/generated-src/outFileList.txt index 555f56f8..228a36d5 100644 --- a/test-suite/generated-src/outFileList.txt +++ b/test-suite/generated-src/outFileList.txt @@ -1,5 +1,6 @@ djinni-output-temp/cpp/RecordWithEmbeddedProto.hpp djinni-output-temp/cpp/RecordWithEmbeddedCppProto.hpp +djinni-output-temp/cpp/RecordWithProtobufEnum.hpp djinni-output-temp/cpp/proto_tests.hpp djinni-output-temp/cpp/nested_outcome.hpp djinni-output-temp/cpp/nested_outcome.cpp @@ -74,6 +75,7 @@ djinni-output-temp/cpp/record_with_nested_derivings.cpp djinni-output-temp/cpp/set_record.hpp djinni-output-temp/java/RecordWithEmbeddedProto.java djinni-output-temp/java/RecordWithEmbeddedCppProto.java +djinni-output-temp/java/RecordWithProtobufEnum.java djinni-output-temp/java/ProtoTests.java djinni-output-temp/java/NestedOutcome.java djinni-output-temp/java/TestOutcome.java @@ -136,6 +138,8 @@ djinni-output-temp/jni/NativeRecordWithEmbeddedProto.hpp djinni-output-temp/jni/NativeRecordWithEmbeddedProto.cpp djinni-output-temp/jni/NativeRecordWithEmbeddedCppProto.hpp djinni-output-temp/jni/NativeRecordWithEmbeddedCppProto.cpp +djinni-output-temp/jni/NativeRecordWithProtobufEnum.hpp +djinni-output-temp/jni/NativeRecordWithProtobufEnum.cpp djinni-output-temp/jni/NativeProtoTests.hpp djinni-output-temp/jni/NativeProtoTests.cpp djinni-output-temp/jni/NativeNestedOutcome.hpp @@ -248,10 +252,14 @@ djinni-output-temp/jni/NativeRecordWithNestedDerivings.hpp djinni-output-temp/jni/NativeRecordWithNestedDerivings.cpp djinni-output-temp/jni/NativeSetRecord.hpp djinni-output-temp/jni/NativeSetRecord.cpp +djinni-output-temp/jni/NativePersonPhoneType.hpp +djinni-output-temp/jni/NativePriority.hpp djinni-output-temp/objc/DBRecordWithEmbeddedProto.h djinni-output-temp/objc/DBRecordWithEmbeddedProto.mm djinni-output-temp/objc/DBRecordWithEmbeddedCppProto.h djinni-output-temp/objc/DBRecordWithEmbeddedCppProto.mm +djinni-output-temp/objc/DBRecordWithProtobufEnum.h +djinni-output-temp/objc/DBRecordWithProtobufEnum.mm djinni-output-temp/objc/DBProtoTests.h djinni-output-temp/objc/DBNestedOutcome.h djinni-output-temp/objc/DBNestedOutcome.mm @@ -341,6 +349,8 @@ djinni-output-temp/objc/DBRecordWithEmbeddedProto+Private.h djinni-output-temp/objc/DBRecordWithEmbeddedProto+Private.mm djinni-output-temp/objc/DBRecordWithEmbeddedCppProto+Private.h djinni-output-temp/objc/DBRecordWithEmbeddedCppProto+Private.mm +djinni-output-temp/objc/DBRecordWithProtobufEnum+Private.h +djinni-output-temp/objc/DBRecordWithProtobufEnum+Private.mm djinni-output-temp/objc/DBProtoTests+Private.h djinni-output-temp/objc/DBProtoTests+Private.mm djinni-output-temp/objc/DBNestedOutcome+Private.h @@ -457,6 +467,8 @@ djinni-output-temp/wasm/NativeRecordWithEmbeddedProto.hpp djinni-output-temp/wasm/NativeRecordWithEmbeddedProto.cpp djinni-output-temp/wasm/NativeRecordWithEmbeddedCppProto.hpp djinni-output-temp/wasm/NativeRecordWithEmbeddedCppProto.cpp +djinni-output-temp/wasm/NativeRecordWithProtobufEnum.hpp +djinni-output-temp/wasm/NativeRecordWithProtobufEnum.cpp djinni-output-temp/wasm/NativeProtoTests.hpp djinni-output-temp/wasm/NativeProtoTests.cpp djinni-output-temp/wasm/NativeNestedOutcome.hpp diff --git a/test-suite/generated-src/ts/test.ts b/test-suite/generated-src/ts/test.ts index b25df92d..beda00d1 100644 --- a/test-suite/generated-src/ts/test.ts +++ b/test-suite/generated-src/ts/test.ts @@ -2,7 +2,7 @@ // This file was generated by Djinni from all.djinni import { PersistingState } from "../../djinni/vendor/third-party/proto/ts/test2" -import { AddressBook, Person } from "../../djinni/vendor/third-party/proto/ts/test" +import { AddressBook, Person, Person_PhoneType, Priority } from "../../djinni/vendor/third-party/proto/ts/test" import { Outcome } from "@djinni_support/Outcome" export interface /*record*/ RecordWithEmbeddedProto { @@ -13,6 +13,11 @@ export interface /*record*/ RecordWithEmbeddedCppProto { state: PersistingState; } +export interface /*record*/ RecordWithProtobufEnum { + phoneType: Person_PhoneType; + priority: Priority; +} + export interface ProtoTests { } export interface ProtoTests_statics { @@ -29,6 +34,12 @@ export interface ProtoTests_statics { optionalProtoToString(x: Person | undefined): string; stringToOptionalProto(x: string): Person | undefined; stringToProtoOutcome(x: string): Outcome; + phoneTypeToString(x: Person_PhoneType): string; + stringToPhoneType(x: string): Person_PhoneType; + enumRecordToString(x: RecordWithProtobufEnum): string; + stringToEnumRecord(x: string): RecordWithProtobufEnum; + priorityToString(x: Priority): string; + stringToPriority(x: string): Priority; } export interface /*record*/ NestedOutcome { diff --git a/test-suite/generated-src/wasm/NativeProtoTests.cpp b/test-suite/generated-src/wasm/NativeProtoTests.cpp index d7b411a9..83271bd2 100644 --- a/test-suite/generated-src/wasm/NativeProtoTests.cpp +++ b/test-suite/generated-src/wasm/NativeProtoTests.cpp @@ -4,6 +4,7 @@ #include "NativeProtoTests.hpp" // my header #include "NativeRecordWithEmbeddedCppProto.hpp" #include "NativeRecordWithEmbeddedProto.hpp" +#include "NativeRecordWithProtobufEnum.hpp" #include "Outcome_wasm.hpp" namespace djinni_generated { @@ -131,6 +132,60 @@ em::val NativeProtoTests::stringToProtoOutcome(const std::string& w_x) { return ::djinni::ExceptionHandlingTraits<::djinni::Outcome<::djinni::Protobuf<::djinni::test::Person, ::djinni::JsClassName<'p','r','o','t','o','t','e','s','t','.','P','e','r','s','o','n'>>, ::djinni::I32>>::handleNativeException(e); } } +std::string NativeProtoTests::phoneTypeToString(const em::val& w_x) { + try { + auto r = ::testsuite::ProtoTests::phoneTypeToString(::djinni::Protobuf::toCpp(w_x)); + return ::djinni::String::fromCpp(r); + } + catch(const std::exception& e) { + return ::djinni::ExceptionHandlingTraits<::djinni::String>::handleNativeException(e); + } +} +em::val NativeProtoTests::stringToPhoneType(const std::string& w_x) { + try { + auto r = ::testsuite::ProtoTests::stringToPhoneType(::djinni::String::toCpp(w_x)); + return ::djinni::Protobuf::fromCpp(r); + } + catch(const std::exception& e) { + return ::djinni::ExceptionHandlingTraits<::djinni::Protobuf>::handleNativeException(e); + } +} +std::string NativeProtoTests::enumRecordToString(const em::val& w_x) { + try { + auto r = ::testsuite::ProtoTests::enumRecordToString(::djinni_generated::NativeRecordWithProtobufEnum::toCpp(w_x)); + return ::djinni::String::fromCpp(r); + } + catch(const std::exception& e) { + return ::djinni::ExceptionHandlingTraits<::djinni::String>::handleNativeException(e); + } +} +em::val NativeProtoTests::stringToEnumRecord(const std::string& w_x) { + try { + auto r = ::testsuite::ProtoTests::stringToEnumRecord(::djinni::String::toCpp(w_x)); + return ::djinni_generated::NativeRecordWithProtobufEnum::fromCpp(r); + } + catch(const std::exception& e) { + return ::djinni::ExceptionHandlingTraits<::djinni_generated::NativeRecordWithProtobufEnum>::handleNativeException(e); + } +} +std::string NativeProtoTests::priorityToString(const em::val& w_x) { + try { + auto r = ::testsuite::ProtoTests::priorityToString(::djinni::Protobuf::toCpp(w_x)); + return ::djinni::String::fromCpp(r); + } + catch(const std::exception& e) { + return ::djinni::ExceptionHandlingTraits<::djinni::String>::handleNativeException(e); + } +} +em::val NativeProtoTests::stringToPriority(const std::string& w_x) { + try { + auto r = ::testsuite::ProtoTests::stringToPriority(::djinni::String::toCpp(w_x)); + return ::djinni::Protobuf::fromCpp(r); + } + catch(const std::exception& e) { + return ::djinni::ExceptionHandlingTraits<::djinni::Protobuf>::handleNativeException(e); + } +} EMSCRIPTEN_BINDINGS(testsuite_proto_tests) { ::djinni::DjinniClass_<::testsuite::ProtoTests>("testsuite_ProtoTests", "testsuite.ProtoTests") @@ -149,6 +204,12 @@ EMSCRIPTEN_BINDINGS(testsuite_proto_tests) { .class_function("optionalProtoToString", NativeProtoTests::optionalProtoToString) .class_function("stringToOptionalProto", NativeProtoTests::stringToOptionalProto) .class_function("stringToProtoOutcome", NativeProtoTests::stringToProtoOutcome) + .class_function("phoneTypeToString", NativeProtoTests::phoneTypeToString) + .class_function("stringToPhoneType", NativeProtoTests::stringToPhoneType) + .class_function("enumRecordToString", NativeProtoTests::enumRecordToString) + .class_function("stringToEnumRecord", NativeProtoTests::stringToEnumRecord) + .class_function("priorityToString", NativeProtoTests::priorityToString) + .class_function("stringToPriority", NativeProtoTests::stringToPriority) ; } diff --git a/test-suite/generated-src/wasm/NativeProtoTests.hpp b/test-suite/generated-src/wasm/NativeProtoTests.hpp index 44eb7e14..fd303319 100644 --- a/test-suite/generated-src/wasm/NativeProtoTests.hpp +++ b/test-suite/generated-src/wasm/NativeProtoTests.hpp @@ -36,6 +36,12 @@ struct NativeProtoTests : ::djinni::JsInterface<::testsuite::ProtoTests, NativeP static std::string optionalProtoToString(const em::val& w_x); static em::val stringToOptionalProto(const std::string& w_x); static em::val stringToProtoOutcome(const std::string& w_x); + static std::string phoneTypeToString(const em::val& w_x); + static em::val stringToPhoneType(const std::string& w_x); + static std::string enumRecordToString(const em::val& w_x); + static em::val stringToEnumRecord(const std::string& w_x); + static std::string priorityToString(const em::val& w_x); + static em::val stringToPriority(const std::string& w_x); }; diff --git a/test-suite/generated-src/wasm/NativeRecordWithProtobufEnum.cpp b/test-suite/generated-src/wasm/NativeRecordWithProtobufEnum.cpp new file mode 100644 index 00000000..77f48278 --- /dev/null +++ b/test-suite/generated-src/wasm/NativeRecordWithProtobufEnum.cpp @@ -0,0 +1,19 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from proto.djinni + +#include "NativeRecordWithProtobufEnum.hpp" // my header + +namespace djinni_generated { + +auto NativeRecordWithProtobufEnum::toCpp(const JsType& j) -> CppType { + return {::djinni::Protobuf::Boxed::toCpp(j["phoneType"]), + ::djinni::Protobuf::Boxed::toCpp(j["priority"])}; +} +auto NativeRecordWithProtobufEnum::fromCpp(const CppType& c) -> JsType { + em::val js = em::val::object(); + js.set("phoneType", ::djinni::Protobuf::Boxed::fromCpp(c.phone_type)); + js.set("priority", ::djinni::Protobuf::Boxed::fromCpp(c.priority)); + return js; +} + +} // namespace djinni_generated diff --git a/test-suite/generated-src/wasm/NativeRecordWithProtobufEnum.hpp b/test-suite/generated-src/wasm/NativeRecordWithProtobufEnum.hpp new file mode 100644 index 00000000..3aa68d6e --- /dev/null +++ b/test-suite/generated-src/wasm/NativeRecordWithProtobufEnum.hpp @@ -0,0 +1,21 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from proto.djinni + +#pragma once + +#include "RecordWithProtobufEnum.hpp" +#include "djinni_wasm.hpp" + +namespace djinni_generated { + +struct NativeRecordWithProtobufEnum +{ + using CppType = ::testsuite::RecordWithProtobufEnum; + using JsType = em::val; + using Boxed = NativeRecordWithProtobufEnum; + + static CppType toCpp(const JsType& j); + static JsType fromCpp(const CppType& c); +}; + +} // namespace djinni_generated diff --git a/test-suite/handwritten-src/cpp/proto_test_helpers.cpp b/test-suite/handwritten-src/cpp/proto_test_helpers.cpp index cb1826c7..6e0790b3 100644 --- a/test-suite/handwritten-src/cpp/proto_test_helpers.cpp +++ b/test-suite/handwritten-src/cpp/proto_test_helpers.cpp @@ -1,6 +1,7 @@ #include "proto_tests.hpp" #include "RecordWithEmbeddedProto.hpp" #include "RecordWithEmbeddedCppProto.hpp" +#include "RecordWithProtobufEnum.hpp" namespace testsuite { @@ -92,4 +93,38 @@ djinni::expected<::djinni::test::Person, int32_t> ProtoTests::stringToProtoOutco return proto; } +std::string ProtoTests::phoneTypeToString(const ::djinni::test::Person_PhoneType& x) { + return ::djinni::test::Person_PhoneType_Name(x); +} + +::djinni::test::Person_PhoneType ProtoTests::stringToPhoneType(const std::string& x) { + if (x == "MOBILE") { + return ::djinni::test::Person_PhoneType_MOBILE; + } else if (x == "HOME") { + return ::djinni::test::Person_PhoneType_HOME; + } else if (x == "WORK") { + return ::djinni::test::Person_PhoneType_WORK; + } else { + return ::djinni::test::Person_PhoneType_HOME; // default + } +} + +std::string ProtoTests::enumRecordToString(const RecordWithProtobufEnum& x) { + return phoneTypeToString(x.phone_type); +} + +RecordWithProtobufEnum ProtoTests::stringToEnumRecord(const std::string& x) { + return RecordWithProtobufEnum{stringToPhoneType(x), ::djinni::test::Priority::LOW}; +} + +std::string ProtoTests::priorityToString(const ::djinni::test::Priority& x) { + return ::djinni::test::Priority_Name(x); +} + +::djinni::test::Priority ProtoTests::stringToPriority(const std::string& x) { + ::djinni::test::Priority result; + ::djinni::test::Priority_Parse(x, &result); + return result; +} + } diff --git a/test-suite/handwritten-src/java/com/dropbox/djinni/test/ProtoTest.java b/test-suite/handwritten-src/java/com/dropbox/djinni/test/ProtoTest.java index 07be766e..5947fbb1 100644 --- a/test-suite/handwritten-src/java/com/dropbox/djinni/test/ProtoTest.java +++ b/test-suite/handwritten-src/java/com/dropbox/djinni/test/ProtoTest.java @@ -4,7 +4,11 @@ import junit.framework.TestCase; import djinni.test.Test.AddressBook; import djinni.test.Test.Person; -import com.snapchat.djinni.Outcome; +import djinni.test.Test.Person.PhoneType; +import djinni.test.Test.Priority; +import com.dropbox.djinni.test.ProtoTests; +import com.dropbox.djinni.test.RecordWithEmbeddedProto; +import com.dropbox.djinni.test.RecordWithProtobufEnum; public class ProtoTest extends TestCase { public void testJavaToNative() { @@ -57,8 +61,135 @@ public void testBoxing() { assertEquals(s, "tom"); Person p = ProtoTests.stringToOptionalProto("tom"); assertEquals(p.getName(), "tom"); + } + + public void testProtobufEnumConversion() { + // Test converting each enum value to string and back + String mobileStr = ProtoTests.phoneTypeToString(PhoneType.MOBILE); + assertEquals(mobileStr, "MOBILE"); + PhoneType mobile = ProtoTests.stringToPhoneType("MOBILE"); + assertEquals(mobile, PhoneType.MOBILE); + + String homeStr = ProtoTests.phoneTypeToString(PhoneType.HOME); + assertEquals(homeStr, "HOME"); + PhoneType home = ProtoTests.stringToPhoneType("HOME"); + assertEquals(home, PhoneType.HOME); + + String workStr = ProtoTests.phoneTypeToString(PhoneType.WORK); + assertEquals(workStr, "WORK"); + PhoneType work = ProtoTests.stringToPhoneType("WORK"); + assertEquals(work, PhoneType.WORK); + } + + public void testProtobufEnumRoundTrip() { + // Test all enum values survive round-trip conversion + PhoneType[] phoneTypes = {PhoneType.MOBILE, PhoneType.HOME, PhoneType.WORK}; + + for (PhoneType original : phoneTypes) { + String str = ProtoTests.phoneTypeToString(original); + PhoneType converted = ProtoTests.stringToPhoneType(str); + assertEquals(original, converted); + } + } + + public void testProtobufEnumInRecord() { + // Test enum embedded in a record + RecordWithProtobufEnum mobileRecord = new RecordWithProtobufEnum(PhoneType.MOBILE, Priority.LOW); + String mobileStr = ProtoTests.enumRecordToString(mobileRecord); + assertEquals(mobileStr, "MOBILE"); + + RecordWithProtobufEnum convertedRecord = ProtoTests.stringToEnumRecord("MOBILE"); + assertEquals(convertedRecord.getPhoneType(), PhoneType.MOBILE); + + // Test different enum values + RecordWithProtobufEnum homeRecord = new RecordWithProtobufEnum(PhoneType.HOME, Priority.LOW); + String homeStr = ProtoTests.enumRecordToString(homeRecord); + assertEquals(homeStr, "HOME"); + + RecordWithProtobufEnum convertedHomeRecord = ProtoTests.stringToEnumRecord("HOME"); + assertEquals(convertedHomeRecord.getPhoneType(), PhoneType.HOME); + + RecordWithProtobufEnum workRecord = new RecordWithProtobufEnum(PhoneType.WORK, Priority.LOW); + String workStr = ProtoTests.enumRecordToString(workRecord); + assertEquals(workStr, "WORK"); + + RecordWithProtobufEnum convertedWorkRecord = ProtoTests.stringToEnumRecord("WORK"); + assertEquals(convertedWorkRecord.getPhoneType(), PhoneType.WORK); + } + + public void testProtobufEnumRecordRoundTrip() { + // Test record with enum survives round-trip conversion + PhoneType[] phoneTypes = {PhoneType.MOBILE, PhoneType.HOME, PhoneType.WORK}; + + for (PhoneType phoneType : phoneTypes) { + RecordWithProtobufEnum original = new RecordWithProtobufEnum(phoneType, Priority.LOW); + String str = ProtoTests.enumRecordToString(original); + RecordWithProtobufEnum converted = ProtoTests.stringToEnumRecord(str); + assertEquals(original.getPhoneType(), converted.getPhoneType()); + } + } + + public void testTopLevelProtobufEnumConversion() { + // Test converting each top-level enum value to string and back + String lowStr = ProtoTests.priorityToString(Priority.LOW); + assertEquals(lowStr, "LOW"); + Priority low = ProtoTests.stringToPriority("LOW"); + assertEquals(low, Priority.LOW); + + String mediumStr = ProtoTests.priorityToString(Priority.MEDIUM); + assertEquals(mediumStr, "MEDIUM"); + Priority medium = ProtoTests.stringToPriority("MEDIUM"); + assertEquals(medium, Priority.MEDIUM); + + String highStr = ProtoTests.priorityToString(Priority.HIGH); + assertEquals(highStr, "HIGH"); + Priority high = ProtoTests.stringToPriority("HIGH"); + assertEquals(high, Priority.HIGH); + + String urgentStr = ProtoTests.priorityToString(Priority.URGENT); + assertEquals(urgentStr, "URGENT"); + Priority urgent = ProtoTests.stringToPriority("URGENT"); + assertEquals(urgent, Priority.URGENT); + } + + public void testTopLevelProtobufEnumRoundTrip() { + // Test enum round-trip conversion + Priority[] priorities = {Priority.LOW, Priority.MEDIUM, Priority.HIGH, Priority.URGENT}; + + for (Priority priority : priorities) { + String str = ProtoTests.priorityToString(priority); + Priority converted = ProtoTests.stringToPriority(str); + assertEquals(priority, converted); + } + } + + public void testTopLevelProtobufEnumInRecord() { + // Test record containing top-level enum - test priority field access + RecordWithProtobufEnum lowRecord = new RecordWithProtobufEnum(PhoneType.MOBILE, Priority.LOW); + assertEquals(lowRecord.getPriority(), Priority.LOW); + + RecordWithProtobufEnum urgentRecord = new RecordWithProtobufEnum(PhoneType.HOME, Priority.URGENT); + assertEquals(urgentRecord.getPriority(), Priority.URGENT); + + // Test that both enum fields work in the same record + RecordWithProtobufEnum mixedRecord = new RecordWithProtobufEnum(PhoneType.WORK, Priority.HIGH); + assertEquals(mixedRecord.getPhoneType(), PhoneType.WORK); + assertEquals(mixedRecord.getPriority(), Priority.HIGH); + } - Outcome r = ProtoTests.stringToProtoOutcome("tom"); - assertEquals(r.resultOr(null).getName(), "tom"); + public void testTopLevelProtobufEnumRecordRoundTrip() { + // Test both enum fields in record round-trip + Priority[] priorities = {Priority.LOW, Priority.MEDIUM, Priority.HIGH, Priority.URGENT}; + PhoneType[] phoneTypes = {PhoneType.MOBILE, PhoneType.HOME, PhoneType.WORK}; + + for (int i = 0; i < priorities.length; i++) { + PhoneType phoneType = phoneTypes[i % phoneTypes.length]; + Priority priority = priorities[i]; + + RecordWithProtobufEnum original = new RecordWithProtobufEnum(phoneType, priority); + // Test that priority field is preserved + assertEquals(original.getPriority(), priority); + assertEquals(original.getPhoneType(), phoneType); + } } } diff --git a/test-suite/handwritten-src/objc/tests/DBProtoTest.mm b/test-suite/handwritten-src/objc/tests/DBProtoTest.mm index df57c1c2..ef93b428 100644 --- a/test-suite/handwritten-src/objc/tests/DBProtoTest.mm +++ b/test-suite/handwritten-src/objc/tests/DBProtoTest.mm @@ -95,4 +95,39 @@ - (void) testBoxing { XCTAssertEqualObjects(r, [DJOutcome fromResult:tom]); } +- (void) testProtobufEnum { + // Test enum conversion + DJTestPerson_PhoneType homeEnum = [DBProtoTests stringToPhoneType:@"HOME"]; + XCTAssertEqual(homeEnum, DJTestPerson_PhoneType_Home); + + DJTestPerson_PhoneType workEnum = [DBProtoTests stringToPhoneType:@"WORK"]; + XCTAssertEqual(workEnum, DJTestPerson_PhoneType_Work); + + DJTestPerson_PhoneType mobileEnum = [DBProtoTests stringToPhoneType:@"MOBILE"]; + XCTAssertEqual(mobileEnum, DJTestPerson_PhoneType_Mobile); + + // Test unknown string defaults to home + DJTestPerson_PhoneType unknownEnum = [DBProtoTests stringToPhoneType:@"unknown"]; + XCTAssertEqual(unknownEnum, DJTestPerson_PhoneType_Home); +} + +- (void) testRecordWithProtobufEnum { + // Test record with protobuf enum + DBRecordWithProtobufEnum* workRecord = [DBRecordWithProtobufEnum RecordWithProtobufEnumWithPhoneType:DJTestPerson_PhoneType_Work priority:DJTestPriority_High]; + NSString* workString = [DBProtoTests enumRecordToString:workRecord]; + XCTAssertEqualObjects(workString, @"WORK"); + + DBRecordWithProtobufEnum* mobileRecord = [DBProtoTests stringToEnumRecord:@"MOBILE"]; + XCTAssertEqual(mobileRecord.phoneType, DJTestPerson_PhoneType_Mobile); + + NSString* mobileString = [DBProtoTests enumRecordToString:mobileRecord]; + XCTAssertEqualObjects(mobileString, @"MOBILE"); + + // Test round-trip conversion + DBRecordWithProtobufEnum* homeRecord = [DBRecordWithProtobufEnum RecordWithProtobufEnumWithPhoneType:DJTestPerson_PhoneType_Home priority:DJTestPriority_Low]; + NSString* homeString = [DBProtoTests enumRecordToString:homeRecord]; + DBRecordWithProtobufEnum* reconvertedRecord = [DBProtoTests stringToEnumRecord:homeString]; + XCTAssertEqual(reconvertedRecord.phoneType, DJTestPerson_PhoneType_Home); +} + @end diff --git a/test-suite/handwritten-src/ts/ProtoTest.ts b/test-suite/handwritten-src/ts/ProtoTest.ts index c483c3ab..254f9034 100644 --- a/test-suite/handwritten-src/ts/ProtoTest.ts +++ b/test-suite/handwritten-src/ts/ProtoTest.ts @@ -61,6 +61,79 @@ export class ProtoTest extends TestCase { const r = this.m.testsuite.ProtoTests.stringToProtoOutcome('tom'); assertEq(r, {result: {name: 'tom', id: 1, email:'', phones:[]}}); } + + testProtobufEnum() { + // Test enum conversion - should match Java/ObjC behavior + const homeEnum = this.m.testsuite.ProtoTests.stringToPhoneType("HOME"); + assertEq(homeEnum, prototest.Person_PhoneType.HOME); + + const workEnum = this.m.testsuite.ProtoTests.stringToPhoneType("WORK"); + assertEq(workEnum, prototest.Person_PhoneType.WORK); + + const mobileEnum = this.m.testsuite.ProtoTests.stringToPhoneType("MOBILE"); + assertEq(mobileEnum, prototest.Person_PhoneType.MOBILE); + + // Test unknown string defaults to home (same as other platforms) + const unknownEnum = this.m.testsuite.ProtoTests.stringToPhoneType("unknown"); + assertEq(unknownEnum, prototest.Person_PhoneType.HOME); + + // Test enum to string conversion + const homeString = this.m.testsuite.ProtoTests.phoneTypeToString(prototest.Person_PhoneType.HOME); + assertEq(homeString, "HOME"); + + const workString = this.m.testsuite.ProtoTests.phoneTypeToString(prototest.Person_PhoneType.WORK); + assertEq(workString, "WORK"); + + const mobileString = this.m.testsuite.ProtoTests.phoneTypeToString(prototest.Person_PhoneType.MOBILE); + assertEq(mobileString, "MOBILE"); + } + + testRecordWithProtobufEnum() { + // Test record with protobuf enum + const workRecord: test.RecordWithProtobufEnum = { + phoneType: prototest.Person_PhoneType.WORK, + priority: prototest.Priority.HIGH + }; + const workString = this.m.testsuite.ProtoTests.enumRecordToString(workRecord); + assertEq(workString, "WORK"); + + const mobileRecord = this.m.testsuite.ProtoTests.stringToEnumRecord("MOBILE"); + assertEq(mobileRecord.phoneType, prototest.Person_PhoneType.MOBILE); + + const mobileString = this.m.testsuite.ProtoTests.enumRecordToString(mobileRecord); + assertEq(mobileString, "MOBILE"); + + // Test round-trip conversion + const homeRecord: test.RecordWithProtobufEnum = { + phoneType: prototest.Person_PhoneType.HOME, + priority: prototest.Priority.LOW + }; + const homeString = this.m.testsuite.ProtoTests.enumRecordToString(homeRecord); + const reconvertedRecord = this.m.testsuite.ProtoTests.stringToEnumRecord(homeString); + assertEq(reconvertedRecord.phoneType, prototest.Person_PhoneType.HOME); + } + + testPriorityEnum() { + // Test Priority enum conversion + const lowPriority = this.m.testsuite.ProtoTests.stringToPriority("LOW"); + assertEq(lowPriority, prototest.Priority.LOW); + + const highPriority = this.m.testsuite.ProtoTests.stringToPriority("HIGH"); + assertEq(highPriority, prototest.Priority.HIGH); + + const urgentPriority = this.m.testsuite.ProtoTests.stringToPriority("URGENT"); + assertEq(urgentPriority, prototest.Priority.URGENT); + + // Test enum to string conversion + const lowString = this.m.testsuite.ProtoTests.priorityToString(prototest.Priority.LOW); + assertEq(lowString, "LOW"); + + const highString = this.m.testsuite.ProtoTests.priorityToString(prototest.Priority.HIGH); + assertEq(highString, "HIGH"); + + const urgentString = this.m.testsuite.ProtoTests.priorityToString(prototest.Priority.URGENT); + assertEq(urgentString, "URGENT"); + } } allTests.push(ProtoTest); diff --git a/third_party/zlib/BUILD.bazel b/third_party/zlib/BUILD.bazel new file mode 100644 index 00000000..c2245071 --- /dev/null +++ b/third_party/zlib/BUILD.bazel @@ -0,0 +1,37 @@ +cc_library( + name = "zlib", + srcs = [ + "adler32.c", + "compress.c", + "crc32.c", + "deflate.c", + "inflate.c", + "infback.c", + "inftrees.c", + "inffast.c", + "trees.c", + "uncompr.c", + # zutil.c excluded due to gzguts.h dependency + ], + hdrs = [ + "zlib.h", + "zconf.h", + "crc32.h", + "deflate.h", + "inffast.h", + "inffixed.h", + "inflate.h", + "inftrees.h", + "trees.h", + "zutil.h", + ], + copts = [ + "-Wno-unused-variable", + "-Wno-implicit-function-declaration", + "-Wno-macro-redefined", + "-Wno-deprecated-non-prototype", + "-DNO_GZCOMPRESS", + "-DNO_GZIP", + ], + visibility = ["//visibility:public"], +)