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

Derived types: Type descriminator support #1652

Open
TonEnfer opened this issue Dec 23, 2024 · 1 comment
Open

Derived types: Type descriminator support #1652

TonEnfer opened this issue Dec 23, 2024 · 1 comment
Labels
enhancement New feature or request

Comments

@TonEnfer
Copy link
Contributor

Protobuf, when using the OneOf construct, generates an enum and a <Field>OneOfCase property, which contains one of the values ​​indicating which of the body fields is filled. This information can be used when generating the mapper with inheritance.

Proto:

message Message
{
    oneof Message 
    {
        Ping Ping = 1;
        Ack Ack = 2;
    }
}

Generated class (simplified):

public sealed partial class Message : IMessage<Message>
{
    public Ping { get; set; }
    public Ack { get; set; }
    public MessageOneofCase MessageCase { get; }
    public enum MessageOneofCase
    {
        Ping = 1,
        Ack = 2,
    }
}

Mapper config:

public partial class MessageMapper
{
    [MapOneOfDerivedType(nameof(@Message.MessageOneofCase.Ack), typeof(AckProtocolMessage))]
    [MapOneOfDerivedType(nameof(@Message.MessageOneofCase.Ping), typeof(PingProtocolMessage))]
    public partial ProtocolMessage MapToProtocolMessage(Message message);

    [MapOneOfDerivedType(nameof(@Message.MessageOneofCase.Ack), typeof(AckProtocolMessage))]
    [MapOneOfDerivedType(nameof(@Message.MessageOneofCase.Ping), typeof(PingProtocolMessage))]
    public partial Message MapToMessage(ProtocolMessage message);
}

Generated mapper:

public partial class MessageMapper
{
    public partial ProtocolMessage MapToProtocolMessage(Message message)
    {
        return message switch
        {
              AckProtocolMessage ackProtocolMessage => MapToProtocolMessage(ackProtocolMessage),
              PingProtocolMessage pingProtocolMessage => MapToProtocolMessage(pingProtocolMessage),
              _ => throw new ArgumentOutOfRangeException();
        }
    }

    public partial Message MapToMessage(ProtocolMessage message)
    { 
        return message.MessageCase switch
        {
            Message.MessageOneofCase.Ack => MapToAckMessage(message.Ack),
            Message.MessageOneofCase.Ping => MapToPingMessage(message.Ping),
            _ => throw new ArgumentOutOfRangeException();
        }
    }

    private PingProtocolMessage MapToPingMessage(Ping ping) { ... }
    private AckProtocolMessage MapToAckMessage(Ack ack) { ... }

    private ProtocolMessage MapToProtocolMessage(AckProtocolMessage ackProtocolMessage) { ... }
    private ProtocolMessage MapToProtocolMessage(PingProtocolMessage pingProtocolMessage) { ... }
}

I realize this may seem like a "niche" situation, but gRPC and Protobuf usage is pretty widespread, so OneOf inheritance support would be a nice addition.

@TonEnfer TonEnfer added the enhancement New feature or request label Dec 23, 2024
@latonz
Copy link
Contributor

latonz commented Dec 23, 2024

Thanks for raising this point! A more protobuf-independent approach could indeed be achieved using a type discriminator for mapping derived types. This would align with how System.Text.Json handles polymorphism (see docs).

For example, the Mapperly API could look like this:

public partial class MessageMapper
{
    [DerivedTypeDiscriminator(nameof(Message.MessageCase))]
    [MapDerivedType<AckProtocolMessage>(Message.MessageOneofCase.Ack)]
    [MapDerivedType<PingProtocolMessage>(Message.MessageOneofCase.Ping)]
    public partial ProtocolMessage MapToProtocolMessage(Message message);
}

This design would not only support protobuf, but could also work seamlessly with other type discriminators, making the feature more versatile and broadly applicable.

What do you think about this approach?

@latonz latonz changed the title Protobuf OneOf support Derived types: Type descriminator support Jan 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants