use askama::Template;
use super::plan::{CSharpEnum, CSharpModule, CSharpRecord};
#[derive(Template)]
#[template(path = "render_csharp/preamble.txt", escape = "none")]
pub struct PreambleTemplate<'a> {
pub module: &'a CSharpModule,
}
#[derive(Template)]
#[template(path = "render_csharp/functions.txt", escape = "none")]
pub struct FunctionsTemplate<'a> {
pub module: &'a CSharpModule,
}
#[derive(Template)]
#[template(path = "render_csharp/native.txt", escape = "none")]
pub struct NativeTemplate<'a> {
pub module: &'a CSharpModule,
}
#[derive(Template)]
#[template(path = "render_csharp/record.txt", escape = "none")]
pub struct RecordTemplate<'a> {
pub record: &'a CSharpRecord,
pub namespace: &'a str,
}
#[derive(Template)]
#[template(path = "render_csharp/enum_c_style.txt", escape = "none")]
pub struct EnumCStyleTemplate<'a> {
pub enumeration: &'a CSharpEnum,
pub namespace: &'a str,
}
#[derive(Template)]
#[template(path = "render_csharp/enum_data.txt", escape = "none")]
pub struct EnumDataTemplate<'a> {
pub enumeration: &'a CSharpEnum,
pub namespace: &'a str,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::render::csharp::plan::{
CSharpEnum, CSharpEnumKind, CSharpEnumVariant, CSharpMethod, CSharpParam, CSharpParamKind,
CSharpReceiver, CSharpRecord, CSharpRecordField, CSharpReturnKind, CSharpType,
};
fn record_field(
name: &str,
csharp_type: CSharpType,
decode: &str,
size: &str,
encode: &str,
) -> CSharpRecordField {
CSharpRecordField {
name: name.to_string(),
csharp_type,
wire_decode_expr: decode.to_string(),
wire_size_expr: size.to_string(),
wire_encode_expr: encode.to_string(),
}
}
#[test]
fn snapshot_blittable_record_point() {
let record = CSharpRecord {
class_name: "Point".to_string(),
is_blittable: true,
fields: vec![
record_field(
"X",
CSharpType::Double,
"reader.ReadF64()",
"8",
"wire.WriteF64(this.X)",
),
record_field(
"Y",
CSharpType::Double,
"reader.ReadF64()",
"8",
"wire.WriteF64(this.Y)",
),
],
};
let template = RecordTemplate {
record: &record,
namespace: "Demo",
};
insta::assert_snapshot!(template.render().unwrap());
}
#[test]
fn snapshot_non_blittable_record_person_with_string() {
let record = CSharpRecord {
class_name: "Person".to_string(),
is_blittable: false,
fields: vec![
record_field(
"Name",
CSharpType::String,
"reader.ReadString()",
"(4 + Encoding.UTF8.GetByteCount(this.Name))",
"wire.WriteString(this.Name)",
),
record_field(
"Age",
CSharpType::UInt,
"reader.ReadU32()",
"4",
"wire.WriteU32(this.Age)",
),
],
};
let template = RecordTemplate {
record: &record,
namespace: "Demo",
};
insta::assert_snapshot!(template.render().unwrap());
}
#[test]
fn snapshot_nested_record_line() {
let record = CSharpRecord {
class_name: "Line".to_string(),
is_blittable: false,
fields: vec![
record_field(
"Start",
CSharpType::Record("Point".to_string()),
"Point.Decode(reader)",
"16",
"this.Start.WireEncodeTo(wire)",
),
record_field(
"End",
CSharpType::Record("Point".to_string()),
"Point.Decode(reader)",
"16",
"this.End.WireEncodeTo(wire)",
),
],
};
let template = RecordTemplate {
record: &record,
namespace: "Demo",
};
insta::assert_snapshot!(template.render().unwrap());
}
#[test]
fn snapshot_empty_record() {
let record = CSharpRecord {
class_name: "Unit".to_string(),
is_blittable: true,
fields: vec![],
};
let template = RecordTemplate {
record: &record,
namespace: "Demo",
};
insta::assert_snapshot!(template.render().unwrap());
}
#[test]
fn snapshot_blittable_record_with_cstyle_enum_field() {
let record = CSharpRecord {
class_name: "Flag".to_string(),
is_blittable: true,
fields: vec![
record_field(
"Status",
CSharpType::CStyleEnum("Status".to_string()),
"StatusWire.Decode(reader)",
"4",
"this.Status.WireEncodeTo(wire)",
),
record_field(
"Count",
CSharpType::UInt,
"reader.ReadU32()",
"4",
"wire.WriteU32(this.Count)",
),
],
};
let template = RecordTemplate {
record: &record,
namespace: "Demo",
};
insta::assert_snapshot!(template.render().unwrap());
}
fn method(
owner_class_name: &str,
name: &str,
ffi_name: &str,
receiver: CSharpReceiver,
params: Vec<CSharpParam>,
return_type: CSharpType,
return_kind: CSharpReturnKind,
) -> CSharpMethod {
CSharpMethod {
name: name.to_string(),
native_method_name: format!("{owner_class_name}{name}"),
ffi_name: ffi_name.to_string(),
receiver,
params,
return_type,
return_kind,
wire_writers: vec![],
}
}
fn param(name: &str, csharp_type: CSharpType) -> CSharpParam {
CSharpParam {
name: name.to_string(),
csharp_type,
kind: CSharpParamKind::Direct,
}
}
#[test]
fn snapshot_c_style_enum_with_methods_direction() {
let enumeration = CSharpEnum {
class_name: "Direction".to_string(),
kind: CSharpEnumKind::CStyle,
c_style_tag_type: Some(crate::ir::types::PrimitiveType::I32),
variants: vec![
CSharpEnumVariant {
name: "North".to_string(),
tag: 0,
wire_tag: 0,
fields: vec![],
},
CSharpEnumVariant {
name: "South".to_string(),
tag: 1,
wire_tag: 1,
fields: vec![],
},
CSharpEnumVariant {
name: "East".to_string(),
tag: 2,
wire_tag: 2,
fields: vec![],
},
CSharpEnumVariant {
name: "West".to_string(),
tag: 3,
wire_tag: 3,
fields: vec![],
},
],
methods: vec![
method(
"Direction",
"FromDegrees",
"boltffi_direction_from_degrees",
CSharpReceiver::Static,
vec![param("degrees", CSharpType::Double)],
CSharpType::CStyleEnum("Direction".to_string()),
CSharpReturnKind::Direct,
),
method(
"Direction",
"Count",
"boltffi_direction_count",
CSharpReceiver::Static,
vec![],
CSharpType::UInt,
CSharpReturnKind::Direct,
),
method(
"Direction",
"Opposite",
"boltffi_direction_opposite",
CSharpReceiver::InstanceExtension,
vec![],
CSharpType::CStyleEnum("Direction".to_string()),
CSharpReturnKind::Direct,
),
method(
"Direction",
"IsHorizontal",
"boltffi_direction_is_horizontal",
CSharpReceiver::InstanceExtension,
vec![],
CSharpType::Bool,
CSharpReturnKind::Direct,
),
method(
"Direction",
"Label",
"boltffi_direction_label",
CSharpReceiver::InstanceExtension,
vec![],
CSharpType::String,
CSharpReturnKind::WireDecodeString,
),
],
};
let template = EnumCStyleTemplate {
enumeration: &enumeration,
namespace: "Demo",
};
insta::assert_snapshot!(template.render().unwrap());
}
#[test]
fn snapshot_c_style_enum_status() {
let enumeration = CSharpEnum {
class_name: "Status".to_string(),
kind: CSharpEnumKind::CStyle,
c_style_tag_type: Some(crate::ir::types::PrimitiveType::I32),
variants: vec![
CSharpEnumVariant {
name: "Active".to_string(),
tag: 0,
wire_tag: 0,
fields: vec![],
},
CSharpEnumVariant {
name: "Inactive".to_string(),
tag: 1,
wire_tag: 1,
fields: vec![],
},
CSharpEnumVariant {
name: "Pending".to_string(),
tag: 2,
wire_tag: 2,
fields: vec![],
},
],
methods: vec![],
};
let template = EnumCStyleTemplate {
enumeration: &enumeration,
namespace: "Demo",
};
insta::assert_snapshot!(template.render().unwrap());
}
#[test]
fn snapshot_c_style_enum_log_level_u8() {
let enumeration = CSharpEnum {
class_name: "LogLevel".to_string(),
kind: CSharpEnumKind::CStyle,
c_style_tag_type: Some(crate::ir::types::PrimitiveType::U8),
variants: vec![
CSharpEnumVariant {
name: "Trace".to_string(),
tag: 0,
wire_tag: 0,
fields: vec![],
},
CSharpEnumVariant {
name: "Debug".to_string(),
tag: 1,
wire_tag: 1,
fields: vec![],
},
CSharpEnumVariant {
name: "Info".to_string(),
tag: 2,
wire_tag: 2,
fields: vec![],
},
CSharpEnumVariant {
name: "Warn".to_string(),
tag: 3,
wire_tag: 3,
fields: vec![],
},
CSharpEnumVariant {
name: "Error".to_string(),
tag: 4,
wire_tag: 4,
fields: vec![],
},
],
methods: vec![],
};
let template = EnumCStyleTemplate {
enumeration: &enumeration,
namespace: "Demo",
};
insta::assert_snapshot!(template.render().unwrap());
}
#[test]
fn snapshot_data_enum_shape() {
let enumeration = CSharpEnum {
class_name: "Shape".to_string(),
kind: CSharpEnumKind::Data,
c_style_tag_type: None,
variants: vec![
CSharpEnumVariant {
name: "Circle".to_string(),
tag: 0,
wire_tag: 0,
fields: vec![record_field(
"Radius",
CSharpType::Double,
"reader.ReadF64()",
"8",
"wire.WriteF64(_v.Radius)",
)],
},
CSharpEnumVariant {
name: "Rectangle".to_string(),
tag: 1,
wire_tag: 1,
fields: vec![
record_field(
"Width",
CSharpType::Double,
"reader.ReadF64()",
"8",
"wire.WriteF64(_v.Width)",
),
record_field(
"Height",
CSharpType::Double,
"reader.ReadF64()",
"8",
"wire.WriteF64(_v.Height)",
),
],
},
CSharpEnumVariant {
name: "Point".to_string(),
tag: 2,
wire_tag: 2,
fields: vec![],
},
],
methods: vec![],
};
let template = EnumDataTemplate {
enumeration: &enumeration,
namespace: "Demo",
};
insta::assert_snapshot!(template.render().unwrap());
}
#[test]
fn snapshot_data_enum_with_methods_shape() {
let enumeration = CSharpEnum {
class_name: "Shape".to_string(),
kind: CSharpEnumKind::Data,
c_style_tag_type: None,
variants: vec![CSharpEnumVariant {
name: "Circle".to_string(),
tag: 0,
wire_tag: 0,
fields: vec![record_field(
"Radius",
CSharpType::Double,
"reader.ReadF64()",
"8",
"wire.WriteF64(_v.Radius)",
)],
}],
methods: vec![
method(
"Shape",
"UnitCircle",
"boltffi_shape_unit_circle",
CSharpReceiver::Static,
vec![],
CSharpType::DataEnum("Shape".to_string()),
CSharpReturnKind::WireDecodeObject {
class_name: "Shape".to_string(),
},
),
method(
"Shape",
"VariantCount",
"boltffi_shape_variant_count",
CSharpReceiver::Static,
vec![],
CSharpType::UInt,
CSharpReturnKind::Direct,
),
method(
"Shape",
"Area",
"boltffi_shape_area",
CSharpReceiver::InstanceNative,
vec![],
CSharpType::Double,
CSharpReturnKind::Direct,
),
method(
"Shape",
"Describe",
"boltffi_shape_describe",
CSharpReceiver::InstanceNative,
vec![],
CSharpType::String,
CSharpReturnKind::WireDecodeString,
),
],
};
let template = EnumDataTemplate {
enumeration: &enumeration,
namespace: "Demo",
};
insta::assert_snapshot!(template.render().unwrap());
}
}