Skip to content

Commit f33d834

Browse files
authored
Merge pull request #217 from JuliaIO/td-common-abstract-type
Introduce `common_abstract_type` option
2 parents b81daed + cd09c2c commit f33d834

File tree

6 files changed

+45
-6
lines changed

6 files changed

+45
-6
lines changed

docs/src/index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ For translating proto files, use `protojl` function (previously `protoc`). To de
126126
### Mutability
127127
Messages are now translated to immutable structs. This means that code that used the mutable structs to accumulate data will now have to prepare each field and construct the struct after all of them are ready.
128128

129+
### Common abstract type
130+
131+
By default, the generated structs don't share any common abstract type (well, except `Any`), when you set the `common_abstract_type` option to `true`, every struct definition will be a subtype of `ProtoBuf.AbstractProtoBufMessage`.
132+
129133
### Naming Conventions
130134
The naming of nested messages and enums now generates names, that cannot collide with other definitions. For example:
131135
```protobuf

src/ProtoBuf.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,10 @@ function encode(e::AbstractProtoEncoder, ::T) where {T}
125125
throw(MethodError(encode, (typeof(e), T)))
126126
end
127127

128+
abstract type AbstractProtoBufMessage end
129+
128130
export protojl, encode, ProtoEncoder, decode, decode!, ProtoDecoder
129-
export OneOf
131+
export OneOf, AbstractProtoBufMessage
130132
export reserved_fields, extendable_field_numbers, oneof_field_types, field_numbers, default_values
131133

132134
if Base.VERSION >= v"1.4.2"

src/codegen/CodeGenerators.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Base.@kwdef struct Options
3131
force_required::Union{Nothing,Dict{String,Set{String}}} = nothing
3232
add_kwarg_constructors::Bool = false
3333
parametrize_oneofs::Bool = false
34+
common_abstract_type::Bool = false
3435
end
3536

3637
struct Context
@@ -63,6 +64,7 @@ include("utils.jl")
6364
force_required::Union{Nothing,Dict{String,Set{String}}}=nothing,
6465
add_kwarg_constructors::Bool=false,
6566
parametrize_oneofs::Bool=false,
67+
common_abstract_type::Bool=false,
6668
) -> Nothing
6769
6870
Generate Julia code for `.proto` files at `relative_paths` within `search_directories` and save it to `output_directory`.
@@ -115,6 +117,7 @@ to
115117
struct MyMessage{T1<:Union{Nothing, OneOf{<:Union{Int, String}}}}
116118
oneof_field::T1
117119
end
120+
- `common_abstract_type::Bool=false`: When `true`, all generated structs will subtype `ProtoBuf.AbstractProtoBufMessage`.
118121
```
119122
120123
# Notes
@@ -129,8 +132,9 @@ function protojl(
129132
force_required::Union{Nothing,<:Dict{<:AbstractString,<:Set{<:AbstractString}}}=nothing,
130133
add_kwarg_constructors::Bool=false,
131134
parametrize_oneofs::Bool=false,
135+
common_abstract_type::Bool = false,
132136
)
133-
options = Options(include_vendored_wellknown_types, always_use_modules, force_required, add_kwarg_constructors, parametrize_oneofs)
137+
options = Options(include_vendored_wellknown_types, always_use_modules, force_required, add_kwarg_constructors, parametrize_oneofs, common_abstract_type)
134138
return _protojl(relative_paths, search_directories, output_directory, options)
135139
end
136140

src/codegen/toplevel_definitions.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ function generate_struct(io, t::MessageType, ctx::Context)
8686
type_params = get_type_params(t, ctx)
8787
params_string = get_type_param_string(type_params)
8888

