1mod filter;
2mod function;
3pub mod templates;
4
5use std::sync::Mutex;
6
7use openapiv3::OpenAPI;
8
9pub type JpUnit = (String, minijinja::Value);
10pub type JpList = Vec<JpUnit>;
11
12pub fn environment(
13 mut value: OpenAPI,
14) -> Result<minijinja::Environment<'static>, minijinja::Error> {
15 if value.servers.is_empty() {
17 value.servers.push(openapiv3::Server {
18 url: "/api".to_string(),
19 description: Some("Default server added by mandolin".to_string()),
20 ..Default::default()
21 });
22 }
23 let value = minijinja::Value::from_serialize(&value);
25 let value_jp = function::jp_list(&value, "#");
26 let mut env = minijinja::Environment::new();
27 for [k, v] in templates::TEMPLATES {
28 env.add_template(k, v)?
29 }
30 {
31 let ls = value_jp.clone();
32 env.add_filter("include_ref", move |value: minijinja::Value| {
33 filter::include_ref(&ls, value)
34 });
35 let ls = value_jp.clone();
36 env.add_filter("include_pointer", move |value: &str| {
37 filter::include_pointer(&ls, value)
38 });
39 }
40 env.add_filter("decode", filter::decode);
41 env.add_filter("encode", filter::encode);
42 env.add_filter("split", filter::split);
43 env.add_filter("re_replace", filter::re_replace);
44 env.add_filter("to_pascal_case", filter::to_pascal_case);
45 env.add_filter("to_snake_case", filter::to_snake_case);
46 env.add_filter("to_camel_case", filter::to_camel_case);
47 {
48 let ls = value_jp.clone();
49 env.add_function("ls", move |value: &str| {
50 function::ls(&ls, function::LsMode::LS((value, false)))
51 });
52 let ls = value_jp.clone();
53 env.add_function("ls_recursive", move |value: &str| {
54 function::ls(&ls, function::LsMode::LS((value, true)))
55 });
56 let ls = value_jp.clone();
57 env.add_function("ls_operation", move || {
58 function::ls(&ls, function::LsMode::OPERATION)
59 });
60 let ls = value_jp.clone();
61 env.add_function("ls_schema", move || {
62 function::ls(&ls, function::LsMode::SCHEMA)
63 });
64 }
65 let queue = std::sync::Arc::new(Mutex::new(function::NestedSchema::default()));
66 {
67 let q = std::sync::Arc::clone(&queue);
68 env.add_function("schema_drain", move || {
69 function::schema_drain(&mut q.lock().unwrap())
70 });
71 let q = std::sync::Arc::clone(&queue);
72 env.add_function(
73 "schema_push",
74 move |pointer: &str, content: Option<&str>| {
75 function::schema_push(&mut q.lock().unwrap(), pointer, content)
76 },
77 );
78 }
79 Ok(env)
80}
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use std::collections::HashMap;
85 use std::fs;
86 use std::fs::File;
87 use std::io::Write;
88 use std::path::Path;
89
90 fn api_map() -> HashMap<String, OpenAPI> {
91 fs::read_dir(&Path::new(".").join("openapi"))
92 .unwrap()
93 .filter_map(Result::ok)
94 .filter_map(|entry| {
95 let path = entry.path();
96 let extension = path.extension()?.to_str()?;
97 let name = entry.file_name().to_str()?.to_string();
98
99 match extension {
100 "yaml" | "yml" => {
101 let file = File::open(&path).ok()?;
102 let reader = std::io::BufReader::new(file);
103 let openapi = serde_yaml::from_reader(reader).ok()?;
104 Some((name, openapi))
105 }
106 "json" => {
107 let file = File::open(&path).ok()?;
108 let reader = std::io::BufReader::new(file);
109 let openapi = serde_json::from_reader(reader).ok()?;
110 Some((name, openapi))
111 }
112 _ => None,
113 }
114 })
115 .collect()
116 }
117 fn write<P: AsRef<Path>, S: AsRef<str>>(path: P, content: S) -> std::io::Result<()> {
118 let path = path.as_ref();
119 if let Some(parent) = path.parent() {
121 fs::create_dir_all(parent)?;
122 }
123 let mut writer = std::io::BufWriter::new(File::create(path)?);
124 writeln!(writer, "{}", content.as_ref())
125 }
126 fn render_target(template: &str, extention: &str) {
127 for (k, input_api) in api_map() {
128 println!("render start: {k}");
129 let env = environment(input_api).unwrap();
130 let template = env.get_template(template).unwrap();
131 let output = template.render(0).unwrap();
132 write(format!("out/{k}.{extention}"), output.as_str()).unwrap();
133 println!("render complete: {k}");
134 }
135 }
136 #[test]
137 fn render() {
138 render_target("TYPESCRIPT_HONO", "ts");
139 render_target("RUST_AXUM", "rs");
140 render_target("GO_SERVER", "go");
141 }
142}