praborrow_sidl/
lib.rs

1mod error;
2mod lexer;
3mod parser;
4
5use crate::lexer::Lexer;
6use crate::parser::{Def, Parser};
7use proc_macro::TokenStream;
8use quote::{format_ident, quote};
9use std::fs;
10use std::path::PathBuf;
11
12/// Macro to include a .sidl file and generate Rust code.
13#[proc_macro]
14pub fn include_sidl(input: TokenStream) -> TokenStream {
15    let input_path_str = input.to_string().trim_matches('"').to_string();
16
17    // Resolve path relative to CARGO_MANIFEST_DIR
18    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
19    let path = PathBuf::from(manifest_dir).join(&input_path_str);
20
21    let sidl_content = match fs::read_to_string(&path) {
22        Ok(content) => content,
23        Err(e) => {
24            return syn::Error::new(
25                proc_macro2::Span::call_site(),
26                format!("Failed to read SIDL file {:?}: {}", path, e),
27            )
28            .to_compile_error()
29            .into();
30        }
31    };
32
33    let lexer = Lexer::new(&sidl_content);
34    let mut parser = match Parser::new(lexer) {
35        Ok(p) => p,
36        Err(e) => {
37            return syn::Error::new(
38                proc_macro2::Span::call_site(),
39                format!("SIDL Parser Init Error: {}", e),
40            )
41            .to_compile_error()
42            .into();
43        }
44    };
45
46    let defs = match parser.parse() {
47        Ok(d) => d,
48        Err(e) => {
49            return syn::Error::new(
50                proc_macro2::Span::call_site(),
51                format!("SIDL Parse Error: {}", e),
52            )
53            .to_compile_error()
54            .into();
55        }
56    };
57
58    let mut expanded_tokens = proc_macro2::TokenStream::new();
59
60    for def in defs {
61        match def {
62            Def::Struct(s) => {
63                let name = format_ident!("{}", s.name);
64                let fields = s.fields.iter().map(|(n, t)| {
65                    let field_name = format_ident!("{}", n);
66                    let field_type = format_ident!("{}", t);
67                    quote! { pub #field_name: #field_type, }
68                });
69
70                expanded_tokens.extend(quote! {
71                    #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Diplomat)]
72                    pub struct #name {
73                        #(#fields)*
74                    }
75                });
76            }
77            Def::Service(s) => {
78                let name = format_ident!("{}", s.name);
79                let methods = s.methods.iter().map(|m| {
80                    let method_name = format_ident!("{}", m.name);
81                    let arg_name = format_ident!("{}", m.arg_name);
82                    let arg_type = format_ident!("{}", m.arg_type);
83                    let ret_type = format_ident!("{}", m.ret_type);
84
85                    quote! {
86                        async fn #method_name(&self, #arg_name: #arg_type) -> #ret_type;
87                    }
88                });
89
90                expanded_tokens.extend(quote! {
91                    #[async_trait::async_trait]
92                    pub trait #name {
93                        #(#methods)*
94                    }
95                });
96            }
97        }
98    }
99
100    TokenStream::from(expanded_tokens)
101}
102
103/// Derive macro for the `Diplomat` trait.
104#[proc_macro_derive(Diplomat)]
105pub fn derive_diplomat(input: TokenStream) -> TokenStream {
106    let input = syn::parse_macro_input!(input as syn::DeriveInput);
107    let name = input.ident;
108
109    let expanded = quote! {
110        impl praborrow_diplomacy::Diplomat for #name {}
111    };
112    TokenStream::from(expanded)
113}