Skip to content

Commit a8f77aa

Browse files
authored
Codegen json (#614)
* Made codegen data driven. * Set template directory in config and move templates out of codegen tool.
1 parent 9ae1234 commit a8f77aa

1 file changed

Lines changed: 138 additions & 8 deletions

File tree

src/main.rs

Lines changed: 138 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,137 @@
1-
mod outputs;
2-
31
use anyhow::{bail, Context};
42
use clap::{arg, command};
53
use rustfmt_wrapper::rustfmt;
4+
use serde::{Deserialize, Serialize};
5+
use std::collections::BTreeMap;
66
use std::path::Path;
7-
use tera::{from_value, to_value, Value};
7+
use tera::{from_value, to_value};
88

9-
use outputs::build_output_pairs;
9+
// use outputs::build_output_pairs;
1010

1111
const GLAM_ROOT: &str = "..";
12+
const CONFIG_FILE: &str = "codegen.json";
13+
14+
#[derive(Serialize, Deserialize)]
15+
struct Config {
16+
version: u32,
17+
template_root: String,
18+
templates: BTreeMap<String, Template>,
19+
}
20+
21+
#[derive(Default, Debug, Serialize, Deserialize)]
22+
struct Template {
23+
properties: BTreeMap<String, Option<serde_json::Value>>,
24+
outputs: BTreeMap<String, Output>,
25+
}
26+
27+
#[derive(Default, Debug, Serialize, Deserialize)]
28+
struct Output {
29+
properties: BTreeMap<String, serde_json::Value>,
30+
}
31+
32+
impl Config {
33+
fn from_file<P: AsRef<Path>>(path: P) -> anyhow::Result<Config> {
34+
let file = std::fs::File::open(path)?;
35+
let reader = std::io::BufReader::new(file);
36+
let config = serde_json::from_reader(reader)?;
37+
Ok(config)
38+
}
39+
40+
fn build_output_pairs(&self) -> anyhow::Result<BTreeMap<String, tera::Context>> {
41+
match self.version {
42+
1 => self.build_output_pairs_v1(),
43+
_ => Err(anyhow::Error::msg("Unexpected config file version")),
44+
}
45+
}
46+
47+
fn build_output_pairs_v1(&self) -> anyhow::Result<BTreeMap<String, tera::Context>> {
48+
let mut output_pairs = BTreeMap::new();
49+
for (template_path, template) in self.templates.iter() {
50+
for (output_path, output) in template.outputs.iter() {
51+
let mut context = tera::Context::new();
52+
context.insert("template_path", template_path);
53+
for (prop_key, prop_value) in template.properties.iter() {
54+
if let Some(prop_override) = output.properties.get(prop_key) {
55+
context.insert(prop_key, prop_override);
56+
} else {
57+
// TODO: error message
58+
if let Some(prop_value) = prop_value {
59+
context.insert(prop_key, prop_value);
60+
} else {
61+
return Err(anyhow::Error::msg("Missing property override"));
62+
}
63+
}
64+
}
65+
output_pairs.insert(output_path.clone(), context);
66+
}
67+
}
68+
Ok(output_pairs)
69+
}
70+
71+
// fn from(output_pairs: std::collections::HashMap<&str, tera::Context>) -> Option<Config> {
72+
// let mut config = Config::new();
73+
// for (output_path, tera_context) in output_pairs.into_iter() {
74+
// let json_context = tera_context.into_json();
75+
// let context_map = json_context.as_object()?;
76+
// let template_path = context_map.get("template_path")?.as_str()?;
77+
// if !config.templates.contains_key(template_path) {
78+
// let mut template = Template::default();
79+
// for (property, value) in context_map.iter() {
80+
// if property == "template_path" {
81+
// continue;
82+
// }
83+
// if value.is_boolean() {
84+
// template.properties.insert(
85+
// property.clone(), Some(serde_json::Value::Bool(false))
86+
// );
87+
// } else if value.is_i64() {
88+
// template
89+
// .properties
90+
// .insert(property.clone(), None);
91+
// } else if value.is_string() {
92+
// template
93+
// .properties
94+
// .insert(property.clone(), None);
95+
// } else {
96+
// unimplemented!();
97+
// }
98+
// }
99+
// config.templates.insert(template_path.to_string(), template);
100+
// }
101+
// let mut output = Output::default();
102+
// for (property, value) in context_map.iter() {
103+
// if property == "template_path" {
104+
// continue;
105+
// }
106+
// if let Some(bool_value) = value.as_bool() {
107+
// if bool_value {
108+
// output
109+
// .properties
110+
// .insert(property.clone(), serde_json::Value::Bool(bool_value));
111+
// }
112+
// } else if let Some(i64_value) = value.as_i64() {
113+
// output.properties.insert(
114+
// property.clone(),
115+
// serde_json::Value::Number(i64_value.into()),
116+
// );
117+
// } else if let Some(str_value) = value.as_str() {
118+
// output.properties.insert(
119+
// property.clone(),
120+
// serde_json::Value::String(str_value.to_string()),
121+
// );
122+
// } else {
123+
// unimplemented!();
124+
// }
125+
// }
126+
// config
127+
// .templates
128+
// .get_mut(template_path)?
129+
// .outputs
130+
// .insert(output_path.to_string(), output);
131+
// }
132+
// Some(config)
133+
// }
134+
}
12135

13136
fn is_modified(repo: &git2::Repository, output_path: &str) -> anyhow::Result<bool> {
14137
match repo.status_file(Path::new(output_path)) {
@@ -61,10 +184,17 @@ fn main() -> anyhow::Result<()> {
61184
} else {
62185
None
63186
};
64-
let mut tera = tera::Tera::new("templates/**/*.rs.tera").context("tera parsing error(s)")?;
187+
188+
let config = Config::from_file(Path::new(GLAM_ROOT).join(CONFIG_FILE))?;
189+
190+
let template_path = Path::new(GLAM_ROOT)
191+
.join(&config.template_root)
192+
.join("**/*.rs.tera");
193+
let mut tera =
194+
tera::Tera::new(template_path.to_str().unwrap()).context("tera parsing error(s)")?;
65195
tera.register_filter(
66196
"snake_case",
67-
|value: &Value, _: &_| -> tera::Result<Value> {
197+
|value: &tera::Value, _: &_| -> tera::Result<tera::Value> {
68198
let input = from_value::<String>(value.clone())?;
69199
let mut iter = input.chars();
70200

@@ -84,11 +214,11 @@ fn main() -> anyhow::Result<()> {
84214
},
85215
);
86216

217+
let output_pairs = config.build_output_pairs()?;
218+
87219
let repo = git2::Repository::open(GLAM_ROOT).context("failed to open git repo")?;
88220
let workdir = repo.workdir().unwrap();
89221

90-
let output_pairs = build_output_pairs();
91-
92222
let mut output_paths = vec![];
93223
if let Some(glob) = glob {
94224
for k in output_pairs.keys() {

0 commit comments

Comments
 (0)