///|
fn CodeGenerator::get_moonbit_type(
  self : CodeGenerator,
  field : Field,
  parent_path? : ImportPath,
) -> String raise {
  guard field.type_ is Some(typ) else { raise @model.UnsupportedType("Group") }
  return match typ {
    FieldDescriptorProto_Type::TYPE_DOUBLE => "Double"
    FieldDescriptorProto_Type::TYPE_FLOAT => "Float"
    FieldDescriptorProto_Type::TYPE_INT64 => "Int64"
    FieldDescriptorProto_Type::TYPE_UINT64 => "UInt64"
    FieldDescriptorProto_Type::TYPE_INT32 => "Int"
    FieldDescriptorProto_Type::TYPE_FIXED64 => "UInt64"
    FieldDescriptorProto_Type::TYPE_FIXED32 => "UInt"
    FieldDescriptorProto_Type::TYPE_BOOL => "Bool"
    FieldDescriptorProto_Type::TYPE_STRING => "String"
    FieldDescriptorProto_Type::TYPE_MESSAGE
    | FieldDescriptorProto_Type::TYPE_GROUP => {
      let message_name = field.type_name.unwrap()
      if self.find_message(message_name) is Some(message) {
        let message_path = message.import_path
        if parent_path is Some(path) &&
          path.package_name != message_path.package_name {
          return "@\{self.qualified_name(message_path)}.\{message_path.path() |> to_camel_case}"
        }
        return message_path.path() |> to_camel_case
      }
      raise @model.UnsupportedType("Message \{message_name} not found")
    }
    FieldDescriptorProto_Type::TYPE_BYTES => "Bytes"
    FieldDescriptorProto_Type::TYPE_UINT32 => "UInt"
    FieldDescriptorProto_Type::TYPE_ENUM => {
      let enum_name = field.type_name.unwrap()
      if self.find_enum(enum_name) is Some(enum_) {
        let enum_path = enum_.import_path
        if parent_path is Some(parent_path) &&
          parent_path.package_name != enum_path.package_name {
          return "@\{self.qualified_name(enum_path)}.\{enum_path.path() |> to_camel_case}"
        }
        return enum_path.path() |> to_camel_case
      }
      raise @model.UnsupportedType("Enum \{enum_name} not found")
    }
    FieldDescriptorProto_Type::TYPE_SFIXED32 => "Int"
    FieldDescriptorProto_Type::TYPE_SFIXED64 => "Int64"
    FieldDescriptorProto_Type::TYPE_SINT32 => "Int"
    FieldDescriptorProto_Type::TYPE_SINT64 => "Int64"
  }
}

///|
fn CodeGenerator::gen_messages(
  self : CodeGenerator,
  messages : Array[Message],
  content : StringBuilder,
) -> Unit raise {
  for message in messages {
    for enum_ in message.enums {
      self.gen_enum(enum_, content)
    }
    self.gen_messages(message.messages, content)
    self.gen_message(message, content)
  }
}

///|
fn CodeGenerator::gen_message(
  self : CodeGenerator,
  message : Message,
  file : StringBuilder,
) -> Unit raise {
  let shape = self.message_shape(message)
  file.write_string("pub(all) struct \{shape.message_name} {\n")
  for field in shape.storage_fields {
    file.write_string("  mut \{field.field_name} : \{field.declaration_type}\n")
  }
  let derive_list = self.derive_list().join(", ")
  for oneof in shape.oneofs {
    file.write_string("  mut \{oneof.field_name} : \{oneof.enum_name}\n")
  }
  file.write_string("} derive(\{derive_list})\n")
  for oneof in shape.oneofs {
    self.gen_oneof_enum(oneof, file)
  }
  self.gen_message_size(shape, file)
  self.gen_message_default(shape, file)
  self.gen_message_new(shape, file)
  self.gen_message_reader(shape, file)
  self.gen_message_writer(shape, file)
  if self.support_json() {
    self.gen_message_json(shape, file)
  }
  if self.support_async() {
    self.gen_message_reader(shape, file, is_async=true)
    self.gen_message_writer(shape, file, is_async=true)
  }
}

///|
fn CodeGenerator::gen_message_default(
  _ : CodeGenerator,
  shape : MessageShape,
  content : StringBuilder,
) -> Unit {
  content.write_string(
    (
      $|pub impl Default for \{shape.message_name} with fn default() -> \{shape.message_name} {
      $|  \{shape.message_name}::{
      $|
    ),
  )
  for field in shape.storage_fields {
    content.write_string("    \{field.field_name} : \{field.default_expr},\n")
  }
  for oneof in shape.oneofs {
    content.write_string(
      "    \{oneof.field_name} : \{oneof.enum_name}::NotSet,\n",
    )
  }
  content.write_string(
    (
      #|  }
      #|}
      #|
    ),
  )
}

///|
fn CodeGenerator::gen_message_new(
  _ : CodeGenerator,
  shape : MessageShape,
  content : StringBuilder,
) -> Unit {
  let constructor_arguments = []
  for field in shape.storage_fields {
    constructor_arguments.push(
      "\{field.constructor_name} : \{field.constructor_type}",
    )
  }
  for oneof in shape.oneofs {
    constructor_arguments.push(
      "\{oneof.field_name}?: \{oneof.enum_name} = \{oneof.enum_name}::NotSet",
    )
  }
  let constructor_arguments_string = constructor_arguments.join(", ")
  content.write_string(
    (
      $|#alias(new, deprecated)
      $|pub fn \{shape.message_name}::\{shape.message_name}(\{constructor_arguments_string}) -> \{shape.message_name} {
      $|  \{shape.message_name}::{
      $|
    ),
  )
  for field in shape.storage_fields {
    content.write_string("    \{field.field_name},\n")
  }
  for oneof in shape.oneofs {
    content.write_string("    \{oneof.field_name},\n")
  }
  content.write_string(
    (
      #|  }
      #|}
      #|
    ),
  )
}