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;
9use 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 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 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 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 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 self.write_to_file(
88 &format!("{}/{}", self.output_dir, "src/mapping.rs"),
89 &format!("{}", INDEXER_MAPPING),
90 true,
91 )?;
92 self.write_to_file(
94 &format!("{}/{}", self.output_dir, "src/generated/mod.rs"),
95 &format!("{}", INDEXER_MOD),
96 true,
97 )?;
98 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 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 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 write_to_file(
139 &self,
140 output_path: &String,
141 content: &String,
142 apply_format: bool,
143 ) -> io::Result<()> {
144 let path = std::path::Path::new(&output_path);
146 let prefix = path.parent().unwrap();
147 std::fs::create_dir_all(prefix).unwrap();
148
149 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 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}