codes_common/build/
mod.rs

1/*!
2One-line description.
3
4More detailed description, with
5
6# Example
7
8YYYYY
9
10*/
11
12use std::{collections::BTreeMap, env, fmt::Debug, fs::File, path::Path};
13use tera::{Map, Value};
14
15// ------------------------------------------------------------------------------------------------
16// Public Macros
17// ------------------------------------------------------------------------------------------------
18
19// ------------------------------------------------------------------------------------------------
20// Public Types
21// ------------------------------------------------------------------------------------------------
22
23pub const DEFAULT_NUMERIC_CODE_TYPE: &str = "u16";
24
25pub const DEFAULT_DATA_DIR: &str = "data";
26
27pub const DEFAULT_TEMPLATE_DIR: &str = "templates";
28
29pub type DataRow = Map<String, Value>;
30
31pub type DataMap = BTreeMap<String, Map<String, Value>>;
32
33pub trait Data {
34    fn new(type_name: &'static str) -> Self
35    where
36        Self: Sized;
37    fn new_with_inner(type_name: &'static str, inner_type_name: &'static str) -> Self
38    where
39        Self: Sized;
40    fn type_name(&self) -> &'static str;
41    fn inner_type_name(&self) -> Option<&'static str>;
42    fn has_inner_type(&self) -> bool {
43        self.inner_type_name().is_some()
44    }
45    fn all_ids(&self) -> Vec<&String> {
46        self.rows().keys().collect()
47    }
48    fn all_ids_sorted(&self) -> Value {
49        let mut all_ids = self.all_ids();
50        all_ids.sort();
51        all_ids.dedup();
52        Value::Array(
53            all_ids
54                .into_iter()
55                .map(|s| Value::String(s.into()))
56                .collect(),
57        )
58    }
59    fn rows(&self) -> &DataMap;
60    fn rows_mut(&mut self) -> &mut DataMap;
61    fn into_rows(self) -> DataMap;
62    fn contains(&self, id: &str) -> bool {
63        self.rows().contains_key(id)
64    }
65    fn get(&self, id: &str) -> Option<&DataRow> {
66        self.rows().get(id)
67    }
68    fn get_mut(&mut self, id: &str) -> Option<&mut DataRow> {
69        self.rows_mut().get_mut(id)
70    }
71    fn insert_row(&mut self, id: &str, row: DataRow) {
72        self.rows_mut().insert(id.to_string(), row);
73    }
74    fn insert_row_value(&mut self, id: &str, key: &str, value: Value) {
75        let row = self.get_mut(id).unwrap();
76        row.insert(key.to_string(), value);
77    }
78}
79
80#[derive(Debug, Default)]
81pub struct SimpleData {
82    type_name: &'static str,
83    inner_type_name: Option<&'static str>,
84    rows: DataMap,
85}
86
87// ------------------------------------------------------------------------------------------------
88// Public Functions
89// ------------------------------------------------------------------------------------------------
90
91pub fn process<T, I, P, F, R>(
92    init: I,
93    process_data: P,
94    finalize: F,
95    render: R,
96) -> Result<(), Box<dyn std::error::Error>>
97where
98    I: FnOnce() -> Result<T, Box<dyn std::error::Error>>,
99    P: FnOnce(T) -> Result<T, Box<dyn std::error::Error>>,
100    F: FnOnce(T) -> Result<tera::Context, Box<dyn std::error::Error>>,
101    R: FnOnce(tera::Context) -> Result<tera::Context, Box<dyn std::error::Error>>,
102{
103    init()
104        .and_then(process_data)
105        .and_then(finalize)
106        .and_then(render)?;
107    Ok(())
108}
109
110pub fn default_init<T>() -> Result<T, Box<dyn std::error::Error>>
111where
112    T: Default,
113{
114    Ok(Default::default())
115}
116
117pub fn default_finalize<T>(data: T) -> Result<tera::Context, Box<dyn std::error::Error>>
118where
119    T: Into<tera::Context>,
120{
121    Ok(data.into())
122}
123
124pub fn default_finalize_for<T>(data: T) -> Result<tera::Context, Box<dyn std::error::Error>>
125where
126    T: Data,
127{
128    let mut ctx = tera::Context::new();
129
130    ctx.insert("type_name", &Value::String(data.type_name().into()));
131
132    if let Some(inner_type_name) = data.inner_type_name() {
133        ctx.insert("inner_type_name", &Value::String(inner_type_name.into()));
134    }
135
136    ctx.insert("all_ids", &data.all_ids_sorted());
137
138    ctx.insert(
139        "codes",
140        &Value::Object(
141            data.into_rows()
142                .into_iter()
143                .map(|(key, value)| (key, Value::Object(value)))
144                .collect(),
145        ),
146    );
147
148    Ok(ctx)
149}
150
151#[inline]
152pub fn input_file_name(name: &str) -> String {
153    let file_name = format!("{}/{}", DEFAULT_DATA_DIR, name);
154    rerun_if_changed(&file_name);
155    file_name
156}
157
158#[inline]
159pub fn rerun_if_changed(file_name: &str) {
160    println!("cargo:rerun-if-changed={}", file_name);
161}
162
163#[inline]
164pub fn rerun_if_template_changed(file_name: &str) {
165    println!(
166        "cargo:rerun-if-changed={}/{}",
167        DEFAULT_TEMPLATE_DIR, file_name
168    );
169}
170
171pub fn make_default_renderer<S1, S2>(
172    template_name: S1,
173    generated_file_name: S2,
174) -> impl Fn(tera::Context) -> Result<tera::Context, Box<dyn std::error::Error>>
175where
176    S1: Into<String>,
177    S2: Into<String>,
178{
179    let template_name = template_name.into();
180    let generated_file_name = generated_file_name.into();
181    move |ctx: tera::Context| -> Result<tera::Context, Box<dyn std::error::Error>> {
182        let output_dir: String = env::var("OUT_DIR").unwrap();
183        let file_name = Path::new(&output_dir).join(&generated_file_name);
184
185        rerun_if_template_changed(&template_name);
186
187        let tera = tera::Tera::new(&format!("{}/*._rs", DEFAULT_TEMPLATE_DIR))?;
188
189        let file = File::create(file_name)?;
190        tera.render_to(&template_name, &ctx, file)?;
191
192        Ok(ctx)
193    }
194}
195
196// ------------------------------------------------------------------------------------------------
197// Private Types
198// ------------------------------------------------------------------------------------------------
199
200// ------------------------------------------------------------------------------------------------
201// Implementations
202// ------------------------------------------------------------------------------------------------
203
204impl Data for SimpleData {
205    fn new(type_name: &'static str) -> Self
206    where
207        Self: Sized,
208    {
209        Self {
210            type_name,
211            inner_type_name: None,
212            rows: Default::default(),
213        }
214    }
215    fn new_with_inner(type_name: &'static str, inner_type_name: &'static str) -> Self
216    where
217        Self: Sized,
218    {
219        Self {
220            type_name,
221            inner_type_name: Some(inner_type_name),
222            rows: Default::default(),
223        }
224    }
225
226    fn type_name(&self) -> &'static str {
227        self.type_name
228    }
229
230    fn inner_type_name(&self) -> Option<&'static str> {
231        self.inner_type_name
232    }
233
234    fn rows(&self) -> &BTreeMap<String, Map<String, Value>> {
235        &self.rows
236    }
237
238    fn rows_mut(&mut self) -> &mut BTreeMap<String, Map<String, Value>> {
239        &mut self.rows
240    }
241
242    fn into_rows(self) -> BTreeMap<String, Map<String, Value>> {
243        self.rows
244    }
245}
246
247impl SimpleData {
248    pub fn retain<F>(&mut self, f: F)
249    where
250        F: FnMut(&String, &mut Map<String, Value>) -> bool,
251    {
252        self.rows.retain(f);
253    }
254}
255
256// ------------------------------------------------------------------------------------------------
257// Private Functions
258// ------------------------------------------------------------------------------------------------
259
260// ------------------------------------------------------------------------------------------------
261// Modules
262// ------------------------------------------------------------------------------------------------
263
264#[cfg(feature = "csv_tools")]
265#[macro_use]
266pub mod csv;