Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type Any #194

Open
whatsthecraic opened this issue Feb 9, 2022 · 1 comment
Open

Type Any #194

whatsthecraic opened this issue Feb 9, 2022 · 1 comment

Comments

@whatsthecraic
Copy link

Can you insert another ProtoBuf message inside a field of type Any? If so, how?
The google doc refers that this depends on the target language.

@Drvi
Copy link
Collaborator

Drvi commented Aug 20, 2022

So, type Any contains two fields:

  string type_url = 1;
  bytes value = 2;

value is the protobuf-encoded message and type_url contains the type information of the encoded message. IIUC, there are two ways you can get the message type from the type_url:

a) You can simply parse the fully qualified name of the type from the last part of type_url (e.g. for "path/google.protobuf.Duration" -> you'd go grab the google module, the protobuf submodule and finally the Duration type).

b) The docs mention type-servers that you can hit with GET requests, querying type_url should return another well-known type back, the Type type, which contains metadata you can use to define a new type on the fly.


I think we can support a) by providing a custom decode method for Any. Here is a small example:

Let's say there is a struct C defined in a submodule A.B:

module A module B struct C; x::Int end end end

Then, you can do e.g. the following to get the Julia type from a url_string:

function type_from_type_url(x)
    m::Module = @__MODULE__
    i = findlast('/', x)
    @assert !isnothing(i)
    @inbounds while true
        j = findnext('.', x, i+1)
        isnothing(j) && return getfield(m, Symbol(@view x[i+1:end]))
        m = getfield(m, Symbol(view(x, i+1:j-1)))::Module
        i = j
    end
end

get_from_module_manual("abcd/efgh/x/A.B.C")(1)
# Main.A.B.C(1)

# benchmark: 364.380 ns (1 allocation: 16 bytes)

With this, we could provide a special decode method:

function decode(d::AbstractProtoDecoder, x::Type{var"#Any"})
    any_message = _decode_any_type(d)
    T = type_from_type_url(any_message.type_url)
    return decode(ProtoDecoder(IOBuffer(any_message.value)), T)
end

For b), IMO, a lot more design work is needed. We'd need to fetch the Type metadata efficiently, figure out how to construct/represent actual Julia types based on the Type metadada (maybe just NamedTuples?), we'd need a special decode method to work on value bytes using the metadata at runtime.

Frankly, I think it would be better to only support Any for types that are guaranteed to be defined in a Julia session.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants