///|
fn CodeGenerator::field_codec_value(
self : CodeGenerator,
field : Field,
parent_path : ImportPath,
) -> FieldCodecValue raise {
guard field.type_ is Some(type_) else {
raise @model.UnexpectedType(
"Field type is not set: \{field.import_path.name()}",
)
}
let type_name = if field.is_enum() || field.is_message() {
self.get_moonbit_type(field, parent_path~)
} else {
""
}
{ type_, type_name, }
}
///|
fn CodeGenerator::field_codec_shape(
self : CodeGenerator,
field : Field,
parent_path : ImportPath,
) -> FieldCodecShape raise {
let value = self.field_codec_value(field, parent_path)
let envelope = self.field_envelope_shape(field, is_packed=field.is_pack())
let mut cardinality = FieldCodecCardinality::SingularField
let mut key = None
let mut key_envelope = None
let mut map_value = None
let mut map_value_envelope = None
if field.is_pack() {
cardinality = FieldCodecCardinality::PackedField
} else if field.map_key_value() is Some(map_entry) {
cardinality = FieldCodecCardinality::MapField
key = Some(self.field_codec_value(map_entry.fields[0], parent_path))
key_envelope = Some(self.field_envelope_shape(map_entry.fields[0]))
map_value = Some(self.field_codec_value(map_entry.fields[1], parent_path))
map_value_envelope = Some(self.field_envelope_shape(map_entry.fields[1]))
} else if field.is_list() {
cardinality = FieldCodecCardinality::RepeatedField
} else if field.is_optional() {
cardinality = FieldCodecCardinality::OptionalField
}
let presence = match cardinality {
PackedField | RepeatedField | MapField =>
FieldCodecPresence::EmitFieldWhenNonEmpty
OptionalField => FieldCodecPresence::EmitFieldWhenSome
SingularField =>
if field.has_implicit_presence() {
FieldCodecPresence::EmitFieldWhenNonDefault
} else {
FieldCodecPresence::AlwaysEmitField
}
}
let read_mode = if field.is_list() && field.is_packable_scalar() {
FieldCodecReadMode::ReadPackedAndUnpackedField
} else {
FieldCodecReadMode::ReadUnpackedField
}
{
field_name: field.import_path.name() |> pascal_to_snake,
value,
envelope,
cardinality,
key,
key_envelope,
map_value,
map_value_envelope,
presence,
read_mode,
}
}
///|
fn FieldCodecValue::read_expr(
self : FieldCodecValue,
envelope : FieldEnvelopeShape,
is_async? : Bool = false,
) -> String raise {
let async_keyword = if is_async { "async_" } else { "" }
match self.type_ {
FieldDescriptorProto_Type::TYPE_BOOL
| FieldDescriptorProto_Type::TYPE_INT32
| FieldDescriptorProto_Type::TYPE_INT64
| FieldDescriptorProto_Type::TYPE_UINT32
| FieldDescriptorProto_Type::TYPE_UINT64
| FieldDescriptorProto_Type::TYPE_FIXED32
| FieldDescriptorProto_Type::TYPE_FIXED64
| FieldDescriptorProto_Type::TYPE_SFIXED32
| FieldDescriptorProto_Type::TYPE_SFIXED64
| FieldDescriptorProto_Type::TYPE_FLOAT
| FieldDescriptorProto_Type::TYPE_DOUBLE
| FieldDescriptorProto_Type::TYPE_STRING
| FieldDescriptorProto_Type::TYPE_BYTES =>
"reader |> \{kind_read_func(self.type_, is_async~)}()"
FieldDescriptorProto_Type::TYPE_SINT32
| FieldDescriptorProto_Type::TYPE_SINT64 =>
"(reader |> \{kind_read_func(self.type_, is_async~)}()).0"
FieldDescriptorProto_Type::TYPE_ENUM =>
"reader |> @lib.\{async_keyword}read_enum() |> \{self.type_name}::from_enum"
FieldDescriptorProto_Type::TYPE_MESSAGE
| FieldDescriptorProto_Type::TYPE_GROUP =>
match envelope.kind {
DelimitedMessageEnvelope =>
"(reader |> @lib.\{async_keyword}read_delimited_message(\{envelope.field_number}U) : \{self.type_name})"
LengthDelimitedEnvelope =>
"(reader |> @lib.\{async_keyword}read_message() : \{self.type_name})"
_ => raise @model.UnexpectedType("unexpected message field envelope")
}
}
}
///|
fn FieldCodecValue::write_expr(
self : FieldCodecValue,
envelope : FieldEnvelopeShape,
variable : String,
is_async? : Bool = false,
) -> String raise {
let async_keyword = if is_async { "async_" } else { "" }
match self.type_ {
FieldDescriptorProto_Type::TYPE_STRING =>
"writer |> @lib.\{async_keyword}write_string(\{variable})"
FieldDescriptorProto_Type::TYPE_BYTES =>
"writer |> @lib.\{async_keyword}write_bytes(\{variable})"
FieldDescriptorProto_Type::TYPE_MESSAGE
| FieldDescriptorProto_Type::TYPE_GROUP =>
match envelope.kind {
DelimitedMessageEnvelope =>
"writer |> @lib.\{async_keyword}write_delimited_message(\{envelope.field_number}U, \{variable})"
LengthDelimitedEnvelope =>
"writer |> @lib.\{async_keyword}write_message(\{variable})"
_ => raise @model.UnexpectedType("unexpected message field envelope")
}
FieldDescriptorProto_Type::TYPE_FIXED32 =>
"writer |> @lib.\{async_keyword}write_fixed32(\{variable})"
FieldDescriptorProto_Type::TYPE_SFIXED32 =>
"writer |> @lib.\{async_keyword}write_sfixed32(\{variable})"
FieldDescriptorProto_Type::TYPE_FLOAT =>
"writer |> @lib.\{async_keyword}write_float(\{variable})"
FieldDescriptorProto_Type::TYPE_FIXED64 =>
"writer |> @lib.\{async_keyword}write_fixed64(\{variable})"
FieldDescriptorProto_Type::TYPE_SFIXED64 =>
"writer |> @lib.\{async_keyword}write_sfixed64(\{variable})"
FieldDescriptorProto_Type::TYPE_DOUBLE =>
"writer |> @lib.\{async_keyword}write_double(\{variable})"
FieldDescriptorProto_Type::TYPE_BOOL =>
"writer |> @lib.\{async_keyword}write_bool(\{variable})"
FieldDescriptorProto_Type::TYPE_INT32 =>
"writer |> @lib.\{async_keyword}write_int32(\{variable})"
FieldDescriptorProto_Type::TYPE_INT64 =>
"writer |> @lib.\{async_keyword}write_int64(\{variable})"
FieldDescriptorProto_Type::TYPE_SINT32 =>
"writer |> @lib.\{async_keyword}write_sint32(\{variable})"
FieldDescriptorProto_Type::TYPE_SINT64 =>
"writer |> @lib.\{async_keyword}write_sint64(\{variable})"
FieldDescriptorProto_Type::TYPE_UINT32 =>
"writer |> @lib.\{async_keyword}write_uint32(\{variable})"
FieldDescriptorProto_Type::TYPE_UINT64 =>
"writer |> @lib.\{async_keyword}write_uint64(\{variable})"
FieldDescriptorProto_Type::TYPE_ENUM =>
"writer |> @lib.\{async_keyword}write_enum(\{variable}.to_enum())"
}
}
///|
fn FieldCodecValue::packed_reader_func(
self : FieldCodecValue,
is_async? : Bool = false,
) -> String raise {
let async_keyword = if is_async { "async_" } else { "" }
match self.type_ {
FieldDescriptorProto_Type::TYPE_ENUM => "@lib.\{async_keyword}read_enum"
_ => kind_read_func(self.type_, is_async~)
}
}
///|
fn FieldCodecValue::unpacked_wire_type(self : FieldCodecValue) -> Int raise {
match self.type_ {
FieldDescriptorProto_Type::TYPE_BOOL
| FieldDescriptorProto_Type::TYPE_ENUM
| FieldDescriptorProto_Type::TYPE_INT32
| FieldDescriptorProto_Type::TYPE_INT64
| FieldDescriptorProto_Type::TYPE_SINT32
| FieldDescriptorProto_Type::TYPE_SINT64
| FieldDescriptorProto_Type::TYPE_UINT32
| FieldDescriptorProto_Type::TYPE_UINT64 => WIRE_TYPE_VARINT
FieldDescriptorProto_Type::TYPE_SFIXED64
| FieldDescriptorProto_Type::TYPE_FIXED64
| FieldDescriptorProto_Type::TYPE_DOUBLE => WIRE_TYPE_FIXED64
FieldDescriptorProto_Type::TYPE_SFIXED32
| FieldDescriptorProto_Type::TYPE_FIXED32
| FieldDescriptorProto_Type::TYPE_FLOAT => WIRE_TYPE_FIXED32
_ => raise @model.UnexpectedType("unexpected packable field type")
}
}
///|
fn FieldCodecValue::packed_element_size(self : FieldCodecValue) -> String raise {
match self.type_ {
FieldDescriptorProto_Type::TYPE_BOOL
| FieldDescriptorProto_Type::TYPE_ENUM
| FieldDescriptorProto_Type::TYPE_INT32
| FieldDescriptorProto_Type::TYPE_INT64
| FieldDescriptorProto_Type::TYPE_SINT32
| FieldDescriptorProto_Type::TYPE_SINT64
| FieldDescriptorProto_Type::TYPE_UINT32
| FieldDescriptorProto_Type::TYPE_UINT64 => "None"
FieldDescriptorProto_Type::TYPE_SFIXED64
| FieldDescriptorProto_Type::TYPE_FIXED64
| FieldDescriptorProto_Type::TYPE_DOUBLE => "Some(8)"
FieldDescriptorProto_Type::TYPE_SFIXED32
| FieldDescriptorProto_Type::TYPE_FIXED32
| FieldDescriptorProto_Type::TYPE_FLOAT => "Some(4)"
_ => raise @model.UnexpectedType("unexpected packable field type")
}
}
///|
fn FieldCodecValue::field_size_expr(
self : FieldCodecValue,
envelope : FieldEnvelopeShape,
value_expr : String,
) -> (String, Bool) {
match self.type_ {
FieldDescriptorProto_Type::TYPE_STRING
| FieldDescriptorProto_Type::TYPE_BYTES =>
(envelope.size_expr("@lib.size_of(\{value_expr})"), true)
FieldDescriptorProto_Type::TYPE_MESSAGE
| FieldDescriptorProto_Type::TYPE_GROUP =>
(envelope.size_expr("@lib.size_of(\{value_expr})"), true)
FieldDescriptorProto_Type::TYPE_FIXED32
| FieldDescriptorProto_Type::TYPE_SFIXED32
| FieldDescriptorProto_Type::TYPE_FLOAT => {
let fixed_size = sizeFixed32()
(envelope.size_expr("\{fixed_size}U"), false)
}
FieldDescriptorProto_Type::TYPE_FIXED64
| FieldDescriptorProto_Type::TYPE_SFIXED64
| FieldDescriptorProto_Type::TYPE_DOUBLE => {
let fixed_size = sizeFixed64()
(envelope.size_expr("\{fixed_size}U"), false)
}
_ => (envelope.size_expr("@lib.size_of(\{value_expr})"), true)
}
}
///|
fn FieldCodecShape::packed_data_size_expr(
self : FieldCodecShape,
) -> String raise {
match self.value.type_ {
FieldDescriptorProto_Type::TYPE_FIXED32
| FieldDescriptorProto_Type::TYPE_SFIXED32
| FieldDescriptorProto_Type::TYPE_FLOAT => {
let fixed_size = sizeFixed32()
"self.\{self.field_name}.length().reinterpret_as_uint() * \{fixed_size}"
}
FieldDescriptorProto_Type::TYPE_FIXED64
| FieldDescriptorProto_Type::TYPE_SFIXED64
| FieldDescriptorProto_Type::TYPE_DOUBLE => {
let fixed_size = sizeFixed64()
"self.\{self.field_name}.length().reinterpret_as_uint() * \{fixed_size}"
}
FieldDescriptorProto_Type::TYPE_INT32
| FieldDescriptorProto_Type::TYPE_INT64
| FieldDescriptorProto_Type::TYPE_SINT32
| FieldDescriptorProto_Type::TYPE_SINT64
| FieldDescriptorProto_Type::TYPE_UINT32
| FieldDescriptorProto_Type::TYPE_UINT64
| FieldDescriptorProto_Type::TYPE_BOOL
| FieldDescriptorProto_Type::TYPE_ENUM =>
"self.\{self.field_name}.iter().map(@lib.size_of).fold(init=0U, UInt::add)"
_ => raise @model.UnexpectedType("unexpected packed field type")
}
}
///|
fn FieldCodecShape::packed_delta_size_expr(
self : FieldCodecShape,
) -> String raise {
let data_size = self.packed_data_size_expr()
self.envelope.size_expr(data_size)
}
///|
fn FieldCodecShape::presence_guard_expr(
self : FieldCodecShape,
value_binding? : String = "v",
) -> String? {
match self.presence {
FieldCodecPresence::AlwaysEmitField => None
FieldCodecPresence::EmitFieldWhenSome =>
Some("self.\{self.field_name} is Some(\{value_binding})")
FieldCodecPresence::EmitFieldWhenNonDefault =>
Some("self.\{self.field_name} != Default::default()")
FieldCodecPresence::EmitFieldWhenNonEmpty =>
Some("!self.\{self.field_name}.is_empty()")
}
}
///|
fn CodeGenerator::gen_field_codec_read(
_ : CodeGenerator,
shape : FieldCodecShape,
content : StringBuilder,
is_async? : Bool = false,
) -> Unit raise {
let async_keyword = if is_async { "async" } else { "" }
let async_keyword_to_snake = async_keyword |> pascal_to_snake
match shape.read_mode {
FieldCodecReadMode::ReadPackedAndUnpackedField => {
let packed_read = "reader |> @lib.\{async_keyword_to_snake}read_packed(\{shape.value.packed_reader_func(is_async~)}, \{shape.value.packed_element_size()})"
match shape.value.type_ {
FieldDescriptorProto_Type::TYPE_SINT32
| FieldDescriptorProto_Type::TYPE_SINT64 =>
content.write_string(
" (\{shape.envelope.field_number}, \{runtime_wire_type_const(WIRE_TYPE_LENGTH_DELIMITED)}) => { let packed = \{packed_read}; for item in packed { msg.\{shape.field_name}.push(item.0) } }\n",
)
FieldDescriptorProto_Type::TYPE_ENUM =>
content.write_string(
" (\{shape.envelope.field_number}, \{runtime_wire_type_const(WIRE_TYPE_LENGTH_DELIMITED)}) => { let packed = \{packed_read}; for item in packed { msg.\{shape.field_name}.push(\{shape.value.type_name}::from_enum(item)) } }\n",
)
_ =>
content.write_string(
" (\{shape.envelope.field_number}, \{runtime_wire_type_const(WIRE_TYPE_LENGTH_DELIMITED)}) => { msg.\{shape.field_name}.push_iter((\{packed_read}).iter()) }\n",
)
}
content.write_string(
" (\{shape.envelope.field_number}, \{runtime_wire_type_const(shape.value.unpacked_wire_type())}) => msg.\{shape.field_name}.push(\{shape.value.read_expr(shape.envelope, is_async~)})\n",
)
return
}
FieldCodecReadMode::ReadUnpackedField => ()
}
match shape.cardinality {
PackedField => raise @model.UnexpectedType("unpackable packed field")
MapField =>
content.write_string(
" (\{shape.envelope.field_number}, \{shape.envelope.read_wire_pattern(shape.value)}) => { let {key, value} = \{shape.value.read_expr(shape.envelope, is_async~)}; msg.\{shape.field_name}[key] = value }\n",
)
RepeatedField =>
content.write_string(
" (\{shape.envelope.field_number}, \{shape.envelope.read_wire_pattern(shape.value)}) => msg.\{shape.field_name}.push(\{shape.value.read_expr(shape.envelope, is_async~)})\n",
)
OptionalField =>
content.write_string(
" (\{shape.envelope.field_number}, \{shape.envelope.read_wire_pattern(shape.value)}) => msg.\{shape.field_name} = \{shape.value.read_expr(shape.envelope, is_async~)} |> Some\n",
)
SingularField =>
content.write_string(
" (\{shape.envelope.field_number}, \{shape.envelope.read_wire_pattern(shape.value)}) => msg.\{shape.field_name} = \{shape.value.read_expr(shape.envelope, is_async~)}\n",
)
}
}
///|
fn CodeGenerator::gen_field_codec_write(
_ : CodeGenerator,
shape : FieldCodecShape,
content : StringBuilder,
is_async? : Bool = false,
) -> Unit raise {
let async_keyword = if is_async { "async" } else { "" }
let async_keyword_to_snake = async_keyword |> pascal_to_snake
match shape.cardinality {
PackedField => {
let tag_val = shape.envelope.tag_value()
guard shape.presence_guard_expr() is Some(presence_guard) else {
raise @model.UnexpectedType("packed field without presence guard")
}
content.write_string(" if \{presence_guard} {\n")
content.write_string(
" writer |> @lib.\{async_keyword_to_snake}write_varint(\{tag_val}UL)\n",
)
content.write_string(" let size = \{shape.packed_data_size_expr()}\n")
let field_write_type = shape.value.write_expr(
shape.envelope,
"item",
is_async~,
)
content.write_string(
(
$| writer |> @lib.\{async_keyword_to_snake}write_uint32(size)
$| for item in self.\{shape.field_name} {
$| \{field_write_type}
$| }
$| }
$|
),
)
}
MapField => {
let key_field = shape.key.unwrap()
let value_field = shape.map_value.unwrap()
let key_envelope = shape.key_envelope.unwrap()
let value_envelope = shape.map_value_envelope.unwrap()
let tag_val = shape.envelope.tag_value()
let (key_size, _) = key_field.field_size_expr(key_envelope, "k")
let (value_size, _) = value_field.field_size_expr(value_envelope, "v")
let key_tag_val = key_envelope.tag_value()
let value_tag_val = value_envelope.tag_value()
let field_write_type_k = key_field.write_expr(
key_envelope,
"k",
is_async~,
)
let field_write_type_v = value_field.write_expr(
value_envelope,
"v",
is_async~,
)
match shape.envelope.kind {
DelimitedMessageEnvelope =>
content.write_string(
(
$| let keys = self.\{shape.field_name}.keys().collect()
$| for i in 0.. @lib.\{async_keyword_to_snake}write_varint(\{tag_val}UL)
$| writer |> @lib.\{async_keyword_to_snake}write_varint(\{key_tag_val}UL)
$| \{field_write_type_k}
$| writer |> @lib.\{async_keyword_to_snake}write_varint(\{value_tag_val}UL);
$| \{field_write_type_v}
$| writer |> @lib.\{async_keyword_to_snake}write_tag((\{shape.envelope.field_number}U, @lib.WIRE_TYPE_END_GROUP))
$| }
$|
),
)
LengthDelimitedEnvelope =>
content.write_string(
(
$| let keys = self.\{shape.field_name}.keys().collect()
$| for i in 0.. @lib.\{async_keyword_to_snake}write_varint(\{tag_val}UL)
$| let key_size = \{key_size}
$| let value_size = \{value_size}
$| writer |> @lib.\{async_keyword_to_snake}write_uint32(key_size + value_size)
$| writer |> @lib.\{async_keyword_to_snake}write_varint(\{key_tag_val}UL)
$| \{field_write_type_k}
$| writer |> @lib.\{async_keyword_to_snake}write_varint(\{value_tag_val}UL);
$| \{field_write_type_v}
$| }
$|
),
)
_ => raise @model.UnexpectedType("unexpected map field envelope")
}
}
RepeatedField => {
let tag_val = shape.envelope.tag_value()
let field_write_type = shape.value.write_expr(
shape.envelope,
"item",
is_async~,
)
content.write_string(" for item in self.\{shape.field_name} {\n")
if !shape.envelope.writes_own_field_tags() {
content.write_string(
" writer |> @lib.\{async_keyword_to_snake}write_varint(\{tag_val}UL)\n",
)
}
content.write_string(" \{field_write_type}\n")
content.write_string(" }\n")
}
OptionalField => {
let field_write_type = shape.value.write_expr(
shape.envelope,
"v",
is_async~,
)
let tag_val = shape.envelope.tag_value()
guard shape.presence_guard_expr() is Some(presence_guard) else {
raise @model.UnexpectedType("optional field without presence guard")
}
content.write_string(" if \{presence_guard} {\n")
if !shape.envelope.writes_own_field_tags() {
content.write_string(
" writer |> @lib.\{async_keyword_to_snake}write_varint(\{tag_val}UL);\n",
)
}
content.write_string(" \{field_write_type}\n")
content.write_string(" }\n")
}
SingularField => {
let field_write_type = shape.value.write_expr(
shape.envelope,
"self.\{shape.field_name}",
is_async~,
)
let tag_val = shape.envelope.tag_value()
let field_write = if shape.envelope.writes_own_field_tags() {
" \{field_write_type}\n"
} else {
" writer |> @lib.\{async_keyword_to_snake}write_varint(\{tag_val}UL);\n \{field_write_type}\n"
}
match shape.presence_guard_expr() {
Some(presence_guard) => {
content.write_string(" if \{presence_guard} {\n")
content.write_string(field_write)
content.write_string(" }\n")
}
None => content.write_string(field_write)
}
}
}
}
///|
fn CodeGenerator::gen_field_codec_size(
_ : CodeGenerator,
shape : FieldCodecShape,
content : StringBuilder,
) -> Unit raise {
match shape.cardinality {
PackedField => {
let delta_size = shape.packed_delta_size_expr()
guard shape.presence_guard_expr() is Some(presence_guard) else {
raise @model.UnexpectedType("packed field without presence guard")
}
content.write_string(
(
$| if \{presence_guard} {
$| size += \{delta_size}
$| }
$|
),
)
}
MapField => {
let key_field = shape.key.unwrap()
let value_field = shape.map_value.unwrap()
let key_envelope = shape.key_envelope.unwrap()
let value_envelope = shape.map_value_envelope.unwrap()
let (key_size, _) = key_field.field_size_expr(key_envelope, "k")
let (value_size, _) = value_field.field_size_expr(value_envelope, "v")
let field_size = shape.envelope.size_expr("key_size + value_size")
content.write_string(
(
$| for k, v in self.\{shape.field_name} {
$| let key_size = \{key_size}
$| let value_size = \{value_size}
$| size += \{field_size}
$| }
$|
),
)
}
RepeatedField => {
let (delta_size, _) = shape.value.field_size_expr(shape.envelope, "s")
content.write_string(
(
$| for s in self.\{shape.field_name} {
$| size += \{delta_size}
$| }
$|
),
)
}
OptionalField => {
let (delta_size, need_v) = shape.value.field_size_expr(
shape.envelope,
"v",
)
let value_binding = if need_v { "v" } else { "_" }
guard shape.presence_guard_expr(value_binding~) is Some(presence_guard) else {
raise @model.UnexpectedType("optional field without presence guard")
}
content.write_string(
(
$| if \{presence_guard} {
$| size += \{delta_size}
$| }
$|
),
)
}
SingularField => {
let (delta_size, _) = shape.value.field_size_expr(
shape.envelope,
"self.\{shape.field_name}",
)
match shape.presence_guard_expr() {
Some(presence_guard) =>
content.write_string(
(
$| if \{presence_guard} {
$| size += \{delta_size}
$| }
$|
),
)
None => content.write_string(" size += \{delta_size}\n")
}
}
}
}