actix_cloud/
response_build.rs

1use anyhow::Result;
2use quote::{format_ident, quote};
3use std::{
4    env,
5    fs::{read_to_string, File},
6    io::Write,
7    path::Path,
8};
9use walkdir::WalkDir;
10use yaml_rust2::YamlLoader;
11
12#[derive(thiserror::Error, Debug)]
13pub enum BuildError {
14    #[error("response file format invalid")]
15    Format,
16
17    #[error("response file name invalid")]
18    File,
19}
20
21/// Generate response file from yml.
22///
23/// This function should be used in `build.rs`.
24/// ```ignore
25/// [build-dependencies]
26/// actix-cloud = { version = "xx", features = ["response-build"] }
27/// ```
28///
29/// ```no_run
30/// use actix_cloud::response_build::generate_response;
31///
32/// generate_response("", "response", "response.rs").unwrap();
33/// ```
34pub fn generate_response(import_prefix: &str, input: &str, output: &str) -> Result<()> {
35    let outfile = Path::new(&env::var("OUT_DIR")?).join(output);
36    let mut output = File::create(&outfile)?;
37    writeln!(
38        output,
39        "use {}actix_cloud::response::ResponseCodeTrait;",
40        import_prefix
41    )?;
42    for entry in WalkDir::new(input) {
43        let entry = entry?;
44        if entry.file_type().is_file() {
45            let file = read_to_string(entry.path())?;
46            let yaml = YamlLoader::load_from_str(&file)?;
47            let doc = &yaml[0];
48            let mut name_vec = Vec::new();
49            let mut code_vec = Vec::new();
50            let mut message_vec = Vec::new();
51            for (name, field) in doc.as_hash().ok_or(BuildError::Format)? {
52                name_vec.push(format_ident!(
53                    "{}",
54                    name.as_str().ok_or(BuildError::Format)?
55                ));
56                code_vec.push(field["code"].as_i64().ok_or(BuildError::Format)?);
57                message_vec.push(field["message"].as_str().ok_or(BuildError::Format)?);
58            }
59
60            let file_stem = entry.path().file_stem().ok_or(BuildError::File)?;
61            let mut s = file_stem.to_str().ok_or(BuildError::File)?.to_owned();
62            let s = s.remove(0).to_uppercase().to_string() + &s;
63            let enum_name = format_ident!("{}Response", s);
64            let mut enum_code = Vec::new();
65            for i in 0..code_vec.len() {
66                let s = &name_vec[i];
67                let c = code_vec[i];
68                enum_code.push(quote! {#enum_name::#s => #c});
69            }
70            let mut enum_message = Vec::new();
71            for i in 0..code_vec.len() {
72                let s = &name_vec[i];
73                let c = message_vec[i];
74                enum_message.push(quote! {#enum_name::#s => #c});
75            }
76            let content = quote! {
77                pub enum #enum_name {
78                    #(#name_vec),*
79                }
80
81                impl ResponseCodeTrait for #enum_name {
82                    fn code(&self) -> i64 {
83                        match self {
84                            #(#enum_code),*
85                        }
86                    }
87
88                    fn message(&self) -> &'static str {
89                        match self {
90                            #(#enum_message),*
91                        }
92                    }
93                }
94            };
95
96            write!(
97                output,
98                "{}",
99                prettyplease::unparse(&syn::parse_file(&content.to_string())?)
100            )?;
101        }
102    }
103    Ok(())
104}