1use proc_macro::TokenStream;
7use std::path::PathBuf;
8
9#[proc_macro_derive(TypeWriter, attributes(sync_to, tw))]
28pub fn derive_typewriter(input: TokenStream) -> TokenStream {
29 let input = syn::parse_macro_input!(input as syn::DeriveInput);
30
31 match typewriter_impl(&input) {
32 Ok(_) => TokenStream::new(),
33 Err(err) => err.to_compile_error().into(),
34 }
35}
36
37fn typewriter_impl(input: &syn::DeriveInput) -> syn::Result<()> {
38 let type_def = typewriter_engine::parser::parse_type_def(input)?;
39 let targets = typewriter_engine::parser::parse_sync_to_attr(input)?;
40
41 if targets.is_empty() {
42 return Err(syn::Error::new_spanned(
43 &input.ident,
44 "typewriter: #[sync_to(...)] attribute is required. \
45 Example: #[sync_to(typescript, python)]",
46 ));
47 }
48
49 let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string());
50 let manifest_dir = PathBuf::from(manifest_dir);
51 let project_root = typewriter_engine::project::discover_macro_root(&manifest_dir);
52 let config = typewriter_engine::project::load_config_or_default(&project_root);
53
54 let spec = typewriter_engine::TypeSpec {
55 type_def,
56 targets,
57 source_path: manifest_dir.join("<proc-macro>"),
58 };
59
60 let files =
61 match typewriter_engine::emit::render_specs(&[spec], &project_root, &config, &[], true) {
62 Ok(files) => files,
63 Err(err) => {
64 eprintln!("typewriter: generation failed for {}: {}", input.ident, err);
65 return Ok(());
66 }
67 };
68
69 if let Err(err) = typewriter_engine::emit::write_generated_files(&files) {
70 eprintln!("typewriter: failed to write generated files: {}", err);
71 return Ok(());
72 }
73
74 for file in files {
75 eprintln!(
76 " typewriter: {} → {}",
77 file.type_name,
78 file.output_path.display()
79 );
80 }
81
82 Ok(())
83}