Skip to content

Conversation

lizziepaquette
Copy link

@lizziepaquette lizziepaquette commented Apr 22, 2020

This pr includes:

  • A new field option extension: brex.elixirpb.field, with one subfield: extype
  • wiring to add field options as argument to fields in dsl
  • wiring to add field options in message_props struct
  • new command line arg: custom_field_options? that flips generation of the above on and off
  • Hooks altering type definition, defaults, construction, encoding, and decoding in FieldOptionsProcessor
  • Functionality specific to extype: elixir type casting. When the extype option is present
  1. Google wrappers are unwrapped
  2. Google Timestamp is casting to datetime or naive datetime (depending on extype argument).

Usage

The proto below can be compiled with

protoc -I src -I test/protobuf/protoc/proto --elixir_out=custom_field_options=true:test/protobuf/protoc/proto_gen --plugin=./protoc-gen-elixir test/protobuf/protoc/proto/extension2.proto

extension2.proto

syntax = "proto3";

package ext;

import "brex_elixirpb.proto";
import "google/protobuf/wrappers.proto";
import "google/protobuf/timestamp.proto";

// To run
// protoc -I src -I test/protobuf/protoc/proto --elixir_out=custom_field_options=true:test/protobuf/protoc/proto_gen --plugin=./protoc-gen-elixir test/protobuf/protoc/proto/extension2.proto

message Nested{
  oneof my_timestamp {
    google.protobuf.Timestamp dt = 1 [(brex.elixirpb.field).extype="DateTime.t"];
    google.protobuf.Timestamp ndt = 2 [(brex.elixirpb.field).extype="NaiveDateTime.t"];
  }
}

message MyMessage {
  google.protobuf.DoubleValue f1 = 1 [(brex.elixirpb.field).extype="float"];
  google.protobuf.FloatValue f2 = 2 [(brex.elixirpb.field).extype="float"];
  google.protobuf.Int64Value f3 = 3 [(brex.elixirpb.field).extype="integer"];
  google.protobuf.UInt64Value f4 = 4 [(brex.elixirpb.field).extype="non_neg_integer"];
  google.protobuf.Int32Value f5 = 5 [(brex.elixirpb.field).extype="integer"];
  google.protobuf.UInt32Value f6 = 6 [(brex.elixirpb.field).extype="non_neg_integer"];
  google.protobuf.BoolValue f7 = 7 [(brex.elixirpb.field).extype="boolean"];
  google.protobuf.StringValue f8 = 8 [(brex.elixirpb.field).extype="String.t"];
  google.protobuf.BytesValue f9 = 9 [(brex.elixirpb.field).extype="String.t()"];

  google.protobuf.StringValue no_extype = 10;
  repeated google.protobuf.StringValue repeated_field = 11 [(brex.elixirpb.field).extype="String.t"];
  uint64 normal1 = 12;
  string normal2 = 13;
  Nested nested = 14;
}

It generates

defmodule Ext.Nested do
  @moduledoc false
  use Protobuf, custom_field_options?: true, syntax: :proto3

  @type t :: %__MODULE__{
          my_timestamp: {atom, any}
        }
  defstruct [:my_timestamp]

  oneof :my_timestamp, 0

  field :dt, 1, type: Google.Protobuf.Timestamp, oneof: 0, options: [extype: "DateTime.t"]
  field :ndt, 2, type: Google.Protobuf.Timestamp, oneof: 0, options: [extype: "NaiveDateTime.t"]
end

defmodule Ext.MyMessage do
  @moduledoc false
  use Protobuf, custom_field_options?: true, syntax: :proto3

  @type t :: %__MODULE__{
          f1: float | nil,
          f2: float | nil,
          f3: integer | nil,
          f4: non_neg_integer | nil,
          f5: integer | nil,
          f6: non_neg_integer | nil,
          f7: boolean | nil,
          f8: String.t() | nil,
          f9: String.t() | nil,
          no_extype: Google.Protobuf.StringValue.t() | nil,
          repeated_field: [String.t()],
          normal1: non_neg_integer,
          normal2: String.t(),
          nested: Ext.Nested.t() | nil
        }
  defstruct [
    :f1,
    :f2,
    :f3,
    :f4,
    :f5,
    :f6,
    :f7,
    :f8,
    :f9,
    :no_extype,
    :repeated_field,
    :normal1,
    :normal2,
    :nested
  ]

  field :f1, 1, type: Google.Protobuf.DoubleValue, options: [extype: "float"]
  field :f2, 2, type: Google.Protobuf.FloatValue, options: [extype: "float"]
  field :f3, 3, type: Google.Protobuf.Int64Value, options: [extype: "integer"]
  field :f4, 4, type: Google.Protobuf.UInt64Value, options: [extype: "non_neg_integer"]
  field :f5, 5, type: Google.Protobuf.Int32Value, options: [extype: "integer"]
  field :f6, 6, type: Google.Protobuf.UInt32Value, options: [extype: "non_neg_integer"]
  field :f7, 7, type: Google.Protobuf.BoolValue, options: [extype: "boolean"]
  field :f8, 8, type: Google.Protobuf.StringValue, options: [extype: "String.t"]
  field :f9, 9, type: Google.Protobuf.BytesValue, options: [extype: "String.t()"]
  field :no_extype, 10, type: Google.Protobuf.StringValue

  field :repeated_field, 11,
    repeated: true,
    type: Google.Protobuf.StringValue,
    options: [extype: "String.t"]

  field :normal1, 12, type: :uint64
  field :normal2, 13, type: :string
  field :nested, 14, type: Ext.Nested
end

And the struct can be used and verified as so:

dt = DateTime.from_unix!(1_464_096_368, :microsecond)

msg = Ext.MyMessage.new(
  f1: 1.0,
  f2: 2.0,
  f3: 3,
  f4: 4,
  f5: 5,
  f6: 6,
  f7: true,
  f8: "8",
  f9: "9",
  nested: Ext.Nested.new(my_timestamp: {:dt, dt}),
  no_extype: %Google.Protobuf.StringValue{value: "none"},
  normal1: 1234,
  normal2: "hello",
  repeated_field: ["r1", "r2"]
)

assert msg |> Ext.MyMessage.encode() |> Ext.MyMessage.decode() == msg

@whatyouhide
Copy link
Collaborator

This should be possible to do now with "tranform modules" (see CHANGELOG). Closing this PR as we don't plan on supporting this directly for the time being and we don't have the resources to take this on, but let us know if you'll have any problems with transform_module. Thanks @lizziepaquette!

@whatyouhide whatyouhide closed this Nov 6, 2021
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

Successfully merging this pull request may close these issues.

2 participants