use crate::Interop;
use crate::converter::{field_name, field_to_type, wire_suffix};
use interoptopus::lang::{Composite, Enum, Type, VariantKind, Visibility, WirePayload};
use interoptopus_backend_utils::{Error, IndentWriter, render};
pub fn write_wire_helpers(_i: &Interop, w: &mut IndentWriter) -> Result<(), Error> {
render!(w, "wire/helpers.cs")
}
pub fn write_type_definitions_wired(_i: &Interop, w: &mut IndentWriter, the_type: &Composite) -> Result<(), Error> {
let type_name = wire_suffix(the_type);
render!(w, "wire/wire_of.cs", ("type", type_name))
}
pub fn write_type_definition_wired_enum(i: &Interop, w: &mut IndentWriter, the_type: &Enum) -> Result<(), Error> {
#[derive(serde::Serialize)]
struct VariantDesc {
tag: String,
value: usize,
}
i.debug(w, "write_type_definition_wired_enum")?;
let name = the_type.rust_name();
let variants = the_type
.variants()
.iter()
.map(|variant| VariantDesc {
tag: variant.name().to_string(),
value: match variant.kind() {
VariantKind::Unit(x) => *x,
VariantKind::Typed(x, _) => *x,
},
})
.collect::<Vec<_>>();
render!(w, "wire/enum.cs", ("name", name), ("variants", &variants))
}
pub fn write_type_definitions_domain_wired(i: &Interop, w: &mut IndentWriter, wired: &Composite) -> Result<(), Error> {
#[derive(serde::Serialize)]
struct FieldDesc {
visibility: String,
docs: String,
type_name: String,
name: String,
}
let type_name = wired.rust_name();
let visibility = i.visibility_types.to_access_modifier();
let self_kind = "partial class"; let docs = wired.meta().docs().lines();
let serialization_code = generate_serialization_code(w, wired)?;
let deserialization_code = generate_deserialization_code(w, wired)?;
let size_calculation = generate_size_calculation(w, wired)?;
let fields = wired
.fields()
.iter()
.map(|field| {
let visibility = String::from(match field.visibility() {
Visibility::Public => "public ",
Visibility::Private => "",
});
let docs = field.docs().lines().join(" ");
let type_name = field_to_type(field.the_type());
let name = field_name(field);
FieldDesc { visibility, docs, type_name, name }
})
.collect::<Vec<FieldDesc>>();
render!(
w,
"wire/payload_type.cs",
("type", type_name),
("visibility", visibility),
("self_kind", self_kind),
("docs", &docs),
("fields", &fields),
("serialization_code", &serialization_code),
("deserialization_code", &deserialization_code),
("size_calculation", &size_calculation)
)
}
#[derive(serde::Serialize, Default, Copy, Clone)]
#[serde(rename_all = "snake_case")]
enum Kind {
String,
Vec,
Map,
Optional,
Enum,
Composite,
#[default]
Primitive,
}
#[allow(clippy::fallible_impl_from, reason = "It's not user input")]
impl From<&Type> for Kind {
fn from(value: &Type) -> Self {
match value {
Type::Primitive(_) => Self::Primitive,
Type::Enum(_) => Self::Enum,
Type::WirePayload(WirePayload::String) => Self::String,
Type::WirePayload(WirePayload::Vec(_)) => Self::Vec,
Type::WirePayload(WirePayload::Map(_, _)) => Self::Map,
Type::WirePayload(WirePayload::Enum(_)) => Self::Enum,
Type::WirePayload(WirePayload::Option(_)) => Self::Optional,
Type::WirePayload(WirePayload::Composite(_)) => Self::Composite,
_ => panic!("Unsupported wire payload type kind {value:?}"),
}
}
}
impl From<&Box<Type>> for Kind {
fn from(value: &Box<Type>) -> Self {
Self::from(&**value)
}
}
#[derive(serde::Serialize, Default)]
struct FieldDesc {
kind: Kind,
name: String,
inner_kind: Kind,
inner_type: String,
primitive_size: usize, }
fn generate_serialization_code(w: &mut IndentWriter, composite: &Composite) -> Result<String, Error> {
let mut buf = Vec::new();
let mut writer = IndentWriter::with_same_indent_as(w, &mut buf);
writer.indent();
let fields = composite
.fields()
.iter()
.map(|field| {
let field_name = field.name();
let field_type = field.the_type();
let (inner_kind, inner_type) = extract_inner_type(field_type);
FieldDesc { kind: field_type.into(), name: field_name.to_string(), inner_kind, inner_type, ..Default::default() }
})
.collect::<Vec<_>>();
render!(writer, "wire/serializer.cs", ("fields", &fields))?;
Ok(String::from_utf8(buf)?)
}
fn generate_deserialization_code(w: &mut IndentWriter, composite: &Composite) -> Result<String, Error> {
let mut buf = Vec::new();
let mut writer = IndentWriter::with_same_indent_as(w, &mut buf);
writer.indent();
let type_name = composite.rust_name();
let fields = composite
.fields()
.iter()
.map(|field| {
let name = field.name().to_string();
let field_type = field.the_type();
let csharp_type = field_to_type(field_type);
let kind: Kind = field_type.into();
let (inner_kind, inner_type) = match kind {
Kind::Vec => extract_inner_type(field_type),
Kind::Optional => extract_inner_type(field_type),
Kind::Primitive => (Kind::Primitive, csharp_type), Kind::Map => extract_inner_type(field_type),
_ => (Kind::Primitive, csharp_type), };
FieldDesc { kind, name, inner_kind, inner_type, ..Default::default() }
})
.collect::<Vec<_>>();
render!(writer, "wire/deserializer.cs", ("type", &type_name), ("fields", &fields))?;
Ok(String::from_utf8(buf)?)
}
fn generate_size_calculation(w: &mut IndentWriter, composite: &Composite) -> Result<String, Error> {
let mut buf = Vec::new();
let mut writer = IndentWriter::with_same_indent_as(w, &mut buf);
writer.indent();
let fields = composite
.fields()
.iter()
.map(|field| {
let field_name = field.name();
let field_type = field.the_type();
let csharp_type = field_to_type(field_type);
let (inner_kind, inner_type) = extract_inner_type(field_type);
FieldDesc {
kind: field_type.into(),
name: field_name.to_string(),
inner_kind,
inner_type,
primitive_size: if matches!(field_type, Type::Primitive(_)) {
get_primitive_size(&csharp_type)
} else {
0
},
}
})
.collect::<Vec<_>>();
render!(writer, "wire/calculate_size_body.cs", ("fields", &fields))?;
Ok(String::from_utf8(buf)?)
}
fn get_primitive_size(csharp_type: &str) -> usize {
match csharp_type {
"bool" | "sbyte" | "byte" => 1,
"short" | "ushort" => 2,
"int" | "uint" | "float" => 4,
"long" | "ulong" | "double" => 8,
_ => 1,
}
}
fn extract_inner_type(a_type: &Type) -> (Kind, String) {
match a_type {
Type::WirePayload(dom) => match dom {
WirePayload::Vec(t) => (t.into(), field_to_type(t)),
WirePayload::Option(o) => (o.into(), field_to_type(o)),
WirePayload::Map(k, v) => (Kind::Map, format!("{}, {}", field_to_type(k), field_to_type(v))), WirePayload::Composite(_c) => (Kind::Composite, "?ask-me-how-we-got-here?".into()),
WirePayload::String => (Kind::String, String::new()),
WirePayload::Enum(_e) => (Kind::Enum, "!ask-me-how-we-got-here!".into()),
},
_ => (Kind::Primitive, "object".to_string()), }
}