use std::fmt::Write;
fn main() {
let cargo_toml_path = std::env::current_dir().unwrap().join("Cargo.toml");
let cargo_toml_content =
std::fs::read_to_string(cargo_toml_path).expect("Failed to read Cargo.toml");
if !cargo_toml_content.contains("[workspace]") {
panic!(
"Cargo.toml does not contain [workspace] (you must run codegen from the workspace root)"
);
}
let mut output = String::new();
let _ = codegen_tuple_impls(&mut output);
let path = "shapely-core/src/impls/tuples_impls.rs";
std::fs::write(path, output).expect("Failed to write file");
let status = std::process::Command::new("rustfmt")
.arg("--edition")
.arg("2024")
.arg(path)
.status()
.expect("Failed to execute rustfmt");
if !status.success() {
eprintln!("rustfmt failed with exit code: {}", status);
}
}
fn codegen_tuple_impls(w: &mut dyn Write) -> std::fmt::Result {
writeln!(w, "use std::alloc::Layout;")?;
writeln!(w)?;
writeln!(
w,
"use crate::{{Field, FieldFlags, Innards, Shape, ShapeDesc, Shapely, mini_typeid}};"
)?;
for n in 1..=12 {
let type_params = (0..n)
.map(|i| format!("T{}", i))
.collect::<Vec<_>>()
.join(", ");
let mut name_format =
r#"if let Some(opts) = opts.for_children() { write!(f, "(")?;"#.to_string();
for i in 0..n {
name_format += &format!(r#"(T{i}::shape().name)(f, opts)?;"#,);
if i < n - 1 {
name_format += r#"write!(f, ",")?;"#;
}
}
name_format += r#"
write!(f, ")")
} else {
write!(f, "()")
}"#;
let where_clause = (0..n)
.map(|i| format!("T{}: Shapely", i))
.collect::<Vec<_>>()
.join(", ");
let tuple = if n == 1 {
"(T0,)".to_string()
} else {
format!("({})", type_params)
};
let field_macro = format!(
r#"
macro_rules! field {{
($idx:tt, $ty:ty) => {{
Field {{
name: stringify!($idx),
shape: ShapeDesc(<$ty>::shape),
offset: std::mem::offset_of!({tuple}, $idx),
flags: FieldFlags::EMPTY,
}}
}}
}}
"#
);
let fields = format!(
"&const {{ [ {} ] }}",
(0..n)
.map(|i| format!("field!({}, T{})", i, i))
.collect::<Vec<_>>()
.join(",")
);
writeln!(
w,
"
impl<{type_params}> Shapely for {tuple} where {where_clause}
{{
fn shape() -> Shape {{
{field_macro}
Shape {{
name: |f, opts| {{
{name_format}
}},
typeid: mini_typeid::of::<Self>(),
layout: Layout::new::<{tuple}>(),
innards: Innards::Tuple {{
fields: {fields}
}},
set_to_default: None,
drop_in_place: Some(|addr: *mut u8| unsafe {{
std::ptr::drop_in_place(addr as *mut {tuple});
}}),
}}
}}
}}"
)?;
}
Ok(())
}