use std::{borrow::Cow, collections::BTreeMap, fmt::Debug};
use serde::{self, Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct TypeRoot {
#[serde(rename = "f")]
pub file: String,
#[serde(rename = "l")]
pub line: u32,
#[serde(rename = "i")]
pub inner: Named<ContainerFormat>,
#[serde(rename = "e")]
pub extras: Vec<Named<ContainerFormat>>,
}
#[derive(Serialize, Deserialize)]
pub struct Spanned<T> {
#[serde(rename = "$")]
pub value: T,
#[serde(rename = "_")]
#[serde(skip_serializing_if = "is_null_bytes", default)]
pub bytes: (usize, usize),
}
fn is_null_bytes(value: &(usize, usize)) -> bool {
value.0 == 0 && value.1 == 0
}
impl<T: Debug> Debug for Spanned<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("#{:?} ", self.bytes))?;
self.value.fmt(f)
}
}
#[derive(Serialize, Deserialize, Debug)]
pub enum Format {
Incomplete {
debug: String,
},
TypeName(String),
Unit,
Bool,
I8,
I16,
I32,
I64,
I128,
ISIZE,
U8,
U16,
U32,
U64,
U128,
USIZE,
F32,
F64,
Char,
Str,
Bytes,
Option(Box<Format>),
Never,
Seq(Box<Format>),
Map {
key: Box<Format>,
value: Box<Format>,
},
Tuple(Vec<Format>),
TupleArray {
content: Box<Format>,
size: usize,
},
}
impl Format {
pub fn is_primitive(&self) -> bool {
match self {
Format::Unit
| Format::Bool
| Format::I8
| Format::I16
| Format::I32
| Format::I64
| Format::I128
| Format::ISIZE
| Format::U8
| Format::U16
| Format::U32
| Format::U64
| Format::U128
| Format::USIZE
| Format::F32
| Format::F64
| Format::Char
| Format::Str
| Format::Bytes => true,
_ => false,
}
}
pub fn is_typename(&self) -> Option<&str> {
match self {
Format::TypeName(name) => Some(name),
_ => None,
}
}
pub fn as_ident(&self) -> Cow<'static, str> {
Cow::Borrowed(match self {
Format::Incomplete { debug } => todo!("Unknown ident incomplete: {debug}"),
Format::TypeName(name) => return Cow::Owned(name.clone()),
Format::Unit => "Nil",
Format::Bool => "Bool",
Format::I8 => "I8",
Format::I16 => "I16",
Format::I32 => "I32",
Format::I64 => "I64",
Format::I128 => "I128",
Format::ISIZE => "ISIZE",
Format::U8 => "U8",
Format::U16 => "U16",
Format::U32 => "U32",
Format::U64 => "U64",
Format::U128 => "U128",
Format::USIZE => "USIZE",
Format::F32 => "F32",
Format::F64 => "F64",
Format::Char => "Char",
Format::Str => "Str",
Format::Bytes => "Bytes",
Format::Option(of) => return Cow::Owned(format!("{}_Option", of.as_ident())),
Format::Never => "Never",
Format::Seq(of) => return Cow::Owned(format!("{}_List", of.as_ident())),
Format::Map { key, value } => {
return Cow::Owned(format!("{}_{}_Map", key.as_ident(), value.as_ident()))
}
Format::Tuple(of) => {
return Cow::Owned(format!(
"{}Tuple",
of.iter()
.flat_map(|v| [v.as_ident(), Cow::Borrowed("_")])
.collect::<String>()
))
}
Format::TupleArray { content, size } => {
return Cow::Owned(format!("{}_{}_TupleOf", content.as_ident(), size))
}
})
}
pub fn replace_incomplete(&mut self, replacement: Format) {
if let Format::Incomplete { .. } = self {
*self = replacement;
return;
}
if self.is_primitive() || self.is_typename().is_some() {
return;
}
match (self, replacement) {
(Format::Option(ref mut original), Format::Option(replacement)) => {
original.replace_incomplete(*replacement);
}
(Format::Seq(ref mut original), Format::Seq(replacement)) => {
original.replace_incomplete(*replacement);
}
(
Format::Map {
ref mut key,
ref mut value,
},
Format::Map {
key: replace_key,
value: replace_value,
},
) => {
key.replace_incomplete(*replace_key);
value.replace_incomplete(*replace_value);
}
(Format::Tuple(ref mut original), Format::Tuple(replacement_vec)) => {
for (original_item, replacement) in original.iter_mut().zip(replacement_vec) {
original_item.replace_incomplete(replacement);
}
}
(
Format::TupleArray {
ref mut content, ..
},
Format::TupleArray {
content: replacement_content,
..
},
) => {
content.replace_incomplete(*replacement_content);
}
(original, replacement) => {
panic!("Failed to merge original and replacement:\n{original:#?}\nREPLACEMENT\n{replacement:#?}")
}
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub enum ContainerFormat {
UnitStruct,
NewTypeStruct(Box<Format>),
TupleStruct(Vec<Format>),
Struct(Vec<Named<Format>>),
Enum(BTreeMap<u32, Named<VariantFormat>>),
}
#[derive(Serialize, Deserialize, Debug)]
pub enum VariantFormat {
Unit,
NewType(Box<Format>),
Tuple(Vec<Format>),
Struct(Vec<Named<Format>>),
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Named<T> {
#[serde(rename = "id")]
pub rust_ident: Spanned<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
#[serde(rename = "docs")]
pub rust_docs: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
#[serde(rename = "sa")]
pub serde_attrs: Vec<Spanned<(Spanned<String>, Spanned<String>)>>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
#[serde(rename = "sf")]
pub serde_flags: Vec<Spanned<String>>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
#[serde(rename = "ca")]
pub codegen_attrs: Vec<Spanned<(Spanned<String>, Spanned<String>)>>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
#[serde(rename = "cf")]
pub codegen_flags: Vec<Spanned<String>>,
#[serde(rename = "$")]
pub value: T,
}
impl<T> Named<T> {
pub fn builtin(ident: &str, docs: &str, bytes: Option<(usize, usize)>, value: T) -> Self {
Named {
rust_ident: Spanned {
value: ident.to_string(),
bytes: bytes.unwrap_or_default(),
},
rust_docs: {
let docs = docs.trim();
if docs.is_empty() {
None
} else {
Some(docs.to_string())
}
},
serde_attrs: Vec::new(),
serde_flags: Vec::new(),
codegen_attrs: Vec::new(),
codegen_flags: Vec::new(),
value,
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub enum EnumRepresentation {
External,
Untagged,
Tagged {
tag: Spanned<String>,
content: Option<Spanned<String>>,
},
}
impl<T> Named<T> {
pub fn serialize_name(&self) -> &str {
self.serde_attrs
.iter()
.filter_map(|attr| {
if attr.value.0.value == "rename" {
Some(&attr.value.1.value)
} else {
None
}
})
.last()
.unwrap_or(&self.rust_ident.value)
}
}