use {
crate::{
identifier::Identifier,
schema::{self, relativize_namespace},
},
std::{
collections::BTreeMap,
fmt::{self, Write},
path::PathBuf,
},
};
const INDENTATION: &str = " ";
const TRAITS_TO_DERIVE: &[&str] = &["Clone", "Debug"];
const RUST_KEYWORDS: &[&str] = &[
"Self", "abstract", "as", "async", "await", "become", "box", "break", "const", "continue",
"crate", "do", "dyn", "else", "enum", "extern", "false", "final", "fn", "for", "if", "impl",
"in", "let", "loop", "macro", "match", "mod", "move", "mut", "override", "priv", "pub", "ref",
"return", "self", "static", "struct", "super", "trait", "true", "try", "type", "typeof",
"unsafe", "unsized", "use", "virtual", "where", "while", "yield",
];
#[derive(Clone, Debug)]
struct Module {
children: BTreeMap<Identifier, Module>,
schema: schema::Schema,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum CaseConvention {
Pascal,
Snake,
}
use CaseConvention::{Pascal, Snake};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Direction {
Atlas,
In,
Out,
}
use Direction::{Atlas, In, Out};
#[allow(clippy::too_many_lines)]
pub fn generate(
typical_version: &str,
schemas: &BTreeMap<schema::Namespace, (schema::Schema, PathBuf, String)>,
) -> String {
let mut tree = Module {
children: BTreeMap::new(),
schema: schema::Schema {
comment: vec![],
imports: BTreeMap::new(),
declarations: vec![],
},
};
for (namespace, (schema, _, _)) in schemas {
insert_schema(&mut tree, namespace, schema);
}
let mut buffer = String::new();
if !tree.children.is_empty() || !tree.schema.declarations.is_empty() {
writeln!(
&mut buffer,
"\
// This file was automatically generated by Typical {}.
// Visit https://github.com/stepchowfun/typical for more information.
use std::{{
cmp::min,
io::{{self, BufRead, Error, ErrorKind, Write}},
mem::transmute,
}};
const MISSING_FIELDS_ERROR_MESSAGE: &str = \"Struct missing one or more required field(s).\";
pub trait Serialize {{
fn size(&self) -> usize;
fn serialize<T: Write>(&self, writer: T) -> io::Result<()>;
}}
pub trait Deserialize: Sized {{
fn deserialize<T: BufRead>(reader: T) -> io::Result<Self>;
}}
fn zigzag_encode(value: i64) -> u64 {{
unsafe {{ transmute::<i64, u64>(value >> 63) ^ transmute::<i64, u64>(value << 1) }}
}}
fn zigzag_decode(value: u64) -> i64 {{
unsafe {{ transmute::<u64, i64>(value >> 1) ^ -transmute::<u64, i64>(value & 1) }}
}}
fn varint_size_from_value(value: u64) -> usize {{
match value {{
0_u64..=127_u64 => 1,
128_u64..=16_511_u64 => 2,
16_512_u64..=2_113_663_u64 => 3,
2_113_664_u64..=270_549_119_u64 => 4,
270_549_120_u64..=34_630_287_487_u64 => 5,
34_630_287_488_u64..=4_432_676_798_591_u64 => 6,
4_432_676_798_592_u64..=567_382_630_219_903_u64 => 7,
567_382_630_219_904_u64..=72_624_976_668_147_839_u64 => 8,
72_624_976_668_147_840_u64..=18_446_744_073_709_551_615_u64 => 9,
}}
}}
fn varint_size_from_first_byte(first_byte: u8) -> u32 {{
first_byte.trailing_zeros() + 1
}}
fn serialize_varint<T: Write>(mut value: u64, writer: &mut T) -> io::Result<()> {{
match value {{
0_u64..=127_u64 => writer.write_all(&[((value << 1) as u8) | 0b0000_0001]),
128_u64..=16_511_u64 => {{
value -= 128_u64;
writer.write_all(&[((value << 2) as u8) | 0b0000_0010, (value >> 6) as u8])
}}
16_512_u64..=2_113_663_u64 => {{
value -= 16_512_u64;
writer.write_all(&[
((value << 3) as u8) | 0b0000_0100,
(value >> 5) as u8,
(value >> 13) as u8,
])
}}
2_113_664_u64..=270_549_119_u64 => {{
value -= 2_113_664_u64;
writer.write_all(&[
((value << 4) as u8) | 0b0000_1000,
(value >> 4) as u8,
(value >> 12) as u8,
(value >> 20) as u8,
])
}}
270_549_120_u64..=34_630_287_487_u64 => {{
value -= 270_549_120_u64;
writer.write_all(&[
((value << 5) as u8) | 0b0001_0000,
(value >> 3) as u8,
(value >> 11) as u8,
(value >> 19) as u8,
(value >> 27) as u8,
])
}}
34_630_287_488_u64..=4_432_676_798_591_u64 => {{
value -= 34_630_287_488_u64;
writer.write_all(&[
((value << 6) as u8) | 0b0010_0000,
(value >> 2) as u8,
(value >> 10) as u8,
(value >> 18) as u8,
(value >> 26) as u8,
(value >> 34) as u8,
])
}}
4_432_676_798_592_u64..=567_382_630_219_903_u64 => {{
value -= 4_432_676_798_592_u64;
writer.write_all(&[
((value << 7) as u8) | 0b0100_0000,
(value >> 1) as u8,
(value >> 9) as u8,
(value >> 17) as u8,
(value >> 25) as u8,
(value >> 33) as u8,
(value >> 41) as u8,
])
}}
567_382_630_219_904_u64..=72_624_976_668_147_839_u64 => {{
value -= 567_382_630_219_904_u64;
writer.write_all(&[
0b1000_0000,
value as u8,
(value >> 8) as u8,
(value >> 16) as u8,
(value >> 24) as u8,
(value >> 32) as u8,
(value >> 40) as u8,
(value >> 48) as u8,
])
}}
72_624_976_668_147_840_u64..=18_446_744_073_709_551_615_u64 => {{
value -= 72_624_976_668_147_840_u64;
writer.write_all(&[
0b0000_0000,
value as u8,
(value >> 8) as u8,
(value >> 16) as u8,
(value >> 24) as u8,
(value >> 32) as u8,
(value >> 40) as u8,
(value >> 48) as u8,
(value >> 56) as u8,
])
}}
}}
}}
fn deserialize_varint<T: BufRead>(reader: &mut T) -> io::Result<u64> {{
let mut first_byte_buffer = [0; 1];
reader.read_exact(&mut first_byte_buffer[..])?;
let first_byte = first_byte_buffer[0];
let size_minus_one = first_byte.trailing_zeros();
let mut remaining_bytes_buffer = [0; 8];
reader.read_exact(&mut remaining_bytes_buffer[0..size_minus_one as usize])?;
let remaining_bytes_value = u64::from_le_bytes(remaining_bytes_buffer);
match size_minus_one {{
0 => Ok(u64::from(first_byte >> 1)),
1 => Ok(128_u64 + u64::from(first_byte >> 2) + (remaining_bytes_value << 6)),
2 => Ok(16_512_u64 + u64::from(first_byte >> 3) + (remaining_bytes_value << 5)),
3 => Ok(2_113_664_u64 + u64::from(first_byte >> 4) + (remaining_bytes_value << 4)),
4 => Ok(270_549_120_u64 + u64::from(first_byte >> 5) + (remaining_bytes_value << 3)),
5 => Ok(34_630_287_488_u64 + u64::from(first_byte >> 6) + (remaining_bytes_value << 2)),
6 => Ok(4_432_676_798_592_u64 + u64::from(first_byte >> 7) + (remaining_bytes_value << 1)),
7 => Ok(567_382_630_219_904_u64 + remaining_bytes_value),
_ => Ok(72_624_976_668_147_840_u64.wrapping_add(remaining_bytes_value)),
}}
}}
fn field_header_size(index: u64, payload_size: usize, integer_encoded: bool) -> usize {{
match payload_size {{
0 => varint_size_from_value((index << 2) | 0b00),
8 => varint_size_from_value((index << 2) | 0b01),
size => {{
if integer_encoded {{
varint_size_from_value((index << 2) | 0b10)
}} else {{
varint_size_from_value((index << 2) | 0b11) + varint_size_from_value(size as u64)
}}
}}
}}
}}
fn serialize_field_header<T: Write>(
writer: &mut T,
index: u64,
payload_size: usize,
integer_encoded: bool,
) -> io::Result<()> {{
match payload_size {{
0 => serialize_varint((index << 2) | 0b00, writer),
8 => serialize_varint((index << 2) | 0b01, writer),
size => {{
if integer_encoded {{
serialize_varint((index << 2) | 0b10, writer)
}} else {{
serialize_varint((index << 2) | 0b11, writer)?;
serialize_varint(size as u64, writer)
}}
}}
}}
}}
fn deserialize_field_header<T: BufRead>(reader: &mut T) -> io::Result<(u64, usize)> {{
let tag = deserialize_varint(&mut *reader)?;
let index = tag >> 2;
let size = match tag & 0b11 {{
0b00 => 0,
0b01 => 8,
0b10 => {{
let buffer = (&mut *reader).fill_buf()?;
if buffer.is_empty() {{
return Err(Error::new(
ErrorKind::UnexpectedEof,
\"Error decoding field.\",
));
}}
varint_size_from_first_byte(buffer[0]) as usize
}}
_ => deserialize_varint(&mut *reader)? as usize,
}};
Ok((index, size))
}}
fn skip<T: BufRead>(reader: &mut T, mut amount: usize) -> io::Result<()> {{
while amount > 0 {{
let buffer = reader.fill_buf()?;
let num_bytes_to_consume = min(buffer.len(), amount);
reader.consume(num_bytes_to_consume);
amount -= num_bytes_to_consume;
}}
Ok(())
}}
fn finish<T: BufRead>(reader: &mut T) -> io::Result<()> {{
loop {{
let buffer = reader.fill_buf()?;
if buffer.is_empty() {{
return Ok(());
}}
let buffer_size = buffer.len();
reader.consume(buffer_size);
}}
}}",
typical_version,
)
.unwrap();
writeln!(&mut buffer).unwrap();
write_module_contents(
&mut buffer,
0,
&schema::Namespace { components: vec![] },
&tree.children,
&tree.schema,
)
.unwrap();
}
buffer
}
fn insert_schema(module: &mut Module, namespace: &schema::Namespace, schema: &schema::Schema) {
let mut iter = namespace.components.iter();
if let Some(head) = iter.next() {
if let Some(child) = module.children.get_mut(head) {
insert_schema(
child,
&schema::Namespace {
components: iter.cloned().collect(),
},
schema,
);
} else {
let mut child = Module {
children: BTreeMap::new(),
schema: schema::Schema {
comment: vec![],
imports: BTreeMap::new(),
declarations: vec![],
},
};
insert_schema(
&mut child,
&schema::Namespace {
components: iter.cloned().collect(),
},
schema,
);
module.children.insert(head.clone(), child);
}
} else {
module.schema = schema.clone();
}
}
fn write_module<T: Write>(
buffer: &mut T,
indentation: usize,
namespace: &schema::Namespace,
name: &Identifier,
module: &Module,
) -> Result<(), fmt::Error> {
write_indentation(buffer, indentation)?;
write!(buffer, "pub mod ")?;
write_identifier(buffer, name, Snake, None)?;
writeln!(buffer, " {{")?;
let mut new_namespace = namespace.clone();
new_namespace.components.push(name.clone());
write_module_contents(
buffer,
indentation + 1,
&new_namespace,
&module.children,
&module.schema,
)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
Ok(())
}
fn write_module_contents<T: Write>(
buffer: &mut T,
indentation: usize,
namespace: &schema::Namespace,
children: &BTreeMap<Identifier, Module>,
schema: &schema::Schema,
) -> Result<(), fmt::Error> {
let schema_empty = schema.declarations.is_empty();
for (i, (child_name, child)) in children.iter().enumerate() {
write_module(buffer, indentation, namespace, child_name, child)?;
if i < children.len() - 1 || !schema_empty {
writeln!(buffer)?;
}
}
write_schema(buffer, indentation, namespace, schema)?;
Ok(())
}
#[allow(clippy::too_many_lines)]
fn write_schema<T: Write>(
buffer: &mut T,
indentation: usize,
namespace: &schema::Namespace,
schema: &schema::Schema,
) -> Result<(), fmt::Error> {
let mut imports = BTreeMap::new();
for (name, import) in &schema.imports {
imports.insert(name.clone(), import.namespace.clone().unwrap());
}
let mut iter = schema.declarations.iter().peekable();
while let Some(declaration) = iter.next() {
match &declaration.variant {
schema::DeclarationVariant::Struct => {
write_struct(
buffer,
indentation,
&imports,
namespace,
&declaration.name,
&declaration.fields,
Atlas,
)?;
writeln!(buffer)?;
write_struct(
buffer,
indentation,
&imports,
namespace,
&declaration.name,
&declaration.fields,
Out,
)?;
writeln!(buffer)?;
write_struct(
buffer,
indentation,
&imports,
namespace,
&declaration.name,
&declaration.fields,
In,
)?;
writeln!(buffer)?;
write_indentation(buffer, indentation)?;
write!(buffer, "impl ")?;
write_supers(buffer, indentation)?;
write!(buffer, "Serialize for ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Out))?;
writeln!(buffer, " {{")?;
write_size_function(buffer, indentation + 1)?;
writeln!(buffer)?;
write_serialize_function(buffer, indentation + 1)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
write_indentation(buffer, indentation)?;
write!(buffer, "impl ")?;
write_supers(buffer, indentation)?;
write!(buffer, "Deserialize for ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(In))?;
writeln!(buffer, " {{")?;
write_deserialize_function(buffer, indentation + 1)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
write_indentation(buffer, indentation)?;
write!(buffer, "impl From<")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Out))?;
write!(buffer, "> for ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(In))?;
writeln!(buffer, " {{")?;
write_indentation(buffer, indentation + 1)?;
write!(buffer, "fn from(message: ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Out))?;
writeln!(buffer, ") -> Self {{")?;
write_indentation(buffer, indentation + 2)?;
write_identifier(buffer, &declaration.name, Pascal, Some(In))?;
writeln!(buffer, " {{")?;
for field in &declaration.fields {
match field.rule {
schema::Rule::Asymmetric => {
write_indentation(buffer, indentation + 3)?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(buffer, ": Some(message.")?;
write_identifier(buffer, &field.name, Snake, None)?;
}
schema::Rule::Optional => {
write_indentation(buffer, indentation + 3)?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(buffer, ": message.")?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(buffer, ".map(|payload| payload")?;
}
schema::Rule::Required => {
write_indentation(buffer, indentation + 3)?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(buffer, ": message.")?;
write_identifier(buffer, &field.name, Snake, None)?;
}
}
write_into_invocation(buffer, &field.r#type.variant)?;
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Optional => {
writeln!(buffer, "),")?;
}
schema::Rule::Required => {
writeln!(buffer, ",")?;
}
}
}
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
write_indentation(buffer, indentation)?;
write!(buffer, "impl ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Out))?;
writeln!(buffer, " {{")?;
write_indentation(buffer, indentation + 1)?;
write!(buffer, "pub fn atlas(&self) -> ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Atlas))?;
writeln!(buffer, " {{")?;
if !&declaration.fields.is_empty() {
for field in &declaration.fields {
write_indentation(buffer, indentation + 2)?;
write!(buffer, "let _")?;
write_identifier(buffer, &field.name, Snake, None)?;
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Required => {
write!(buffer, " = {{ let payload = &self.")?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(buffer, "; ")?;
write_atlas_calculation(
buffer,
indentation,
&field.r#type.variant,
true,
)?;
writeln!(buffer, " }};")?;
}
schema::Rule::Optional => {
write!(buffer, " = self.")?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(buffer, ".as_ref().map(|payload| ")?;
write_atlas_calculation(
buffer,
indentation,
&field.r#type.variant,
true,
)?;
writeln!(buffer, ");")?;
}
}
}
writeln!(buffer)?;
}
write_indentation(buffer, indentation + 2)?;
write_identifier(buffer, &declaration.name, Pascal, Some(Atlas))?;
writeln!(buffer, " {{")?;
write_indentation(buffer, indentation + 3)?;
write!(buffer, "_size:")?;
if declaration.fields.is_empty() {
write!(buffer, " 0")?;
}
let mut first = true;
for field in &declaration.fields {
writeln!(buffer)?;
write_indentation(buffer, indentation + 4)?;
if first {
first = false;
} else {
write!(buffer, "+ ")?;
}
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Required => {
write!(buffer, "{{ let payload_atlas = &_")?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(buffer, "; let payload_size = ")?;
}
schema::Rule::Optional => {
write!(buffer, "_")?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(
buffer,
".as_ref().map_or(0_usize, |payload_atlas| {{ let payload_size = ",
)?;
}
}
write_atlas_lookup(buffer, &field.r#type.variant)?;
write!(buffer, "; ")?;
write_supers(buffer, indentation)?;
write!(
buffer,
"field_header_size({}_u64, payload_size, {}) + payload_size }}",
field.index,
integer_encoded(&field.r#type),
)?;
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Required => {}
schema::Rule::Optional => {
write!(buffer, ")")?;
}
}
}
writeln!(buffer, ",")?;
for field in &declaration.fields {
write_indentation(buffer, indentation + 3)?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(buffer, ": _")?;
write_identifier(buffer, &field.name, Snake, None)?;
writeln!(buffer, ",")?;
}
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "pub fn serialize_with_atlas<T: ::std::io::Write>(")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "&self,")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "writer: &mut T,")?;
write_indentation(buffer, indentation + 2)?;
write!(buffer, "atlas: &")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Atlas))?;
writeln!(buffer, ",")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, ") -> ::std::io::Result<()> {{")?;
for field in &declaration.fields {
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Required => {
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "{{")?;
write_indentation(buffer, indentation + 3)?;
write!(buffer, "let payload = &self.")?;
write_identifier(buffer, &field.name, Snake, None)?;
writeln!(buffer, ";")?;
write_indentation(buffer, indentation + 3)?;
write!(buffer, "let payload_atlas = &atlas.")?;
write_identifier(buffer, &field.name, Snake, None)?;
writeln!(buffer, ";")?;
}
schema::Rule::Optional => {
write_indentation(buffer, indentation + 2)?;
write!(
buffer,
"if let (Some(payload), Some(payload_atlas)) = (&self.",
)?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(buffer, ", &atlas.")?;
write_identifier(buffer, &field.name, Snake, None)?;
writeln!(buffer, ") {{")?;
}
}
write_indentation(buffer, indentation + 3)?;
write_supers(buffer, indentation)?;
write!(
buffer,
"serialize_field_header(writer, {}_u64, ",
field.index,
)?;
write_atlas_lookup(buffer, &field.r#type.variant)?;
writeln!(buffer, ", {})?;", integer_encoded(&field.r#type))?;
write_serialization_invocation(
buffer,
indentation + 3,
indentation,
&field.r#type.variant,
true,
)?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
}
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "Ok(())")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
write_indentation(buffer, indentation)?;
write!(buffer, "impl ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(In))?;
writeln!(buffer, " {{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(
buffer,
"pub fn deserialize_from_reader_ref<T: ::std::io::BufRead>(",
)?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "reader: &mut T,")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, ") -> ::std::io::Result<Self> {{")?;
if !&declaration.fields.is_empty() {
for field in &declaration.fields {
write_indentation(buffer, indentation + 2)?;
write!(buffer, "let mut _")?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(buffer, ": Option<")?;
write_type(buffer, &imports, namespace, &field.r#type.variant, In)?;
writeln!(buffer, "> = None;")?;
}
writeln!(buffer)?;
}
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "loop {{")?;
write_indentation(buffer, indentation + 3)?;
write!(buffer, "let (index, payload_size) = match ")?;
write_supers(buffer, indentation)?;
writeln!(buffer, "deserialize_field_header(&mut *reader) {{")?;
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "Ok(header) => header,")?;
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "Err(err) => {{")?;
write_indentation(buffer, indentation + 5)?;
writeln!(
buffer,
"if let std::io::ErrorKind::UnexpectedEof = err.kind() {{",
)?;
write_indentation(buffer, indentation + 6)?;
writeln!(buffer, "break;")?;
write_indentation(buffer, indentation + 5)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
write_indentation(buffer, indentation + 5)?;
writeln!(buffer, "return Err(err);")?;
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "}};")?;
writeln!(buffer)?;
write_indentation(buffer, indentation + 3)?;
writeln!(
buffer,
"let mut sub_reader = ::std::io::Read::take(&mut *reader, \
payload_size as u64);",
)?;
writeln!(buffer)?;
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "match index {{")?;
for field in &declaration.fields {
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "{} => {{", field.index)?;
write_deserialization_invocation(
buffer,
indentation + 5,
indentation,
&imports,
namespace,
&field.r#type.variant,
true,
)?;
write_indentation(buffer, indentation + 5)?;
write!(buffer, "_")?;
write_identifier(buffer, &field.name, Snake, None)?;
writeln!(buffer, ".get_or_insert(payload);")?;
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "}}")?;
}
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "_ => {{")?;
write_indentation(buffer, indentation + 5)?;
write_supers(buffer, indentation)?;
writeln!(buffer, "skip(&mut sub_reader, payload_size as usize)?;")?;
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
if declaration.fields.iter().any(|field| match field.rule {
schema::Rule::Asymmetric | schema::Rule::Optional => false,
schema::Rule::Required => true,
}) {
write_indentation(buffer, indentation + 2)?;
write!(buffer, "if ")?;
first = true;
for field in &declaration.fields {
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Optional => {}
schema::Rule::Required => {
if first {
first = false;
} else {
writeln!(buffer)?;
write_indentation(buffer, indentation + 3)?;
write!(buffer, "|| ")?;
}
write!(buffer, "_")?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(buffer, ".is_none()")?;
}
}
}
writeln!(buffer, " {{")?;
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "return Err(::std::io::Error::new(")?;
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "::std::io::ErrorKind::InvalidData,")?;
write_indentation(buffer, indentation + 4)?;
write_supers(buffer, indentation)?;
writeln!(buffer, "MISSING_FIELDS_ERROR_MESSAGE,")?;
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "));")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
}
write_indentation(buffer, indentation + 2)?;
write!(buffer, "Ok(")?;
write_identifier(buffer, &declaration.name, Pascal, Some(In))?;
writeln!(buffer, " {{")?;
for field in &declaration.fields {
write_indentation(buffer, indentation + 3)?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(buffer, ": _")?;
write_identifier(buffer, &field.name, Snake, None)?;
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Optional => {}
schema::Rule::Required => {
write!(buffer, ".unwrap()")?;
}
}
writeln!(buffer, ",")?;
}
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}})")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
write_indentation(buffer, indentation)?;
write!(buffer, "impl ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Atlas))?;
writeln!(buffer, " {{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "pub fn size(&self) -> usize {{")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "self._size")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
}
schema::DeclarationVariant::Choice => {
write_choice(
buffer,
indentation,
&imports,
namespace,
&declaration.name,
&declaration.fields,
Atlas,
)?;
writeln!(buffer)?;
write_choice(
buffer,
indentation,
&imports,
namespace,
&declaration.name,
&declaration.fields,
Out,
)?;
writeln!(buffer)?;
write_choice(
buffer,
indentation,
&imports,
namespace,
&declaration.name,
&declaration.fields,
In,
)?;
writeln!(buffer)?;
write_indentation(buffer, indentation)?;
write!(buffer, "impl ")?;
write_supers(buffer, indentation)?;
write!(buffer, "Serialize for ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Out))?;
writeln!(buffer, " {{")?;
write_size_function(buffer, indentation + 1)?;
writeln!(buffer)?;
write_serialize_function(buffer, indentation + 1)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
write_indentation(buffer, indentation)?;
write!(buffer, "impl ")?;
write_supers(buffer, indentation)?;
write!(buffer, "Deserialize for ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(In))?;
writeln!(buffer, " {{")?;
write_deserialize_function(buffer, indentation + 1)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
write_indentation(buffer, indentation)?;
write!(buffer, "impl From<")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Out))?;
write!(buffer, "> for ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(In))?;
writeln!(buffer, " {{")?;
write_indentation(buffer, indentation + 1)?;
write!(buffer, "fn from(message: ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Out))?;
writeln!(buffer, ") -> Self {{")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "match message {{")?;
for field in &declaration.fields {
write_indentation(buffer, indentation + 3)?;
write_identifier(buffer, &declaration.name, Pascal, Some(Out))?;
write!(buffer, "::")?;
write_identifier(buffer, &field.name, Pascal, None)?;
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Optional => {
if matches!(field.r#type.variant, schema::TypeVariant::Unit) {
write!(buffer, "(fallback) => ")?;
} else {
write!(buffer, "(payload, fallback) => ")?;
}
}
schema::Rule::Required => {
if matches!(field.r#type.variant, schema::TypeVariant::Unit) {
write!(buffer, " => ")?;
} else {
write!(buffer, "(payload) => ")?;
}
}
}
write_identifier(buffer, &declaration.name, Pascal, Some(In))?;
write!(buffer, "::")?;
write_identifier(buffer, &field.name, Pascal, None)?;
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Required => {
if matches!(field.r#type.variant, schema::TypeVariant::Unit) {
writeln!(buffer, ",")?;
} else {
write!(buffer, "(payload")?;
write_into_invocation(buffer, &field.r#type.variant)?;
writeln!(buffer, "),")?;
}
}
schema::Rule::Optional => {
if matches!(field.r#type.variant, schema::TypeVariant::Unit) {
writeln!(buffer, "(Box::new((*fallback).into())),")?;
} else {
write!(buffer, "(payload")?;
write_into_invocation(buffer, &field.r#type.variant)?;
writeln!(buffer, ", Box::new((*fallback).into())),")?;
}
}
}
}
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
write_indentation(buffer, indentation)?;
write!(buffer, "impl ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Out))?;
writeln!(buffer, " {{")?;
write_indentation(buffer, indentation + 1)?;
write!(buffer, "pub fn atlas(&self) -> ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Atlas))?;
writeln!(buffer, " {{")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "match *self {{")?;
for field in &declaration.fields {
write_indentation(buffer, indentation + 3)?;
write_identifier(buffer, &declaration.name, Pascal, Some(Out))?;
write!(buffer, "::")?;
write_identifier(buffer, &field.name, Pascal, None)?;
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Optional => {
if matches!(field.r#type.variant, schema::TypeVariant::Unit) {
writeln!(buffer, "(ref fallback) => {{")?;
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "let payload = &();")?;
} else {
writeln!(buffer, "(ref payload, ref fallback) => {{")?;
}
}
schema::Rule::Required => {
if matches!(field.r#type.variant, schema::TypeVariant::Unit) {
writeln!(buffer, " => {{")?;
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "let payload = &();")?;
} else {
writeln!(buffer, "(ref payload) => {{")?;
}
}
}
write_indentation(buffer, indentation + 4)?;
write!(buffer, "let payload_atlas = ")?;
write_atlas_calculation(buffer, indentation, &field.r#type.variant, true)?;
writeln!(buffer, ";")?;
write_indentation(buffer, indentation + 4)?;
write!(
buffer,
"let payload_size = {{ let payload_atlas = &payload_atlas; ",
)?;
write_atlas_lookup(buffer, &field.r#type.variant)?;
writeln!(buffer, " }};")?;
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Optional => {
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "let fallback_atlas = fallback.atlas();")?;
write_indentation(buffer, indentation + 4)?;
write_identifier(buffer, &declaration.name, Pascal, Some(Atlas))?;
write!(buffer, "::")?;
write_identifier(buffer, &field.name, Pascal, None)?;
write!(buffer, "(")?;
write_supers(buffer, indentation)?;
writeln!(
buffer,
"field_header_size({}_u64, payload_size, {}) + \
payload_size + fallback_atlas.size(), \
payload_atlas, Box::new(fallback_atlas))",
field.index,
integer_encoded(&field.r#type),
)?;
}
schema::Rule::Required => {
write_indentation(buffer, indentation + 4)?;
write_identifier(buffer, &declaration.name, Pascal, Some(Atlas))?;
write!(buffer, "::")?;
write_identifier(buffer, &field.name, Pascal, None)?;
write!(buffer, "(")?;
write_supers(buffer, indentation)?;
writeln!(
buffer,
"field_header_size({}_u64, payload_size, {}) + payload_size, \
payload_atlas)",
field.index,
integer_encoded(&field.r#type),
)?;
}
}
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "}}")?;
}
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "pub fn serialize_with_atlas<T: ::std::io::Write>(")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "&self,")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "writer: &mut T,")?;
write_indentation(buffer, indentation + 2)?;
write!(buffer, "atlas: &")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Atlas))?;
writeln!(buffer, ",")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, ") -> ::std::io::Result<()> {{")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "match (self, atlas) {{")?;
for field in &declaration.fields {
write_indentation(buffer, indentation + 3)?;
write!(buffer, "(")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Out))?;
write!(buffer, "::")?;
write_identifier(buffer, &field.name, Pascal, None)?;
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Optional => {
if matches!(field.r#type.variant, schema::TypeVariant::Unit) {
write!(buffer, "(fallback), ")?;
} else {
write!(buffer, "(payload, fallback), ")?;
}
}
schema::Rule::Required => {
if matches!(field.r#type.variant, schema::TypeVariant::Unit) {
write!(buffer, ", ")?;
} else {
write!(buffer, "(payload), ")?;
}
}
}
write_identifier(buffer, &declaration.name, Pascal, Some(Atlas))?;
write!(buffer, "::")?;
write_identifier(buffer, &field.name, Pascal, None)?;
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Optional => {
writeln!(buffer, "(_, payload_atlas, fallback_atlas)) => {{")?;
}
schema::Rule::Required => {
writeln!(buffer, "(_, payload_atlas)) => {{")?;
}
}
write_indentation(buffer, indentation + 4)?;
write_supers(buffer, indentation)?;
write!(
buffer,
"serialize_field_header(writer, {}_u64, ",
field.index,
)?;
write_atlas_lookup(buffer, &field.r#type.variant)?;
writeln!(buffer, ", {})?;", integer_encoded(&field.r#type))?;
write_serialization_invocation(
buffer,
indentation + 4,
indentation,
&field.r#type.variant,
true,
)?;
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Optional => {
write_indentation(buffer, indentation + 4)?;
writeln!(
buffer,
"fallback.serialize_with_atlas(writer, fallback_atlas)",
)?;
}
schema::Rule::Required => {
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "Ok(())")?;
}
}
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "}}")?;
}
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "(_, _) => panic!(),")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
write_indentation(buffer, indentation)?;
write!(buffer, "impl ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(In))?;
writeln!(buffer, " {{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(
buffer,
"pub fn deserialize_from_reader_ref<T: ::std::io::BufRead>(",
)?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "reader: &mut T,")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, ") -> ::std::io::Result<Self> {{")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "loop {{")?;
write_indentation(buffer, indentation + 3)?;
write!(buffer, "let (index, payload_size) = ")?;
write_supers(buffer, indentation)?;
writeln!(buffer, "deserialize_field_header(&mut *reader)?;")?;
writeln!(buffer)?;
write_indentation(buffer, indentation + 3)?;
writeln!(
buffer,
"let mut sub_reader = ::std::io::Read::take(&mut *reader, \
payload_size as u64);",
)?;
writeln!(buffer)?;
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "match index {{")?;
for field in &declaration.fields {
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "{} => {{", field.index)?;
write_deserialization_invocation(
buffer,
indentation + 5,
indentation,
&imports,
namespace,
&field.r#type.variant,
true,
)?;
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Required => {
write_indentation(buffer, indentation + 5)?;
write_supers(buffer, indentation)?;
writeln!(buffer, "finish(&mut *reader)?;")?;
write_indentation(buffer, indentation + 5)?;
write!(buffer, "return Ok(")?;
write_identifier(buffer, &declaration.name, Pascal, Some(In))?;
write!(buffer, "::")?;
write_identifier(buffer, &field.name, Pascal, None)?;
if matches!(field.r#type.variant, schema::TypeVariant::Unit) {
writeln!(buffer, ");")?;
} else {
writeln!(buffer, "(payload));")?;
}
}
schema::Rule::Optional => {
write_indentation(buffer, indentation + 5)?;
write!(buffer, "let fallback = Box::new(")?;
write_identifier(buffer, &declaration.name, Pascal, Some(In))?;
writeln!(buffer, "::deserialize_from_reader_ref(&mut *reader)?);")?;
write_indentation(buffer, indentation + 5)?;
write!(buffer, "return Ok(")?;
write_identifier(buffer, &declaration.name, Pascal, Some(In))?;
write!(buffer, "::")?;
write_identifier(buffer, &field.name, Pascal, None)?;
if matches!(field.r#type.variant, schema::TypeVariant::Unit) {
writeln!(buffer, "(fallback));")?;
} else {
writeln!(buffer, "(payload, fallback));")?;
}
}
}
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "}}")?;
}
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "_ => {{")?;
write_indentation(buffer, indentation + 5)?;
write_supers(buffer, indentation)?;
writeln!(buffer, "skip(&mut sub_reader, payload_size as usize)?;")?;
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
writeln!(buffer)?;
write_indentation(buffer, indentation)?;
write!(buffer, "impl ")?;
write_identifier(buffer, &declaration.name, Pascal, Some(Atlas))?;
writeln!(buffer, " {{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "pub fn size(&self) -> usize {{")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "match *self {{")?; for field in &declaration.fields {
write_indentation(buffer, indentation + 3)?;
write_identifier(buffer, &declaration.name, Pascal, Some(Atlas))?;
write!(buffer, "::")?;
write_identifier(buffer, &field.name, Pascal, None)?;
match field.rule {
schema::Rule::Asymmetric | schema::Rule::Optional => {
writeln!(buffer, "(ref size, _, _) => *size,")?;
}
schema::Rule::Required => {
writeln!(buffer, "(ref size, _) => *size,")?;
}
}
}
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
}
}
if iter.peek().is_some() {
writeln!(buffer)?;
}
}
Ok(())
}
fn write_struct<T: Write>(
buffer: &mut T,
indentation: usize,
imports: &BTreeMap<Identifier, schema::Namespace>,
namespace: &schema::Namespace,
name: &Identifier,
fields: &[schema::Field],
direction: Direction,
) -> Result<(), fmt::Error> {
write_indentation(buffer, indentation)?;
writeln!(buffer, "#[derive({})]", TRAITS_TO_DERIVE.join(", "))?;
write_indentation(buffer, indentation)?;
write!(buffer, "pub struct ")?;
write_identifier(buffer, name, Pascal, Some(direction))?;
writeln!(buffer, " {{")?;
match direction {
Direction::Atlas => {
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "pub _size: usize,")?;
}
Direction::In | Direction::Out => {}
}
for field in fields {
write_indentation(buffer, indentation + 1)?;
write!(buffer, "pub ")?;
write_identifier(buffer, &field.name, Snake, None)?;
write!(buffer, ": ")?;
match field.rule {
schema::Rule::Asymmetric => match direction {
Direction::Atlas | Direction::Out => {}
Direction::In => {
write!(buffer, "Option<")?;
}
},
schema::Rule::Optional => {
write!(buffer, "Option<")?;
}
schema::Rule::Required => {}
}
write_type(buffer, imports, namespace, &field.r#type.variant, direction)?;
match field.rule {
schema::Rule::Asymmetric => match direction {
Direction::Atlas | Direction::Out => {}
Direction::In => {
write!(buffer, ">")?;
}
},
schema::Rule::Optional => {
write!(buffer, ">")?;
}
schema::Rule::Required => {}
}
writeln!(buffer, ",")?;
}
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
Ok(())
}
fn write_choice<T: Write>(
buffer: &mut T,
indentation: usize,
imports: &BTreeMap<Identifier, schema::Namespace>,
namespace: &schema::Namespace,
name: &Identifier,
fields: &[schema::Field],
direction: Direction,
) -> Result<(), fmt::Error> {
write_indentation(buffer, indentation)?;
writeln!(buffer, "#[derive({})]", TRAITS_TO_DERIVE.join(", "))?;
write_indentation(buffer, indentation)?;
write!(buffer, "pub enum ")?;
write_identifier(buffer, name, Pascal, Some(direction))?;
writeln!(buffer, " {{")?;
for field in fields {
write_indentation(buffer, indentation + 1)?;
write_identifier(buffer, &field.name, Pascal, None)?;
let size = match direction {
Direction::Atlas => true,
Direction::In | Direction::Out => false,
};
let payload = match direction {
Direction::Atlas => true,
Direction::In | Direction::Out => {
!matches!(field.r#type.variant, schema::TypeVariant::Unit)
}
};
let fallback = match field.rule {
schema::Rule::Asymmetric => match direction {
Direction::Atlas | Direction::Out => true,
Direction::In => false,
},
schema::Rule::Optional => true,
schema::Rule::Required => false,
};
if size || payload || fallback {
write!(buffer, "(")?;
}
if size {
write!(buffer, "usize")?;
if payload || fallback {
write!(buffer, ", ")?;
}
}
if payload {
write_type(buffer, imports, namespace, &field.r#type.variant, direction)?;
if fallback {
write!(buffer, ", ")?;
}
}
if fallback {
write!(buffer, "Box<")?;
write_identifier(buffer, name, Pascal, Some(direction))?;
write!(buffer, ">")?;
}
if size || payload || fallback {
write!(buffer, ")")?;
}
writeln!(buffer, ",")?;
}
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
Ok(())
}
fn write_size_function<T: Write>(buffer: &mut T, indentation: usize) -> Result<(), fmt::Error> {
write_indentation(buffer, indentation)?;
writeln!(buffer, "fn size(&self) -> usize {{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "self.atlas().size()")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
}
fn write_serialize_function<T: Write>(
buffer: &mut T,
indentation: usize,
) -> Result<(), fmt::Error> {
write_indentation(buffer, indentation)?;
writeln!(
buffer,
"fn serialize<T: ::std::io::Write>(&self, mut writer: T) -> ::std::io::Result<()> {{",
)?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "let atlas = self.atlas();")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "self.serialize_with_atlas(&mut writer, &atlas)")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
}
fn write_deserialize_function<T: Write>(
buffer: &mut T,
indentation: usize,
) -> Result<(), fmt::Error> {
write_indentation(buffer, indentation)?;
writeln!(
buffer,
"fn deserialize<T: ::std::io::BufRead>(mut reader: T) -> ::std::io::Result<Self> {{",
)?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "Self::deserialize_from_reader_ref(&mut reader)")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
}
fn write_type<T: Write>(
buffer: &mut T,
imports: &BTreeMap<Identifier, schema::Namespace>,
namespace: &schema::Namespace,
type_variant: &schema::TypeVariant,
direction: Direction,
) -> Result<(), fmt::Error> {
match type_variant {
schema::TypeVariant::Array(inner_type) => match direction {
Direction::Atlas => match &inner_type.variant {
schema::TypeVariant::Array(_)
| schema::TypeVariant::Bytes
| schema::TypeVariant::Custom(_, _)
| schema::TypeVariant::String => {
write!(buffer, "(usize, Vec<")?;
write_type(buffer, imports, namespace, &inner_type.variant, direction)?;
write!(buffer, ">)")?;
}
schema::TypeVariant::Bool
| schema::TypeVariant::F64
| schema::TypeVariant::S64
| schema::TypeVariant::U64
| schema::TypeVariant::Unit => {
write!(buffer, "usize")?;
}
},
Direction::In | Direction::Out => {
write!(buffer, "Vec<")?;
write_type(buffer, imports, namespace, &inner_type.variant, direction)?;
write!(buffer, ">")?;
}
},
schema::TypeVariant::Bool => match direction {
Direction::Atlas => {
write!(buffer, "usize")?;
}
Direction::In | Direction::Out => {
write!(buffer, "bool")?;
}
},
schema::TypeVariant::Bytes => match direction {
Direction::Atlas => {
write!(buffer, "usize")?;
}
Direction::In | Direction::Out => {
write!(buffer, "Vec<u8>")?;
}
},
schema::TypeVariant::Custom(import, name) => {
let type_namespace = schema::Namespace {
components: import.as_ref().map_or_else(
|| namespace.components.clone(),
|import| imports[import].components.clone(),
),
};
let (relative_type_namespace, ancestors) =
relativize_namespace(&type_namespace, namespace);
write_supers(buffer, ancestors)?;
for component in relative_type_namespace.components {
write_identifier(buffer, &component, Snake, None)?;
write!(buffer, "::")?;
}
write_identifier(buffer, name, Pascal, Some(direction))?;
}
schema::TypeVariant::F64 => match direction {
Direction::Atlas => {
write!(buffer, "usize")?;
}
Direction::In | Direction::Out => {
write!(buffer, "f64")?;
}
},
schema::TypeVariant::S64 => match direction {
Direction::Atlas => {
write!(buffer, "usize")?;
}
Direction::In | Direction::Out => {
write!(buffer, "i64")?;
}
},
schema::TypeVariant::String => match direction {
Direction::Atlas => {
write!(buffer, "usize")?;
}
Direction::In | Direction::Out => {
write!(buffer, "String")?;
}
},
schema::TypeVariant::U64 => match direction {
Direction::Atlas => {
write!(buffer, "usize")?;
}
Direction::In | Direction::Out => {
write!(buffer, "u64")?;
}
},
schema::TypeVariant::Unit => match direction {
Direction::Atlas => {
write!(buffer, "usize")?;
}
Direction::In | Direction::Out => {
write!(buffer, "()")?;
}
},
}
Ok(())
}
fn write_identifier<T: Write>(
buffer: &mut T,
identifier: &Identifier,
case: CaseConvention,
suffix: Option<Direction>,
) -> Result<(), fmt::Error> {
let identifier_with_suffix = suffix.map_or_else(
|| identifier.clone(),
|suffix| {
identifier.join(
&match suffix {
Direction::Atlas => "Atlas",
Direction::In => "In",
Direction::Out => "Out",
}
.into(),
)
},
);
let converted_identifier = match case {
CaseConvention::Pascal => identifier_with_suffix.pascal_case(),
CaseConvention::Snake => identifier_with_suffix.snake_case(),
};
if RUST_KEYWORDS
.iter()
.any(|keyword| converted_identifier == *keyword)
{
write!(buffer, "r#")?;
}
write!(buffer, "{}", converted_identifier)?;
Ok(())
}
fn write_indentation<T: Write>(buffer: &mut T, indentation: usize) -> Result<(), fmt::Error> {
for _ in 0..indentation {
write!(buffer, "{}", INDENTATION)?;
}
Ok(())
}
fn write_supers<T: Write>(buffer: &mut T, count: usize) -> Result<(), fmt::Error> {
for _ in 0..count {
write!(buffer, "super::")?;
}
Ok(())
}
fn write_into_invocation<T: Write>(
buffer: &mut T,
type_variant: &schema::TypeVariant,
) -> Result<(), fmt::Error> {
if let schema::TypeVariant::Array(inner_type) = type_variant {
let mut layer = inner_type;
while let schema::TypeVariant::Array(inner_type) = &layer.variant {
layer = inner_type;
}
if let schema::TypeVariant::Custom(_, _) = &layer.variant {
write!(buffer, ".into_iter().map(|x| ")?;
layer = inner_type;
while let schema::TypeVariant::Array(inner_type) = &layer.variant {
layer = inner_type;
write!(buffer, "x.into_iter().map(|x| ")?;
}
write!(buffer, "x.into()")?;
layer = inner_type;
while let schema::TypeVariant::Array(inner_type) = &layer.variant {
layer = inner_type;
write!(buffer, ").collect::<Vec<_>>()")?;
}
write!(buffer, ").collect::<Vec<_>>()")
} else {
write!(buffer, ".into()")
}
} else {
write!(buffer, ".into()")
}
}
fn write_atlas_calculation<T: Write>(
buffer: &mut T,
supers: usize,
type_variant: &schema::TypeVariant,
is_field: bool,
) -> Result<(), fmt::Error> {
write!(buffer, "(")?;
match type_variant {
schema::TypeVariant::Array(inner_type) => match &inner_type.variant {
schema::TypeVariant::Array(_)
| schema::TypeVariant::Bytes
| schema::TypeVariant::Custom(_, _)
| schema::TypeVariant::String => {
write!(buffer, "{{ let atlases = payload.iter().map(|payload| ")?;
write_atlas_calculation(buffer, supers, &inner_type.variant, false)?;
write!(
buffer,
").collect::<Vec<_>>(); (atlases.iter().fold(0_usize, \
|x, payload_atlas| {{ let atlas_size = ",
)?;
write_atlas_lookup(buffer, &inner_type.variant)?;
write!(buffer, "; x + ")?;
write_supers(buffer, supers)?;
write!(
buffer,
"varint_size_from_value(atlas_size as u64) + atlas_size }}), atlases) }}",
)?;
}
schema::TypeVariant::Bool | schema::TypeVariant::S64 | schema::TypeVariant::U64 => {
write!(buffer, "payload.iter().fold(0_usize, |x, payload| x + ")?;
write_atlas_calculation(buffer, supers, &inner_type.variant, false)?;
write!(buffer, ")")?;
}
schema::TypeVariant::F64 => {
write!(buffer, "8_usize * payload.len()")?;
}
schema::TypeVariant::Unit => {
write!(buffer, "{{ let payload = &(payload.len() as u64); ")?;
write_atlas_calculation(buffer, supers, &schema::TypeVariant::U64, is_field)?;
write!(buffer, " }}")?;
}
},
schema::TypeVariant::Bool => {
if is_field {
write!(buffer, "if *payload {{ 1_usize }} else {{ 0_usize }}")?;
} else {
write!(buffer, "1_usize")?;
}
}
schema::TypeVariant::Bytes | schema::TypeVariant::String => {
write!(buffer, "payload.len()")?;
}
schema::TypeVariant::Custom(_, _) => {
write!(buffer, "payload.atlas()")?;
}
schema::TypeVariant::F64 => {
if is_field {
write!(
buffer,
"if payload.to_bits() == 0_u64 {{ 0_usize }} else {{ 8_usize }}",
)?;
} else {
write!(buffer, "8_usize")?;
}
}
schema::TypeVariant::S64 => {
write!(buffer, "{{ let zigzag = ")?;
write_supers(buffer, supers)?;
write!(buffer, "zigzag_encode(*payload); let payload = &zigzag; ")?;
write_atlas_calculation(buffer, supers, &schema::TypeVariant::U64, is_field)?;
write!(buffer, " }}")?;
}
schema::TypeVariant::U64 => {
if is_field {
write!(
buffer,
"match *payload {{ 0_u64 => {{ 0_usize }}, 1_u64..=567_382_630_219_903_u64 => \
{{ ",
)?;
write_supers(buffer, supers)?;
write!(
buffer,
"varint_size_from_value(*payload) }}, 567_382_630_219_904_u64..=\
18_446_744_073_709_551_615_u64 => {{ 8_usize }} }}",
)?;
} else {
write_supers(buffer, supers)?;
write!(buffer, "varint_size_from_value(*payload)")?;
}
}
schema::TypeVariant::Unit => {
write!(buffer, "0_usize")?;
}
}
write!(buffer, ")")
}
fn write_atlas_lookup<T: Write>(
buffer: &mut T,
type_variant: &schema::TypeVariant,
) -> Result<(), fmt::Error> {
match type_variant {
schema::TypeVariant::Array(inner_type) => match &inner_type.variant {
schema::TypeVariant::Array(_)
| schema::TypeVariant::Bytes
| schema::TypeVariant::Custom(_, _)
| schema::TypeVariant::String => write!(buffer, "payload_atlas.0"),
schema::TypeVariant::Bool
| schema::TypeVariant::F64
| schema::TypeVariant::S64
| schema::TypeVariant::U64
| schema::TypeVariant::Unit => write!(buffer, "*payload_atlas"),
},
schema::TypeVariant::Bool
| schema::TypeVariant::Bytes
| schema::TypeVariant::F64
| schema::TypeVariant::S64
| schema::TypeVariant::String
| schema::TypeVariant::U64
| schema::TypeVariant::Unit => write!(buffer, "*payload_atlas"),
schema::TypeVariant::Custom(_, _) => write!(buffer, "payload_atlas.size()"),
}
}
#[allow(clippy::too_many_lines)]
fn write_serialization_invocation<T: Write>(
buffer: &mut T,
indentation: usize,
supers: usize,
type_variant: &schema::TypeVariant,
is_field: bool,
) -> Result<(), fmt::Error> {
match type_variant {
schema::TypeVariant::Array(inner_type) => match &inner_type.variant {
schema::TypeVariant::Array(_)
| schema::TypeVariant::Bytes
| schema::TypeVariant::Custom(_, _)
| schema::TypeVariant::String => {
write_indentation(buffer, indentation)?;
writeln!(
buffer,
"for (payload, payload_atlas) in \
payload.iter().zip(payload_atlas.1.iter()) {{",
)?;
write_indentation(buffer, indentation + 1)?;
write_supers(buffer, supers)?;
write!(buffer, "serialize_varint(")?;
write_atlas_lookup(buffer, &inner_type.variant)?;
writeln!(buffer, " as u64, writer)?;")?;
write_serialization_invocation(
buffer,
indentation + 1,
supers,
&inner_type.variant,
false,
)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
}
schema::TypeVariant::Bool
| schema::TypeVariant::S64
| schema::TypeVariant::U64
| schema::TypeVariant::F64 => {
write_indentation(buffer, indentation)?;
writeln!(buffer, "for payload in payload {{")?;
write_serialization_invocation(
buffer,
indentation + 1,
supers,
&inner_type.variant,
false,
)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
}
schema::TypeVariant::Unit => {
write_indentation(buffer, indentation)?;
writeln!(buffer, "{{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "let varint = payload.len() as u64 as u64;")?;
write_u64_serialization_invocation(buffer, indentation + 1, supers, is_field)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
}
},
schema::TypeVariant::Bool => {
write_indentation(buffer, indentation)?;
writeln!(buffer, "{{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "let varint = *payload as u64;")?;
write_u64_serialization_invocation(buffer, indentation + 1, supers, is_field)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
}
schema::TypeVariant::Bytes => {
write_indentation(buffer, indentation)?;
writeln!(buffer, "writer.write_all(payload)?;")
}
schema::TypeVariant::Custom(_, _) => {
write_indentation(buffer, indentation)?;
writeln!(
buffer,
"payload.serialize_with_atlas(writer, payload_atlas)?;",
)
}
schema::TypeVariant::F64 => {
write_indentation(buffer, indentation)?;
if is_field {
writeln!(buffer, "if payload.to_bits() != 0_u64 {{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "writer.write_all(&payload.to_le_bytes())?;")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
} else {
writeln!(buffer, "writer.write_all(&payload.to_le_bytes())?;")
}
}
schema::TypeVariant::S64 => {
write_indentation(buffer, indentation)?;
writeln!(buffer, "{{")?;
write_indentation(buffer, indentation + 1)?;
write!(buffer, "let varint = ")?;
write_supers(buffer, supers)?;
writeln!(buffer, "zigzag_encode(*payload);")?;
write_u64_serialization_invocation(buffer, indentation + 1, supers, is_field)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
}
schema::TypeVariant::String => {
write_indentation(buffer, indentation)?;
writeln!(buffer, "writer.write_all(payload.as_bytes())?;")
}
schema::TypeVariant::U64 => {
write_indentation(buffer, indentation)?;
writeln!(buffer, "{{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "let varint = *payload;")?;
write_u64_serialization_invocation(buffer, indentation + 1, supers, is_field)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
}
schema::TypeVariant::Unit => Ok(()),
}
}
fn write_u64_serialization_invocation<T: Write>(
buffer: &mut T,
indentation: usize,
supers: usize,
is_field: bool,
) -> Result<(), fmt::Error> {
write_indentation(buffer, indentation)?;
if is_field {
writeln!(buffer, "match varint {{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "0_u64 => {{}}")?;
write_indentation(buffer, indentation + 1)?;
write!(buffer, "1_u64..=567_382_630_219_903_u64 => ")?;
write_supers(buffer, supers)?;
writeln!(buffer, "serialize_varint(varint, writer)?,")?;
write_indentation(buffer, indentation + 1)?;
writeln!(
buffer,
"567_382_630_219_904_u64..=18_446_744_073_709_551_615_u64 => \
writer.write_all(&varint.to_le_bytes())?,",
)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
} else {
write_supers(buffer, supers)?;
writeln!(buffer, "serialize_varint(varint, writer)?;")
}
}
#[allow(clippy::too_many_lines)]
fn write_deserialization_invocation<T: Write>(
buffer: &mut T,
indentation: usize,
supers: usize,
imports: &BTreeMap<Identifier, schema::Namespace>,
namespace: &schema::Namespace,
type_variant: &schema::TypeVariant,
is_field: bool,
) -> Result<(), fmt::Error> {
match type_variant {
schema::TypeVariant::Array(inner_type) => match &inner_type.variant {
schema::TypeVariant::Array(_)
| schema::TypeVariant::Bytes
| schema::TypeVariant::Custom(_, _)
| schema::TypeVariant::String => {
write_indentation(buffer, indentation)?;
writeln!(buffer, "let mut payload = Vec::new();")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "loop {{")?;
write_indentation(buffer, indentation + 1)?;
write!(buffer, "let payload_size = match ")?;
write_supers(buffer, supers)?;
writeln!(buffer, "deserialize_varint(&mut sub_reader) {{")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "Ok(payload_size) => payload_size as usize,")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "Err(err) => {{")?;
write_indentation(buffer, indentation + 3)?;
writeln!(
buffer,
"if let std::io::ErrorKind::UnexpectedEof = err.kind() {{",
)?;
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "break;")?;
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "return Err(err);")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}};")?;
write_indentation(buffer, indentation + 1)?;
writeln!(
buffer,
"let mut sub_reader = ::std::io::Read::take(\
&mut sub_reader, payload_size as u64);",
)?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "payload.push({{")?;
write_deserialization_invocation(
buffer,
indentation + 2,
supers,
imports,
namespace,
&inner_type.variant,
false,
)?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "payload")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}});")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
}
schema::TypeVariant::Bool
| schema::TypeVariant::F64
| schema::TypeVariant::S64
| schema::TypeVariant::U64 => {
write_indentation(buffer, indentation)?;
write!(
buffer,
"fn deserialize_element<T: ::std::io::BufRead>(mut sub_reader: &mut T) -> \
::std::io::Result<",
)?;
write_type(buffer, imports, namespace, &inner_type.variant, In)?;
writeln!(buffer, "> {{")?;
write_deserialization_invocation(
buffer,
indentation + 1,
supers,
imports,
namespace,
&inner_type.variant,
false,
)?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "Ok(payload)")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "let mut payload = Vec::new();")?;
if matches!(inner_type.variant, schema::TypeVariant::F64) {
write_indentation(buffer, indentation)?;
writeln!(buffer, "payload.reserve_exact(payload_size);")?;
}
write_indentation(buffer, indentation)?;
writeln!(buffer, "loop {{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(
buffer,
"payload.push(match deserialize_element(&mut sub_reader) {{",
)?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "Ok(element) => element,")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "Err(err) => {{")?;
write_indentation(buffer, indentation + 3)?;
writeln!(
buffer,
"if let std::io::ErrorKind::UnexpectedEof = err.kind() {{",
)?;
write_indentation(buffer, indentation + 4)?;
writeln!(buffer, "break;")?;
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "return Err(err);")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}});")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
}
schema::TypeVariant::Unit => {
write_deserialization_invocation(
buffer,
indentation,
supers,
imports,
namespace,
&schema::TypeVariant::U64,
is_field,
)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "let payload = vec![(); payload as usize];")
}
},
schema::TypeVariant::Bool => {
write_deserialization_invocation(
buffer,
indentation,
supers,
imports,
namespace,
&schema::TypeVariant::U64,
is_field,
)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "let payload = payload != 0_u64;")
}
schema::TypeVariant::Bytes => {
write_indentation(buffer, indentation)?;
writeln!(buffer, "let mut payload = vec![];")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "payload.reserve_exact(payload_size);")?;
write_indentation(buffer, indentation)?;
writeln!(
buffer,
"::std::io::Read::read_to_end(&mut sub_reader, &mut payload)?;",
)
}
schema::TypeVariant::Custom(_, _) => {
write_indentation(buffer, indentation)?;
write!(buffer, "let payload = ")?;
write_type(buffer, imports, namespace, type_variant, In)?;
writeln!(buffer, "::deserialize_from_reader_ref(&mut sub_reader)?;")
}
schema::TypeVariant::F64 => {
write_indentation(buffer, indentation)?;
if is_field {
writeln!(buffer, "let payload = if payload_size == 0_usize {{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "0.0_f64")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}} else {{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "let mut buffer = [0; 8];")?;
write_indentation(buffer, indentation + 1)?;
writeln!(
buffer,
"::std::io::Read::read_exact(&mut sub_reader, &mut buffer)?;",
)?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "f64::from_le_bytes(buffer)")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}};")
} else {
writeln!(buffer, "let mut buffer = [0; 8];")?;
write_indentation(buffer, indentation)?;
writeln!(
buffer,
"::std::io::Read::read_exact(&mut sub_reader, &mut buffer)?;",
)?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "let payload = f64::from_le_bytes(buffer);")
}
}
schema::TypeVariant::S64 => {
write_deserialization_invocation(
buffer,
indentation,
supers,
imports,
namespace,
&schema::TypeVariant::U64,
is_field,
)?;
write_indentation(buffer, indentation)?;
write!(buffer, "let payload = ")?;
write_supers(buffer, supers)?;
writeln!(buffer, "zigzag_decode(payload);")
}
schema::TypeVariant::String => {
write_indentation(buffer, indentation)?;
writeln!(buffer, "let mut payload = String::new();")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "payload.reserve_exact(payload_size);")?;
write_indentation(buffer, indentation)?;
writeln!(
buffer,
"::std::io::Read::read_to_string(&mut sub_reader, &mut payload)?;",
)
}
schema::TypeVariant::U64 => {
write_indentation(buffer, indentation)?;
if is_field {
writeln!(buffer, "let payload = match payload_size {{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "0_usize => 0_u64,")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "8_usize => {{")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "let mut buffer = [0; 8];")?;
write_indentation(buffer, indentation + 2)?;
writeln!(
buffer,
"::std::io::Read::read_exact(&mut sub_reader, &mut buffer[..])?;",
)?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "u64::from_le_bytes(buffer)")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 1)?;
write!(buffer, "_ => ")?;
write_supers(buffer, supers)?;
writeln!(buffer, "deserialize_varint(&mut sub_reader)?,")?;
write_indentation(buffer, indentation)?;
writeln!(buffer, "}};")
} else {
write!(buffer, "let payload = ")?;
write_supers(buffer, supers)?;
writeln!(buffer, "deserialize_varint(&mut sub_reader)?;")
}
}
schema::TypeVariant::Unit => {
write_indentation(buffer, indentation)?;
writeln!(buffer, "let payload = ();")
}
}
}
fn integer_encoded(r#type: &schema::Type) -> bool {
match &r#type.variant {
schema::TypeVariant::Bool | schema::TypeVariant::S64 | schema::TypeVariant::U64 => true,
schema::TypeVariant::Array(_)
| schema::TypeVariant::Bytes
| schema::TypeVariant::Custom(_, _)
| schema::TypeVariant::F64
| schema::TypeVariant::String
| schema::TypeVariant::Unit => false,
}
}
#[cfg(test)]
mod tests {
use {
crate::{generate_rust::generate, schema_loader::load_schemas, validator::validate},
std::{fs::read_to_string, path::Path},
};
#[test]
fn generate_example() {
let schemas = load_schemas(Path::new("integration_tests/types/types.t")).unwrap();
validate(&schemas).unwrap();
assert_eq!(
generate("0.0.0", &schemas),
read_to_string("test_data/types.rs").unwrap(),
);
}
}