Skip to content

Commit a9f4c64

Browse files
committed
migrate to askama
1 parent 065d816 commit a9f4c64

File tree

6 files changed

+907
-873
lines changed

6 files changed

+907
-873
lines changed

src/convert/_build.rs

Lines changed: 80 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::{borrow::Cow, path::Path};
22

3+
use askama::Template;
4+
35

46
struct Line<S> {
57
pub hut: S,
@@ -30,51 +32,70 @@ impl<T> From<Vec<T>> for Line<T> {
3032
}
3133
}
3234

33-
const CONVERT_GENERAL: &str = r##"
34-
pub fn {from}_to_{to}(value: {From}) -> Option<{To}> {
35+
mod filters {
36+
pub fn pad_right<T: std::fmt::Display>(s: T, _: &dyn askama::Values, len: usize) -> askama::Result<String> {
37+
let mut s = s.to_string();
38+
while s.len() < len {
39+
s.push(' ');
40+
}
41+
Ok(s)
42+
}
43+
}
44+
45+
#[derive(Debug, Clone, askama::Template)]
46+
#[template(ext = "txt", source = r#"// This file is auto-generated. Do not edit manually.
47+
48+
49+
pub fn {{from | lower}}_to_{{to | lower}}(value: {{from}}) -> Option<{{to}}> {
3550
let result = match value {
36-
#( $key => {prefix}$value{suffix}, )#
51+
{% for (k, v) in map -%}
52+
{{ k | pad_right(*k_len) }} => {{prefix}}{{ v | pad_right(*v_len) }}{{suffix}},
53+
{% endfor -%}
3754
_ => return None,
3855
};
3956
Some(result)
4057
}
4158
42-
impl crate::convert::Convert<{From}, {To}> for crate::convert::Converter {
43-
fn convert(value: {From}) -> Option<{To}> {
44-
{from}_to_{to}(value)
59+
impl crate::convert::Convert<{{from}}, {{to}}> for crate::convert::Converter {
60+
fn convert(value: {{from}}) -> Option<{{to}}> {
61+
{{from|lower}}_to_{{to|lower}}(value)
4562
}
4663
}
47-
"##;
48-
49-
fn gen_template(template: &str, from: &str, to: &str, prefix: Option<&str>, suffix: Option<&str>) -> String {
50-
template.replace("{from}", &from.to_lowercase()).replace("{to}", &to.to_lowercase())
51-
.replace("{From}", from).replace("{To}", to)
52-
.replace("{prefix}", prefix.unwrap_or_default()).replace("{suffix}", suffix.unwrap_or_default())
64+
"#)]
65+
pub struct GeneralTemplate<'a> {
66+
pub from: &'a str,
67+
pub to: &'a str,
68+
pub prefix: &'a str,
69+
pub suffix: &'a str,
70+
pub map: Vec<(Cow<'a, str>, Cow<'a, str>)>,
71+
pub k_len: usize,
72+
pub v_len: usize,
5373
}
5474

55-
fn gen_convert<S1: AsRef<str>, S2: AsRef<str>, I: IntoIterator<Item = (S1, S2)>>(template: &str, map: I) -> String {
56-
fn normalize(s: &str) -> Cow<str> {
57-
let s = s.trim_start();
58-
if s.contains('*') {
59-
s.rsplit('*').next().unwrap();
60-
return format!("{}{}", s.trim_end().trim_end_matches('*'), s.rsplit('*').next().unwrap()).into()
75+
impl<'a> GeneralTemplate<'a> {
76+
pub fn create(from: &'a str, to: &'a str, prefix: Option<&'a str>, suffix: Option<&'a str>) -> Self {
77+
let prefix = prefix.unwrap_or("");
78+
let suffix = suffix.unwrap_or("");
79+
Self {
80+
from,
81+
to,
82+
prefix,
83+
suffix,
84+
map: vec![],
85+
k_len: 0,
86+
v_len: 0,
6187
}
62-
s.into()
63-
}
64-
let map = map.into_iter().collect::<Vec<_>>();
65-
// https://rustexp.lpil.uk/
66-
let generated_code = regex::Regex::new(r"\n([ \t]*)#\(([\s\S]*)\)#(\n?)").unwrap().replace_all(template, |caps: &regex::Captures| {
67-
let indent = &caps[1];
68-
let inner = caps[2].trim();
69-
let newline = &caps[3];
70-
let content = map.iter().map(|(key, value)| {
71-
inner.replace("$key", &normalize(key.as_ref())).replace("$value", &normalize(value.as_ref()))
72-
}).collect::<Vec<String>>().join(&format!("{newline}{indent}"));
73-
format!("{newline}{indent}{content}{newline}")
74-
});
75-
format!("// This file is auto-generated. Do not edit manually.\n\n{}", generated_code)
76-
}
88+
}
7789

90+
pub fn build(self, map: Vec<(Cow<'a, str>, Cow<'a, str>)>) -> String {
91+
Self {
92+
k_len: map.iter().map(|(k, _)| k.len()).max().unwrap_or(0),
93+
v_len: map.iter().map(|(_, v)| v.len()).max().unwrap_or(0),
94+
map,
95+
..self
96+
}.render().unwrap()
97+
}
98+
}
7899

79100
#[expect(unused)]
80101
#[derive(Debug, Clone, Copy)]
@@ -123,26 +144,39 @@ impl KeyType {
123144
_ => None
124145
}
125146
}
126-
}
127147

128-
fn is_valid<S: AsRef<str>>(s: S) -> bool {
129-
let s = s.as_ref().trim();
130-
!s.is_empty() && !s.starts_with("n!") && !s.starts_with("na!") && !s.starts_with("todo!") && !s.starts_with("none!")
131-
}
132-
fn kv_is_valid<K: AsRef<str>, V: AsRef<str>>((k, v): &(K, V)) -> bool {
133-
is_valid(k) && is_valid(v) && !k.as_ref().trim().ends_with('*')
148+
pub fn is_valid<S: AsRef<str>>(self, s: S) -> bool {
149+
let s = s.as_ref().trim();
150+
!s.is_empty() && !s.starts_with("n!") && !s.starts_with("na!") && !s.starts_with("todo!") && !s.starts_with("none!")
151+
}
152+
153+
pub fn get_content_unchecked<'a>(self, s: &'a str) -> Cow<'a, str> {
154+
let s = s.trim_start();
155+
Cow::Borrowed(s.trim().trim_end_matches("*"))
156+
}
134157
}
135158

