diff --git a/src/parsing/Parsers.jl b/src/parsing/Parsers.jl index 723dfac..73fcce8 100644 --- a/src/parsing/Parsers.jl +++ b/src/parsing/Parsers.jl @@ -205,6 +205,7 @@ function parse_proto_file(ps::ParserState) definitions[type.name] = type end end + package_identifier = get(options, "julia_package", package_identifier) package_parts = split(package_identifier, '.', keepempty=false) preamble = ProtoFilePreamble( ps.is_proto3, @@ -225,4 +226,4 @@ function parse_proto_file(ps::ParserState) ) end -end # module \ No newline at end of file +end # module diff --git a/src/parsing/proto_options.jl b/src/parsing/proto_options.jl index fab7440..f5efebd 100644 --- a/src/parsing/proto_options.jl +++ b/src/parsing/proto_options.jl @@ -26,6 +26,17 @@ function _parse_option_value(ps) # TODO: proper value parsing with validation return has_minus ? string("-", str_val) : str_val end +function _parse_julia_package(ps) + val = _parse_option_value(ps) + if startswith(val, "\"") && endswith(val, "\"") || startswith(val, "'") && endswith(val, "'") + val = val[begin+1:end-1] + if all(Lexers.is_fully_qualified_ident_char, val) + return val + end + end + error("Invalid value for julia_package option: $(val)") +end + function _parse_option_name(ps) buf = IOBuffer() option_name = "" @@ -91,11 +102,15 @@ function _parse_option!(ps::ParserState, options::Dict{String,Union{String,Dict{ option_name = _parse_option_name(ps) accept(ps, Tokens.COLON) expectnext(ps, Tokens.EQ) # = - if accept(ps, Tokens.LBRACE) # {key: val, ...} - options[option_name] = _parse_aggregate_option(ps) - # accept(ps, Tokens.SEMICOLON) + if option_name == "julia_package" + options[option_name] = _parse_julia_package(ps) else - options[option_name] = _parse_option_value(ps) + if accept(ps, Tokens.LBRACE) # {key: val, ...} + options[option_name] = _parse_aggregate_option(ps) + # accept(ps, Tokens.SEMICOLON) + else + options[option_name] = _parse_option_value(ps) + end end return nothing end diff --git a/test/test_codegen.jl b/test/test_codegen.jl index 5f9f677..d469592 100644 --- a/test/test_codegen.jl +++ b/test/test_codegen.jl @@ -126,6 +126,16 @@ end end # module""" end + @testset "Minimal proto file with julia_package option" begin + s, p, ctx = translate_simple_proto("""syntax = "proto3"; option julia_package = "Foo.Bar.Baz";""", Options(always_use_modules=false)) + @test "Foo.Bar.Baz" == p.preamble.options["julia_package"] + @test ["Foo", "Bar", "Baz"] == namespace(p) + + @test_throws ErrorException begin + translate_simple_proto("""syntax = "proto3"; option julia_package = "Foo/Bar/Baz";""", Options(always_use_modules=false)) + end + end + @testset "`force_required` option makes optional fields required" begin s, p, ctx = translate_simple_proto("message A {} message B { optional A a = 1; }", Options(force_required=Dict("main" => Set(["B.a"])))) ctx._toplevel_name[] = "B" diff --git a/test/test_protojl.jl b/test/test_protojl.jl index 0eaa284..26c8919 100644 --- a/test/test_protojl.jl +++ b/test/test_protojl.jl @@ -316,3 +316,28 @@ using ProtoBuf @test pairs_message.map_field == decoded_pairs.map_field end end + +module TestJuliaPackage +using Test +using ProtoBuf + +@testset "Use julia_package option" begin + mktempdir() do tmpdir + @testset "translate source" begin + @test isnothing(protojl( + "test_julia_package.proto", + joinpath(@__DIR__, "test_protos/"), + tmpdir; + always_use_modules=false, + parametrize_oneofs=false + )) + end + @testset "include generated" begin + @test include(joinpath(tmpdir, "Foo/Foo.jl")) isa Module + @test Foo.Bar.Baz.Message isa Type + @test :message == fieldnames(Foo.Bar.Baz.Message)[1] + @test String == fieldtypes(Foo.Bar.Baz.Message)[1] + end + end +end +end diff --git a/test/test_protos/test_julia_package.proto b/test/test_protos/test_julia_package.proto new file mode 100644 index 0000000..a0ae17f --- /dev/null +++ b/test/test_protos/test_julia_package.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +option julia_package = "Foo.Bar.Baz"; + +message Message { + string message = 1; +};