openrr_internal_codegen/
main.rs

1mod plugin;
2mod rpc;
3
4use std::path::{Path, PathBuf};
5
6use anyhow::{Result, format_err};
7use fs_err as fs;
8use proc_macro2::TokenStream;
9
10fn main() -> Result<()> {
11    let workspace_root = workspace_root();
12    plugin::r#gen(&workspace_root)?;
13    rpc::r#gen(&workspace_root)?;
14    Ok(())
15}
16
17fn arci_types(
18    workspace_root: &Path,
19) -> Result<(
20    Vec<syn::ItemTrait>,
21    Vec<syn::ItemStruct>,
22    Vec<syn::ItemEnum>,
23)> {
24    let path = &workspace_root.join("arci/src/traits");
25    let mut files: Vec<_> = fs::read_dir(path)?
26        .filter_map(Result::ok)
27        .filter_map(|entry| {
28            let path = entry.path();
29            if !path.is_file() || path.extension().is_none_or(|e| e != "rs") {
30                None
31            } else {
32                Some(path)
33            }
34        })
35        .collect();
36    files.sort_unstable();
37    let mut traits = vec![];
38    let mut structs = vec![];
39    let mut enums = vec![];
40    for path in &files {
41        let s = &fs::read_to_string(path)?;
42        let file: syn::File = syn::parse_str(s)?;
43        for item in file.items {
44            match item {
45                syn::Item::Trait(item) if matches!(item.vis, syn::Visibility::Public(_)) => {
46                    traits.push(item);
47                }
48                syn::Item::Struct(item) if matches!(item.vis, syn::Visibility::Public(_)) => {
49                    structs.push(item);
50                }
51                syn::Item::Enum(item) if matches!(item.vis, syn::Visibility::Public(_)) => {
52                    enums.push(item);
53                }
54                _ => {}
55            }
56        }
57    }
58    Ok((traits, structs, enums))
59}
60
61fn is_str(ty: &syn::Type) -> bool {
62    if let syn::Type::Reference(ty) = ty
63        && let Some(path) = get_ty_path(&ty.elem)
64        && path.is_ident("str")
65    {
66        return true;
67    }
68    false
69}
70
71fn get_ty_path(ty: &syn::Type) -> Option<&syn::Path> {
72    if let syn::Type::Path(ty) = ty {
73        return Some(&ty.path);
74    }
75    None
76}
77
78fn is_option(ty: &syn::Type) -> Option<&syn::Type> {
79    let path = get_ty_path(ty)?;
80    if path.segments.len() == 1
81        && path.segments[0].ident == "Option"
82        && let syn::PathArguments::AngleBracketed(args) = &path.segments.last().unwrap().arguments
83        && let syn::GenericArgument::Type(ty) = &args.args[0]
84    {
85        return Some(ty);
86    }
87    None
88}
89
90fn is_result(ty: &syn::Type) -> Option<&syn::Type> {
91    let path = get_ty_path(ty)?;
92    if path.segments.len() == 1
93        && path.segments[0].ident == "Result"
94        && let syn::PathArguments::AngleBracketed(args) = &path.segments.last().unwrap().arguments
95        && let syn::GenericArgument::Type(ty) = &args.args[0]
96    {
97        return Some(ty);
98    }
99    None
100}
101
102fn is_vec(ty: &syn::Type) -> Option<&syn::Type> {
103    let path = get_ty_path(ty)?;
104    if path.segments.len() == 1
105        && path.segments[0].ident == "Vec"
106        && let syn::PathArguments::AngleBracketed(args) = &path.segments.last().unwrap().arguments
107        && let syn::GenericArgument::Type(ty) = &args.args[0]
108    {
109        return Some(ty);
110    }
111    None
112}
113
114pub(crate) fn is_primitive(ty: &syn::Type) -> bool {
115    if let Some(path) = get_ty_path(ty)
116        && let Some(ident) = path.get_ident()
117    {
118        return matches!(
119            ident.to_string().as_str(),
120            "isize"
121                | "i8"
122                | "i16"
123                | "i32"
124                | "i64"
125                | "i128"
126                | "usize"
127                | "u8"
128                | "u16"
129                | "u32"
130                | "u64"
131                | "u128"
132                | "f32"
133                | "f64"
134                | "bool"
135        );
136    }
137    false
138}
139
140fn workspace_root() -> PathBuf {
141    let mut dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
142    dir.pop(); // codegen
143    dir.pop(); // tools
144    dir
145}
146
147fn header() -> String {
148    concat!(
149        "// This file is @generated by ",
150        env!("CARGO_BIN_NAME"),
151        ".\n",
152        "// It is not intended for manual editing.\n",
153        "\n",
154        "#![allow(unused_variables)]\n",
155        "#![allow(clippy::useless_conversion, clippy::unit_arg)]\n",
156        "\n",
157    )
158    .into()
159}
160
161fn write(path: &Path, contents: TokenStream) -> Result<()> {
162    let mut out = header().into_bytes();
163    out.extend_from_slice(
164        prettyplease::unparse(
165            &syn::parse2(contents.clone())
166                .map_err(|e| format_err!("{e} in:\n---\n{contents}\n---"))?,
167        )
168        .as_bytes(),
169    );
170    if path.is_file() && fs::read(path)? == out {
171        return Ok(());
172    }
173    fs::write(path, out)?;
174    Ok(())
175}