openrr_internal_codegen/
main.rs

1mod plugin;
2mod rpc;
3
4use std::path::{Path, PathBuf};
5
6use anyhow::{format_err, Result};
7use fs_err as fs;
8use proc_macro2::TokenStream;
9
10fn main() -> Result<()> {
11    let workspace_root = workspace_root();
12    plugin::gen(&workspace_root)?;
13    rpc::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        if let Some(path) = get_ty_path(&ty.elem) {
64            if path.is_ident("str") {
65                return true;
66            }
67        }
68    }
69    false
70}
71
72fn get_ty_path(ty: &syn::Type) -> Option<&syn::Path> {
73    if let syn::Type::Path(ty) = ty {
74        return Some(&ty.path);
75    }
76    None
77}
78
79fn is_option(ty: &syn::Type) -> Option<&syn::Type> {
80    let path = get_ty_path(ty)?;
81    if path.segments.len() == 1 && path.segments[0].ident == "Option" {
82        if let syn::PathArguments::AngleBracketed(args) = &path.segments.last().unwrap().arguments {
83            if let syn::GenericArgument::Type(ty) = &args.args[0] {
84                return Some(ty);
85            }
86        }
87    }
88    None
89}
90
91fn is_result(ty: &syn::Type) -> Option<&syn::Type> {
92    let path = get_ty_path(ty)?;
93    if path.segments.len() == 1 && path.segments[0].ident == "Result" {
94        if let syn::PathArguments::AngleBracketed(args) = &path.segments.last().unwrap().arguments {
95            if let syn::GenericArgument::Type(ty) = &args.args[0] {
96                return Some(ty);
97            }
98        }
99    }
100    None
101}
102
103fn is_vec(ty: &syn::Type) -> Option<&syn::Type> {
104    let path = get_ty_path(ty)?;
105    if path.segments.len() == 1 && path.segments[0].ident == "Vec" {
106        if let syn::PathArguments::AngleBracketed(args) = &path.segments.last().unwrap().arguments {
107            if let syn::GenericArgument::Type(ty) = &args.args[0] {
108                return Some(ty);
109            }
110        }
111    }
112    None
113}
114
115pub(crate) fn is_primitive(ty: &syn::Type) -> bool {
116    if let Some(path) = get_ty_path(ty) {
117        if let Some(ident) = path.get_ident() {
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    }
138    false
139}
140
141fn workspace_root() -> PathBuf {
142    let mut dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
143    dir.pop(); // codegen
144    dir.pop(); // tools
145    dir
146}
147
148fn header() -> String {
149    concat!(
150        "// This file is @generated by ",
151        env!("CARGO_BIN_NAME"),
152        ".\n",
153        "// It is not intended for manual editing.\n",
154        "\n",
155        "#![allow(unused_variables)]\n",
156        "#![allow(clippy::useless_conversion, clippy::unit_arg)]\n",
157        "\n",
158    )
159    .into()
160}
161
162fn write(path: &Path, contents: TokenStream) -> Result<()> {
163    let mut out = header().into_bytes();
164    out.extend_from_slice(
165        prettyplease::unparse(
166            &syn::parse2(contents.clone())
167                .map_err(|e| format_err!("{e} in:\n---\n{contents}\n---"))?,
168        )
169        .as_bytes(),
170    );
171    if path.is_file() && fs::read(path)? == out {
172        return Ok(());
173    }
174    fs::write(path, out)?;
175    Ok(())
176}