gear_mesh_derive/lib.rs
1//! gear-mesh-derive: proc-macro for GearMesh derive
2//!
3//! このクレートは `#[derive(GearMesh)]` マクロを提供します。
4
5use proc_macro::TokenStream;
6use quote::quote;
7use syn::{DeriveInput, parse_macro_input};
8
9mod attributes;
10mod parser;
11
12use parser::parse_type;
13
14/// GearMesh derive macro
15///
16/// Rust型をTypeScriptに変換可能な中間表現に変換します。
17///
18/// # 属性
19///
20/// - `#[gear_mesh(branded)]`: Branded Typeとして生成
21/// - `#[gear_mesh(validate)]`: バリデーション関数を生成
22/// - `#[gear_mesh(bigint = "auto")]`: BigInt自動変換を有効化
23///
24/// # Example
25///
26/// ```ignore
27/// use gear_mesh::GearMesh;
28///
29/// #[derive(GearMesh)]
30/// #[gear_mesh(branded)]
31/// struct UserId(i32);
32///
33/// #[derive(GearMesh)]
34/// struct User {
35/// id: UserId,
36/// name: String,
37/// }
38/// ```
39#[proc_macro_derive(GearMesh, attributes(gear_mesh, validate))]
40pub fn derive_gear_mesh(input: TokenStream) -> TokenStream {
41 let input = parse_macro_input!(input as DeriveInput);
42
43 match parse_type(&input) {
44 Ok(gear_mesh_type) => {
45 let name = &input.ident;
46 let type_json = serde_json::to_string(&gear_mesh_type).unwrap_or_default();
47
48 let expanded = quote! {
49 impl ::gear_mesh::GearMeshExport for #name {
50 fn gear_mesh_type() -> ::gear_mesh::GearMeshType {
51 let json = #type_json;
52 ::serde_json::from_str(json).expect("Failed to deserialize GearMeshType")
53 }
54
55 fn type_name() -> &'static str {
56 stringify!(#name)
57 }
58 }
59
60 // Register type with inventory for automatic collection
61 ::gear_mesh::inventory::submit! {
62 ::gear_mesh::TypeInfo {
63 get_type: || <#name as ::gear_mesh::GearMeshExport>::gear_mesh_type(),
64 type_name: stringify!(#name),
65 }
66 }
67 };
68
69 // If output path is specified, trigger automatic generation
70 let output_trigger = if let Some(output_path) = &gear_mesh_type.attributes.output_path {
71 quote! {
72 // Trigger automatic type generation on first use
73 const _: () = {
74 ::gear_mesh::register_output(#output_path);
75 };
76 }
77 } else {
78 quote! {}
79 };
80
81 let final_output = quote! {
82 #expanded
83 #output_trigger
84 };
85
86 TokenStream::from(final_output)
87 }
88 Err(err) => TokenStream::from(err.to_compile_error()),
89 }
90}