diff --git a/dprotoc/src/app.d b/dprotoc/src/app.d index 2975fdf..5dc4a08 100644 --- a/dprotoc/src/app.d +++ b/dprotoc/src/app.d @@ -5,6 +5,7 @@ import std.stdio; import std.string; import dproto.parse; import dproto.intermediate; +import std.format; auto openFileComplex(string fn, string mode) { @@ -22,11 +23,13 @@ void main(string[] args) { string infile = "-"; string outfile = "-"; - string fmt = "%d"; + char syntx = 'd'; + int servicetype = 1; auto helpInformation = getopt( args, "out|o", "Output filename (default stdout)", &outfile, - "format|f", "Code generation format", &fmt, + "syntx|s", "code generation syntx. it should be : \n\t\t\td : dlang file; \n\t\t\ts : dlang code string;\n\t\t\tp : protobuf file.",&syntx, + "type|t", "what dlang file or code string will create service to. it should be :\n\t\t\t1 : interface N { R metond(P);}\n\t\t\t2 : interface N { void metond(const P, ref R);}\n\t\t\t3 : class N { R metond(P){R res;return res;}}",&servicetype ); if (helpInformation.helpWanted) { @@ -45,5 +48,23 @@ void main(string[] args) } pack = ParseProtoSchema(infile, inf.byLine.join("\n").idup); } - openFileComplex(outfile, "w").write(fmt.format(pack)); + auto file = openFileComplex(outfile, "w"); + + ProtoConfig fmt; + fmt.rpc = servicetype; + final switch(syntx){ + case 'p': + fmt.type = ProtoConfig.Type.Proto; + break; + case 's' : + fmt.type = ProtoConfig.Type.Dcode; + break; + case 'd' : + fmt.type = ProtoConfig.Type.Dfile; + break; + } + + Appender!string buffer; + pack.toString((const(char)[] data){buffer.put(data);},fmt); + file.write(buffer.data); } diff --git a/examples/proto/person.d b/examples/proto/person.d new file mode 100644 index 0000000..3537168 --- /dev/null +++ b/examples/proto/person.d @@ -0,0 +1,70 @@ +package persion; + +import std.range; +import dproto.serialize; + +static struct Person { + static import dproto.attributes; + mixin dproto.attributes.ProtoAccessors; + +enum PhoneType { + WORK = 2, + HOME = 0, + MOBILE = 0, +} + +static struct PhoneNumber { + static import dproto.attributes; + mixin dproto.attributes.ProtoAccessors; + + @(dproto.attributes.Required()) + @(dproto.attributes.ProtoField("string", 1)) + BuffType!"string" number= UnspecifiedDefaultValue!(BuffType!"string"); + + + @(dproto.attributes.ProtoField("PhoneType", 2)) + dproto.serialize.PossiblyNullable!(PhoneType) type= SpecifiedDefaultValue!(PhoneType, "MOBILE"); + +} + + @(dproto.attributes.Required()) + @(dproto.attributes.ProtoField("string", 1)) + BuffType!"string" name= UnspecifiedDefaultValue!(BuffType!"string"); + + + @(dproto.attributes.Required()) + @(dproto.attributes.ProtoField("int32", 2)) + BuffType!"int32" id= UnspecifiedDefaultValue!(BuffType!"int32"); + + + @(dproto.attributes.ProtoField("string", 3)) + BuffType!"string" email= UnspecifiedDefaultValue!(BuffType!"string"); + + + @(dproto.attributes.ProtoField("PhoneNumber", 4)) + PhoneNumber[] phone; + +} + +static struct ServiceRequest { + static import dproto.attributes; + mixin dproto.attributes.ProtoAccessors; + + @(dproto.attributes.ProtoField("string", 1)) + BuffType!"string" request= UnspecifiedDefaultValue!(BuffType!"string"); + +} + +static struct ServiceResponse { + static import dproto.attributes; + mixin dproto.attributes.ProtoAccessors; + + @(dproto.attributes.ProtoField("string", 1)) + BuffType!"string" response= UnspecifiedDefaultValue!(BuffType!"string"); + +} + +interface TestService { + ServiceResponse TestMethod (ServiceRequest); +} + diff --git a/examples/proto/person.proto b/examples/proto/person.proto index 0145f0a..132375c 100644 --- a/examples/proto/person.proto +++ b/examples/proto/person.proto @@ -1,5 +1,7 @@ +package persion; + message Person { - required string name = 1; + required string name = 1; // namnmknhi required int32 id = 2; optional string email = 3; @@ -16,3 +18,15 @@ message Person { repeated PhoneNumber phone = 4; } + + +message ServiceRequest { + string request = 1; +} +message ServiceResponse { + string response = 1; +} + +service TestService { + rpc TestMethod (ServiceRequest) returns (ServiceResponse); +} diff --git a/import/dproto/intermediate.d b/import/dproto/intermediate.d index 0445826..3255dc5 100644 --- a/import/dproto/intermediate.d +++ b/import/dproto/intermediate.d @@ -17,6 +17,76 @@ import std.conv; import std.string; import std.format; +struct ProtoConfig{ + enum Type{ + Proto, + Dcode, + Dfile + } + Type type = Type.Dcode; + int rpc = 1; +} + +struct ProtoPackage { + this(string fileName) { + this.fileName = fileName.idup; + } + string fileName; + string packageName; + Dependency[] dependencies; + EnumType[] enumTypes; + MessageType[] messageTypes; + Options options; + Service[] rpcServices; + string syntax = "proto2"; + + const void toString(scope void delegate(const(char)[]) sink,ProtoConfig fmt = ProtoConfig()) + { + if(fmt.type == ProtoConfig.Type.Proto) { + if(packageName) { + sink.formattedWrite("package %s; \n\n", packageName); + } + if(syntax != "proto2") { + sink.formattedWrite(`syntax = %s; \n`, syntax); + } + }else if(fmt.type == ProtoConfig.Type.Dfile){ + if(packageName) { + sink.formattedWrite("module %s; \n\n", packageName); + } + sink("import std.range;\nimport dproto.serialize;\n"); + } + foreach(dep; dependencies) { + dep.toString(sink, fmt); + sink("\n"); + } + sink("\n"); + foreach(e; enumTypes){ e.toString(sink, fmt);sink("\n");} + foreach(m; messageTypes){ m.toString(sink, fmt); sink("\n");} + foreach(r; rpcServices){r.toString(sink, fmt);sink("\n");} + if(fmt.type == ProtoConfig.Type.Proto) { + foreach(opt, val; options) { + sink.formattedWrite("option %s = %s; \n", opt, val); + } + } + } + + string toProto() @property const { + import std.array; + Appender!string data = appender!string(); + ProtoConfig fmt; + fmt.type = ProtoConfig.Type.Proto; + toString((const(char)[] str){data.put(str);},fmt); + return data.data; + } + string toD() @property const { + import std.array; + Appender!string data = appender!string(); + toString((const(char)[] str){data.put(str);}); + return data.data; + } +} + + package: struct Options { @@ -43,27 +113,28 @@ struct MessageType { EnumType[] enumTypes; MessageType[] messageTypes; - const void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) + const void toString(scope void delegate(const(char)[]) sink, ref ProtoConfig fmt) { - if(fmt.spec == 'p') { + if(fmt.type == ProtoConfig.Type.Proto) { sink.formattedWrite("message %s { ", name); foreach(opt, val; options) { - sink.formattedWrite("option %s = %s; ", opt, val); + sink.formattedWrite("\toption %s = %s; ", opt, val); } } else { sink.formattedWrite("static struct %s {\n", name); // Methods for serialization and deserialization. - sink("static import dproto.attributes;\n"); + sink("\tstatic import dproto.attributes;\n\t"); sink(`mixin dproto.attributes.ProtoAccessors;`); + sink("\n"); } - foreach(et; enumTypes) et.toString(sink, fmt); - foreach(mt; messageTypes) mt.toString(sink, fmt); - foreach(field; fields) field.toString(sink, fmt); + foreach(et; enumTypes){sink("\n");et.toString(sink, fmt);} + foreach(mt; messageTypes){sink("\n"); mt.toString(sink, fmt);} + foreach(field; fields){sink("\n"); field.toString(sink, fmt);} sink("}\n"); } - string toD() @property const { return "%s".format(this); } + string toD() @property const {return "%s".format(this); } } struct EnumType { @@ -74,18 +145,18 @@ struct EnumType { Options options; int[string] values; - const void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) + const void toString(scope void delegate(const(char)[]) sink, ref ProtoConfig fmt) { sink.formattedWrite("enum %s {\n", name); string suffix = ", "; - if(fmt.spec == 'p') { + if(fmt.type == ProtoConfig.Type.Proto) { foreach(opt, val; options) { - sink.formattedWrite("option %s = %s; ", opt, val); + sink.formattedWrite("\toption %s = %s; \n", opt, val); } suffix = "; "; } foreach(key, val; values) { - sink.formattedWrite("%s = %s%s", key, val, suffix); + sink.formattedWrite("\t%s = %s%s \n", key, val, suffix); } sink("}\n"); } @@ -115,14 +186,20 @@ struct Dependency { string name; bool isPublic; - const void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) + const void toString(scope void delegate(const(char)[]) sink,ref ProtoConfig fmt) { - if(fmt.spec == 'p') { + if(fmt.type == ProtoConfig.Type.Proto) { sink("import "); if(isPublic) { sink("public "); } sink.formattedWrite(`"%s"; `, name); + } else if(fmt.type == ProtoConfig.Type.Dfile){ + sink("\n//NOTE: please change your import module.\n"); + if(isPublic) { + sink("public "); + } + sink.formattedWrite(`import "%s"; `, name); } else { sink.formattedWrite(`mixin ProtocolBuffer!"%s";`, name); } @@ -131,46 +208,6 @@ struct Dependency { string toD() @property const { return "%s".format(this); } } -struct ProtoPackage { - this(string fileName) { - this.fileName = fileName.idup; - } - string fileName; - string packageName; - Dependency[] dependencies; - EnumType[] enumTypes; - MessageType[] messageTypes; - Options options; - Service[] rpcServices; - string syntax = "proto2"; - - const void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) - { - if(fmt.spec == 'p') { - if(packageName) { - sink.formattedWrite("package %s; ", packageName); - } - if(syntax != "proto2") { - sink.formattedWrite(`syntax = %s; `, syntax); - } - } - foreach(dep; dependencies) { - dep.toString(sink, fmt); - } - foreach(e; enumTypes) e.toString(sink, fmt); - foreach(m; messageTypes) m.toString(sink, fmt); - foreach(r; rpcServices) r.toString(sink, fmt); - if(fmt.spec == 'p') { - foreach(opt, val; options) { - sink.formattedWrite("option %s = %s; ", opt, val); - } - } - } - - string toProto() @property const { return "%p".format(this); } - string toD() @property const { return "%s".format(this); } -} - struct Field { enum Requirement { OPTIONAL, @@ -197,40 +234,26 @@ struct Field { return options["default"]; } - const void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) + const void toString(scope void delegate(const(char)[]) sink, ref ProtoConfig fmt) { - switch(fmt.spec) { - case 'p': - if(fmt.width == 3 && requirement != Requirement.REPEATED) { - sink.formattedWrite("%s %s = %s%p; ", - type, name, id, options); - } - else { - sink.formattedWrite("%s %s %s = %s%p; ", - requirement.to!string.toLower(), - type, name, id, options); - } - break; - default: - if(!fmt.flDash) { - getDeclaration(sink); - } else { - sink.formattedWrite("%s %s;\n", - type, name); - } - break; + if(fmt.type == ProtoConfig.Type.Proto){ + sink.formattedWrite("\t%s %s %s = %s%p; \n", + requirement.to!string.toLower(), + type, name, id, options); + } else { + getDeclaration(sink); } } void getDeclaration(scope void delegate(const(char)[]) sink) const { if(requirement == Requirement.REQUIRED) { - sink("@(dproto.attributes.Required())\n"); + sink("\t@(dproto.attributes.Required())\n"); } else if(requirement == Requirement.REPEATED) { if(options.get("packed", "false") != "false") { - sink("@(dproto.attributes.Packed())\n"); + sink("\t@(dproto.attributes.Packed())\n"); } } - sink("@(dproto.attributes.ProtoField"); + sink("\t@(dproto.attributes.ProtoField"); sink.formattedWrite(`("%s", %s)`, type, id); sink(")\n"); @@ -239,13 +262,15 @@ struct Field { ! type.isBuiltinType(); if(wrap_with_nullable) { + sink("\t"); sink(`dproto.serialize.PossiblyNullable!(`); } string typestr = type; if(type.isBuiltinType) { typestr = format(`BuffType!"%s"`, type); } - + if(!wrap_with_nullable) + sink("\t"); sink(typestr); if(wrap_with_nullable) { @@ -296,49 +321,43 @@ struct Service { string responseType; Options options; - const void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) + const void toString(scope void delegate(const(char)[]) sink, ref ProtoConfig fmt) { - switch(fmt.spec) { - case 'p': - sink.formattedWrite("rpc %s (%s) returns (%s)", name, requestType, responseType); - if(options.length > 0) { - sink(" {\n"); - foreach(opt, val; options) { - sink.formattedWrite("option %s = %s;\n", opt, val); - } - sink("}\n"); - } else { - sink(";\n"); - } - break; - default: - if(fmt.precision == 3) { - sink.formattedWrite("%s %s (%s) { %s res; return res; }\n", responseType, name, requestType, responseType); - } else if(fmt.precision == 2) { - sink.formattedWrite("void %s (const %s, ref %s);\n", name, requestType, responseType); - } else if(fmt.precision == 1) { - sink.formattedWrite("%s %s (%s);\n", responseType, name, requestType); + if(fmt.type == ProtoConfig.Type.Proto){ + sink.formattedWrite("\trpc %s (%s) returns (%s)", name, requestType, responseType); + if(options.length > 0) { + sink(" {\n"); + foreach(opt, val; options) { + sink.formattedWrite("\toption %s = %s;\n", opt, val); } - break; + sink("}\n"); + } else { + sink(";\n"); + } + } else { + if(fmt.rpc == 3) { + sink.formattedWrite("\t%s %s (%s) { %s res; return res; }\n", responseType, name, requestType, responseType); + } else if(fmt.rpc == 2) { + sink.formattedWrite("\tvoid %s (const %s, ref %s);\n", name, requestType, responseType); + } else if(fmt.rpc == 1) { + sink.formattedWrite("\t%s %s (%s);\n", responseType, name, requestType); + } } } } - const void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) + const void toString(scope void delegate(const(char)[]) sink, ref ProtoConfig fmt) { - switch(fmt.spec) { - case 'p': + if(fmt.type == ProtoConfig.Type.Proto){ sink.formattedWrite("service %s {\n", name); - break; - default: - if(fmt.precision == 3) { + } else { + if(fmt.rpc == 3) { sink.formattedWrite("class %s {\n", name); - } else if(fmt.precision == 2 || fmt.precision == 1) { - sink.formattedWrite("interface %s {\n", name); - } else { - return; - } - break; + } else if(fmt.rpc == 2 || fmt.rpc == 1) { + sink.formattedWrite("interface %s {\n", name); + } else { + return; + } } foreach(m; rpc) m.toString(sink, fmt); sink("}\n"); diff --git a/import/dproto/unittests.d b/import/dproto/unittests.d index dc4902c..3a94bc7 100644 --- a/import/dproto/unittests.d +++ b/import/dproto/unittests.d @@ -11,6 +11,8 @@ module dproto.unittests; import dproto.dproto; +import std.stdio; + unittest { assert(__traits(compiles, ProtocolBufferFromString!"message Test @@ -192,11 +194,7 @@ unittest // Force code coverage in doveralls import std.string; - import std.format; import dproto.parse; - - auto normalizedServiceDefinition = "%3.3p".format(ParseProtoSchema("", serviceDefinition)); - assert(__traits(compiles, ProtocolBufferFromString!serviceDefinition)); assert(__traits(compiles, ProtocolBufferInterface!serviceDefinition)); assert(__traits(compiles, ProtocolBufferRpc!serviceDefinition)); @@ -578,7 +576,7 @@ unittest auto proto_src = `import "foo/baz.proto";`; auto proto_struct = ParseProtoSchema("", proto_src); auto d_src = proto_struct.toD; - assert(`mixin ProtocolBuffer!"foo/baz.proto";` == d_src, + assert(`mixin ProtocolBuffer!"foo/baz.proto";` == d_src.strip, "Mixin string should not have two double quotes " ~ d_src); assert(proto_src == proto_struct.toProto.strip, "Round tripping to protobuf source should yield starting text " ~ proto_struct.toProto); @@ -723,11 +721,8 @@ unittest // Force code coverage in doveralls import std.string; - import std.format; import dproto.parse; - auto normalizedServiceDefinition = "%3.3p".format(ParseProtoSchema("", pbstring)); - mixin ProtocolBufferFromString!pbstring; Msg msg;