89-
print(io, "struct ", struct_name, length(t.fields) > 0 ? params_string : ' ', _maybe_subtype(abstract_base_name))
89+
print(io, "struct ", struct_name, length(t.fields) > 0 ? params_string : ' ', _maybe_subtype(abstract_base_name, ctx.options))
9090
length(t.fields) > 0 && println(io)
9191
for field in t.fields
9292
generate_struct_field(io, field, ctx, type_params)
@@ -173,6 +173,7 @@ function translate(io, rp::ResolvedProtoFile, file_map::Dict{String,ResolvedProt
173173
end
174174
end # Otherwise all includes will happen in the enclosing module
175175
println(io, "import ProtoBuf as PB")
176+
options.common_abstract_type && println(io, "using ProtoBuf: AbstractProtoBufMessage")
176177
println(io, "using ProtoBuf: OneOf")
177178
println(io, "using EnumX: @enumx")
178179
if (is_namespaced(p) || options.always_use_modules) && !isempty(p.definitions)
@@ -191,7 +192,7 @@ function translate(io, rp::ResolvedProtoFile, file_map::Dict{String,ResolvedProt
191192
end
192193
!isempty(p.cyclic_definitions) && println(io, "\n\n# Abstract types to help resolve mutually recursive definitions")
193194
for name in p.cyclic_definitions
194-
println(io, "abstract type ", abstract_type_name(name), " end")
195+
println(io, "abstract type ", abstract_type_name(name), options.common_abstract_type ? " <: AbstractProtoBufMessage" : "", " end")
195196
end
196197
println(io)
197198
for def_name in p.sorted_definitions

src/codegen/types.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ function _get_type_bound(f::OneOfType, ctx::Context)
112112
return type
113113
end
114114

115-
function _maybe_subtype(name)
116-
isempty(name) && return ""
115+
function _maybe_subtype(name, options)
116+
isempty(name) && return options.common_abstract_type ? " <: AbstractProtoBufMessage" : ""
117117
return string(" <: ", abstract_type_name(name))
118118
end
119119

test/test_codegen.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ end
7878
end # module"""
7979
end
8080

81+
@testset "Minimal proto file with common abstract type" begin
82+
s, p, ctx = translate_simple_proto("", Options(always_use_modules=false, common_abstract_type=true))
83+
@test s == """
84+
import ProtoBuf as PB
85+
using ProtoBuf: AbstractProtoBufMessage
86+
using ProtoBuf: OneOf
87+
using EnumX: @enumx"""
88+
end
89+
8190
@testset "Minimal proto file with file imports" begin
8291
s, p, ctx = translate_simple_proto("import \"path/to/a\";", Dict("path/to/a" => ""), Options(always_use_modules=false))
8392
@test s == """
@@ -284,6 +293,25 @@ end
284293
@test CodeGenerators.jl_init_value(p.definitions["A"].fields[1], ctx) == "Ref{Union{Nothing,B}}(nothing)"
285294
end
286295

296+
@testset "Simple type with a common abstract type" begin
297+
s, p, ctx = translate_simple_proto("message B { optional int32 b = 1; }", Options(always_use_modules=false, common_abstract_type=true))
298+
@test generate_struct_str(p.definitions["B"], ctx) == """
299+
struct B <: AbstractProtoBufMessage
300+
b::Int32
301+
end
302+
"""
303+
end
304+
305+
@testset "Self-referential type with a common abstract type" begin
306+
s, p, ctx = translate_simple_proto("message B { optional B b = 1; }", Options(always_use_modules=false, common_abstract_type=true))
307+
@test occursin("abstract type var\"##AbstractB\" <: AbstractProtoBufMessage end", s)
308+
@test generate_struct_str(p.definitions["B"], ctx) == """
309+
struct B <: var"##AbstractB"
310+
b::Union{Nothing,B}
311+
end
312+
"""
313+
end
314+
287315
@testset "OneOf field codegen" begin
288316
s, p, ctx = translate_simple_proto("message A { oneof a { int32 b = 1; } }", Options(parametrize_oneofs=true))
289317
@test occursin("""

0 commit comments

Comments
 (0)