159+
160+
#[derive(Debug, Clone, Copy)]
136161
struct Gen(KeyType, KeyType);
137162

138163
impl Gen {
139-
pub fn build(self, csv: &[Line<&str>]) -> String {
164+
pub fn build_kv<'a>(self, csv: &'a [Line<&'a str>]) -> Vec<(Cow<'a, str>, Cow<'a, str>)> {
165+
let from = self.0;
166+
let to = self.1;
167+
csv.iter().map(|i| (from.get_line(i), to.get_line(i))).filter(|t| self.kv_is_valid(t)).map(|(k, v)| (from.get_content_unchecked(k), to.get_content_unchecked(v))).collect()
168+
}
169+
170+
pub fn kv_is_valid<K: AsRef<str>, V: AsRef<str>>(self, (k, v): &(K, V)) -> bool {
171+
self.0.is_valid(k) && self.1.is_valid(v) && !k.as_ref().trim().ends_with('*')
172+
}
173+
174+
pub fn build_general<'a>(self, csv: &'a [Line<&'a str>]) -> String {
140175
let from = self.0;
141176
let to = self.1;
142-
gen_convert(
143-
&gen_template(CONVERT_GENERAL, from.name(), to.name(), to.as_value_prefix(), to.as_value_suffix()),
144-
csv.iter().map(|i| (from.get_line(i), to.get_line(i))).filter(kv_is_valid)
145-
)
177+
let map = self.build_kv(csv);
178+
GeneralTemplate::create(from.name(), to.name(), to.as_value_prefix(), to.as_value_suffix())
179+
.build(map)
146180
}
147181
}
148182

@@ -187,7 +221,7 @@ pub fn main() {
187221
] {
188222
let (from, to) = tuple;
189223
let filename = format!("generated.{from:?}_to_{to:?}.rs");
190-
let content = Gen(from, to).build(&csv);
224+
let content = Gen(from, to).build_general(&csv);
191225
save_file(format!("{output_path}/{filename}"), content)
192226
.expect("Failed to write generated.rs");
193227
}

0 commit comments

Comments
 (0)