moar/
lib.rs

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    // def name, field name, field value
22    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 {
119            // todo: create the matrix of route permutations for multi-slug
120            // }
121
122            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}