1use std::{
2 collections::BTreeMap,
3 fs::{File, read_to_string},
4 io::Write,
5 path::{Path, PathBuf},
6};
7
8use configinator::{AppConfig, TemplateRefFieldBind};
9use serde_json::Value;
10
11pub async fn download(
12 path: &String,
13 url: &String,
14 dest: &String,
15) -> Result<(), Box<dyn std::error::Error>> {
16 let path = Path::new(path);
17
18 let config_str = std::fs::read_to_string(path.join("./ordinary.json"))?;
19 let config: AppConfig = serde_json::from_str(&config_str).unwrap();
20
21 let mut indexed_vals: BTreeMap<String, Vec<(String, Value)>> = BTreeMap::new();
23
24 if let Some(content) = config.content {
25 let mut def_map = BTreeMap::new();
26
27 for def in content.definitions {
28 def_map.insert(def.name.clone(), def);
29 }
30
31 let objects_json = read_to_string(path.join(&content.file_path))?;
32
33 let objects: Vec<notatype::ContentObject> = serde_json::from_str(&objects_json)?;
34
35 for object in objects {
36 if let Some(def) = def_map.get(&object.instance_of) {
37 for def_field in &def.fields {
38 if def_field.indexed.is_some() && def_field.indexed.unwrap() == true {
39 for obj_field in &object.fields {
40 if def_field.name == obj_field.name {
41 if let Some(vec) = indexed_vals.get_mut(&def.name) {
42 vec.push((obj_field.name.clone(), obj_field.value.clone()));
43 } else {
44 indexed_vals.insert(
45 def.name.clone(),
46 vec![(obj_field.name.clone(), obj_field.value.clone())],
47 );
48 }
49 }
50 }
51 }
52 }
53 }
54 }
55 }
56
57 if let Some(templates) = config.templates {
58 let client = reqwest::Client::new();
59
60 for template_config in templates {
61 if template_config.models.is_some() {
62 println!(
63 "skipping template {} because models cannot be statically generated.",
64 template_config.name
65 );
66 continue;
67 }
68
69 let mut request_routes = vec![];
70
71 if let Some(template_content) = template_config.content {
72 let mut has_bindings = false;
73
74 for content_ref in template_content {
75 for ref_field in content_ref.fields {
76 if let Some(binding) = ref_field.bind {
77 match binding {
78 TemplateRefFieldBind::Segment {
79 name,
80 expression: _,
81 } => {
82 has_bindings = true;
83
84 if let Some(vals) = indexed_vals.get(&content_ref.name) {
85 for (field_name, val) in vals {
86 if field_name == &ref_field.name {
87 if let Some(val) = val.as_str() {
88 request_routes.push(
89 template_config
90 .route
91 .replace(&format!("{{{}}}", name), val),
92 );
93 }
94 }
95 }
96 }
97 }
98 TemplateRefFieldBind::Token {
99 field: _,
100 expression: _,
101 } => {
102 println!(
103 "cannot generate for templates whose content binds to token fields"
104 )
105 }
106 }
107 }
108 }
109 }
110
111 if !has_bindings {
112 request_routes.push(template_config.route);
113 }
114 }
115
116 println!("generating files for template \"{}\"", template_config.name);
117
118 for request_route in request_routes {
123 println!("route: \"{}\"", request_route);
124
125 let res = client
126 .get(format!("{}{}", url.clone(), request_route))
127 .send()
128 .await?
129 .bytes()
130 .await?;
131
132 let mut path: PathBuf = Path::new(&format!("{dest}{}", request_route)).into();
133
134 path.set_extension("");
135
136 let file_path = if request_route.ends_with("/") {
137 std::fs::create_dir_all(&path)?;
138
139 match template_config.mime.as_str() {
140 "text/html" => path.join("index.html"),
141 "text/xml" => path.join("index.xml"),
142 "application/rss+xml" => path.join("feed.xml"),
143 "text/plain" => path.join("index.txt"),
144 _ => path.join("unknown"),
145 }
146 } else {
147 let file_name = path.file_name().unwrap().to_str().unwrap();
148 let path = path.parent().unwrap();
149
150 std::fs::create_dir_all(path)?;
151
152 match template_config.mime.as_str() {
153 "text/html" => path.join(format!("{}.html", file_name)),
154 "text/xml" => path.join(format!("{}.xml", file_name)),
155 "application/rss+xml" => path.join(format!("{}.xml", file_name)),
156 "text/plain" => path.join(format!("{}.txt", file_name)),
157 _ => path.join(file_name),
158 }
159 };
160
161 println!("output: {:?}", file_path);
162
163 let mut file = File::create(file_path)?;
164 file.write_all(&res)?;
165 }
166 }
167 }
168
169 Ok(())
170}