massbit_sol/generator/
mod.rs

1pub mod graphql;
2pub mod handler;
3pub mod helper;
4pub mod indexer_lib;
5pub mod indexer_mapping;
6pub mod indexer_mod;
7pub mod indexer_setting;
8pub mod instruction;
9//pub mod model;
10
11use crate::generator::indexer_mod::INDEXER_MOD;
12use crate::schema::Schema;
13use handlebars::Handlebars;
14use indexer_lib::INDEXER_LIB;
15use indexer_mapping::INDEXER_MAPPING;
16use indexer_setting::*;
17use minifier::json::minify;
18
19use serde_json::json;
20use serde_json::Value;
21use std::collections::BTreeMap;
22use std::fs;
23use std::io;
24
25#[derive(Debug)]
26#[must_use]
27pub struct Generator<'a> {
28    pub structure_path: &'a str,
29    pub config_path: &'a str,
30    /// The output dir
31    pub output_dir: &'a str,
32    pub schema: Option<Schema>,
33    pub config: Option<Value>,
34    pub definitions: BTreeMap<String, Schema>,
35}
36
37impl<'a> Generator<'a> {
38    pub fn builder() -> GeneratorBuilder<'a> {
39        GeneratorBuilder::default()
40    }
41
42    pub fn generate(&self) -> Result<(), io::Error> {
43        let ref_schema = self.schema.as_ref();
44        if let Some(schema) = ref_schema {
45            let config = self.config.clone().unwrap();
46            let name = &config["name"].as_str().unwrap_or_default();
47            let contract_address = &config["contract_address"].as_str().unwrap_or_default();
48            let start_block = &config["start_block"].as_i64().unwrap_or_default();
49
50            //Instruction
51            let data = self.generate_instruction(schema);
52            self.write_to_file(
53                &format!("{}/{}", self.output_dir, "src/generated/instruction.rs"),
54                &data,
55                true,
56            )?;
57            //Instruction handler
58            let data = self.generate_handler(schema);
59            self.write_to_file(
60                &format!("{}/{}", self.output_dir, "src/generated/handler.rs"),
61                &data,
62                true,
63            )?;
64            //Models
65            // let data = schema.gen_models();
66            // self.write_to_file(
67            //     &format!("{}/{}", self.output_dir, "src/models.rs"),
68            //     &data,
69            //     true,
70            // )?;
71
72            //libs
73            let lib_content = &Handlebars::new()
74                .render_template(
75                    INDEXER_LIB,
76                    &json!({
77                        "address": contract_address,
78                    }),
79                )
80                .unwrap();
81            self.write_to_file(
82                &format!("{}/{}", self.output_dir, "src/lib.rs"),
83                &lib_content,
84                true,
85            )?;
86            //Mapping
87            self.write_to_file(
88                &format!("{}/{}", self.output_dir, "src/mapping.rs"),
89                &format!("{}", INDEXER_MAPPING),
90                true,
91            )?;
92            //mod.rs
93            self.write_to_file(
94                &format!("{}/{}", self.output_dir, "src/generated/mod.rs"),
95                &format!("{}", INDEXER_MOD),
96                true,
97            )?;
98            //subgraph.yaml
99
100            self.write_to_file(
101                &format!("{}/{}", self.output_dir, "src/subgraph.yaml"),
102                &Handlebars::new()
103                    .render_template(
104                        INDEXER_YAML,
105                        &json!({
106                            "name": name,
107                            "address": contract_address,
108                            "start_block": start_block
109                        }),
110                    )
111                    .unwrap(),
112                true,
113            )?;
114            //Schema graphql
115            let data = self.generate_graphql_schema(schema);
116            self.write_to_file(
117                &format!("{}/{}", self.output_dir, "src/schema.graphql"),
118                &data,
119                false,
120            )?;
121            //Cargo toml
122            self.write_to_file(
123                &format!("{}/{}", self.output_dir, "Cargo.toml"),
124                &format!("{}", CARGO_TOML),
125                false,
126            )?;
127        };
128        Ok(())
129    }
130    //pub fn generate_to_file<P: ?Sized + AsRef<Path>>(&self, output_file: &'b P) -> io::Result<()> {
131
132    // pub fn write_to_file<P: ?Sized + AsRef<Path>>(
133    //     &self,
134    //     output_path: &P,
135    //     content: &String,
136    //     apply_format: bool,
137    // ) -> io::Result<()> {
138    pub fn write_to_file(
139        &self,
140        output_path: &String,
141        content: &String,
142        apply_format: bool,
143    ) -> io::Result<()> {
144        // Check and create parent directory
145        let path = std::path::Path::new(&output_path);
146        let prefix = path.parent().unwrap();
147        std::fs::create_dir_all(prefix).unwrap();
148
149        // Write the file
150        match fs::write(output_path, content) {
151            Ok(_) => {
152                if apply_format {
153                    use std::process::Command;
154                    let _ = Command::new("rustfmt").arg(output_path).output();
155                }
156                log::info!("Write content to file {:?} successfully", &output_path);
157                Ok(())
158            }
159            e @ Err(_) => {
160                log::info!("Write content to file {:?} fail. {:?}", &output_path, &e);
161                e
162            }
163        }
164    }
165}
166
167pub struct GeneratorBuilder<'a> {
168    inner: Generator<'a>,
169}
170
171impl<'a> Default for GeneratorBuilder<'a> {
172    fn default() -> Self {
173        Self {
174            inner: Generator {
175                structure_path: "",
176                config_path: "",
177                output_dir: "",
178                schema: None,
179                config: None,
180                definitions: BTreeMap::default(),
181            },
182        }
183    }
184}
185
186impl<'a> GeneratorBuilder<'a> {
187    pub fn with_structure_path(mut self, path: &'a str) -> Self {
188        self.inner.structure_path = path;
189        let json = std::fs::read_to_string(path)
190            .unwrap_or_else(|err| panic!("Unable to read `{}`: {}", path, err));
191
192        let schema: Schema = serde_json::from_str(&json)
193            .unwrap_or_else(|err| panic!("Cannot parse `{}` as JSON: {}", path, err));
194        self.collect_definitions(&schema);
195        self.inner.schema = Some(schema);
196        self
197    }
198    pub fn with_config_path(mut self, path: &'a str) -> Self {
199        self.inner.config_path = path;
200        let json = std::fs::read_to_string(path)
201            .unwrap_or_else(|err| panic!("Unable to read `{}`: {}", path, err));
202        let config: Value = serde_json::from_str(&minify(&json))
203            .unwrap_or_else(|err| panic!("Cannot parse `{}` as JSON: {}", path, err));
204        //println!("config: {:?}", &config);
205        self.inner.config = Some(config);
206        self
207    }
208    pub fn with_output_dir(mut self, output_dir: &'a str) -> Self {
209        self.inner.output_dir = output_dir;
210        self
211    }
212    fn collect_definitions(&mut self, schema: &Schema) {
213        schema.definitions.iter().for_each(|(name, schema)| {
214            self.inner.definitions.insert(name.clone(), schema.clone());
215            self.collect_definitions(schema);
216        });
217    }
218    pub fn build(self) -> Generator<'a> {
219        self.inner
220    }
221}