1use cirru_edn::Edn;
2use cirru_parser::Cirru;
3use std::collections::hash_map::HashMap;
4use std::collections::hash_set::HashSet;
5use std::path::Path;
6use std::sync::Arc;
7
8#[derive(Debug, Clone, PartialEq)]
9pub struct SnapshotConfigs {
10 pub init_fn: Arc<str>,
11 pub reload_fn: Arc<str>,
12 pub modules: Vec<Arc<str>>,
13 pub version: Arc<str>,
14}
15
16#[derive(Debug, Clone, PartialEq)]
17pub struct FileInSnapShot {
18 pub ns: Cirru,
19 pub defs: HashMap<Arc<str>, Cirru>,
20}
21
22#[derive(Debug, Clone, PartialEq)]
23pub struct Snapshot {
24 pub package: Arc<str>,
25 pub configs: SnapshotConfigs,
26 pub entries: HashMap<Arc<str>, SnapshotConfigs>,
27 pub files: HashMap<Arc<str>, FileInSnapShot>,
28}
29
30fn load_configs(data: &Edn) -> Result<SnapshotConfigs, String> {
31 let c = SnapshotConfigs {
32 init_fn: match data.map_get("init-fn")?.read_str() {
33 Ok(v) => (*v).into(),
34 Err(e) => return Err(format!("failed to load init-fn from: {}", e)),
35 },
36 reload_fn: match data.map_get("reload-fn")?.read_str() {
37 Ok(v) => (*v).into(),
38 Err(e) => return Err(format!("failed to load reload-fn from: {}", e)),
39 },
40 version: match data.map_get("version")? {
41 Edn::Nil => "".into(),
42 x => match x.read_str() {
43 Ok(v) => (*v).into(),
44 Err(e) => return Err(format!("failed to load version, {}", e)),
45 },
46 },
47 modules: match data.map_get("modules")? {
48 Edn::Nil => vec![],
49 x => load_modules(&x)?,
50 },
51 };
52 Ok(c)
53}
54
55fn load_modules(data: &Edn) -> Result<Vec<Arc<str>>, String> {
56 match data.read_list() {
57 Ok(xs) => {
58 let mut ys: Vec<Arc<str>> = Vec::with_capacity(xs.len());
59 for x in xs {
60 ys.push(x.read_str()?.into())
61 }
62 Ok(ys)
63 }
64 Err(e) => Err(format!("failed to load modules, {}", e)),
65 }
66}
67
68fn load_file_info(data: &Edn) -> Result<FileInSnapShot, String> {
69 let ns_code = data.map_get("ns")?.read_quoted_cirru()?;
70 let defs = data.map_get("defs")?.read_map().map_err(|e| format!("failed get `defs`:{}", e))?;
71 let mut defs_info: HashMap<Arc<str>, Cirru> = HashMap::with_capacity(defs.len());
72 for (k, v) in defs {
73 let var = k.read_str()?;
74 let def_code = v.read_quoted_cirru()?;
75 defs_info.insert((*var).into(), def_code);
76 }
77 let file = FileInSnapShot {
78 ns: ns_code,
79 defs: defs_info,
80 };
81 Ok(file)
82}
83
84fn load_files(data: &Edn) -> Result<HashMap<Arc<str>, FileInSnapShot>, String> {
85 let xs = data.read_map().map_err(|e| format!("failed loading files, {}", e))?;
86 let mut ys: HashMap<Arc<str>, FileInSnapShot> = HashMap::with_capacity(xs.len());
87 for (k, v) in xs {
88 let key = k.read_str()?;
89 let file = load_file_info(&v)?;
90 ys.insert((*key).into(), file);
91 }
92 Ok(ys)
93}
94
95fn load_entries(data: &Edn) -> Result<HashMap<Arc<str>, SnapshotConfigs>, String> {
96 let xs = data.read_map_or_nil().map_err(|e| format!("failed loading entries, {}", e))?;
97 let mut ys: HashMap<Arc<str>, SnapshotConfigs> = HashMap::with_capacity(xs.len());
98 for (k, v) in xs {
99 let key: Box<str> = match k {
100 Edn::Keyword(s) => s.to_str(),
101 Edn::Str(s) => s,
102 _ => return Err(format!("unknown data for an entry: {}", k)),
103 };
104 let configs = load_configs(&v)?;
105 ys.insert((*key).into(), configs);
106 }
107 Ok(ys)
108}
109
110pub fn load_snapshot_data(data: &Edn, path: &str) -> Result<Snapshot, String> {
112 let pkg = data.map_get("package")?.read_str()?;
113 let mut files = load_files(&data.map_get("files")?)?;
114 let meta_ns = format!("{}.$meta", pkg);
115 files.insert(meta_ns.to_owned().into(), gen_meta_ns(&meta_ns, path));
116 let s = Snapshot {
117 package: (*pkg).into(),
118 configs: load_configs(&data.map_get("configs")?)?,
119 entries: load_entries(&data.map_get("entries")?)?,
120 files,
121 };
122 Ok(s)
123}
124
125pub fn gen_meta_ns(ns: &str, path: &str) -> FileInSnapShot {
126 let mut def_dict: HashMap<Arc<str>, Cirru> = HashMap::with_capacity(2);
127 def_dict.insert(
128 "calcit-filename".into(),
129 Cirru::List(vec![
130 Cirru::leaf("def"),
131 Cirru::leaf("calcit-filename"),
132 Cirru::leaf(format!("|{}", path.escape_default())),
133 ]),
134 );
135 let path_data = Path::new(path);
136 let parent = path_data.parent().unwrap();
137 let parent_str = parent.to_str().unwrap();
138
139 def_dict.insert(
140 "calcit-dirname".into(),
141 Cirru::List(vec![
142 Cirru::leaf("def"),
143 Cirru::leaf("calcit-dirname"),
144 Cirru::leaf(format!("|{}", parent_str.escape_default())),
145 ]),
146 );
147
148 FileInSnapShot {
149 ns: Cirru::List(vec![Cirru::leaf("ns"), Cirru::Leaf(ns.to_owned().into())]),
150 defs: def_dict,
151 }
152}
153
154pub fn gen_default() -> Snapshot {
155 Snapshot {
156 package: "app".into(),
157 configs: SnapshotConfigs {
158 init_fn: "app.main/main!".into(),
159 reload_fn: "app.main/reload!".into(),
160 version: "0.0.0".into(),
161 modules: vec![],
162 },
163 entries: HashMap::new(),
164 files: HashMap::new(),
165 }
166}
167
168pub fn create_file_from_snippet(raw: &str) -> Result<FileInSnapShot, String> {
169 match cirru_parser::parse(raw) {
170 Ok(lines) => {
171 let mut def_dict: HashMap<Arc<str>, Cirru> = HashMap::with_capacity(2);
172 let mut func_code = vec![Cirru::leaf("defn"), Cirru::leaf("main!"), Cirru::List(vec![])];
173 for line in lines {
174 func_code.push(line.to_owned());
175 }
176 def_dict.insert("main!".into(), Cirru::List(func_code));
177 def_dict.insert(
178 "reload!".into(),
179 Cirru::List(vec![Cirru::leaf("defn"), Cirru::leaf("reload!"), Cirru::List(vec![])]),
180 );
181 Ok(FileInSnapShot {
182 ns: Cirru::List(vec![Cirru::leaf("ns"), Cirru::leaf("app.main")]),
183 defs: def_dict,
184 })
185 }
186 Err(e) => Err(format!("failed to make snapshot: {}", e)),
187 }
188}
189
190#[derive(Debug, PartialEq, Clone)]
191pub struct FileChangeInfo {
192 pub ns: Option<Cirru>,
193 pub added_defs: HashMap<Arc<str>, Cirru>,
194 pub removed_defs: HashSet<Arc<str>>,
195 pub changed_defs: HashMap<Arc<str>, Cirru>,
196}
197
198#[derive(Debug, PartialEq, Clone)]
199pub struct ChangesDict {
200 pub added: HashMap<Arc<str>, FileInSnapShot>,
201 pub removed: HashSet<Arc<str>>,
202 pub changed: HashMap<Arc<str>, FileChangeInfo>,
203}
204
205pub fn load_changes_info(data: &Edn) -> Result<ChangesDict, String> {
206 let mut added: HashMap<Arc<str>, FileInSnapShot> = HashMap::new();
208 for (ns, file) in &data.map_get("added")?.read_map_or_nil()? {
209 added.insert(ns.read_str()?.into(), load_file_info(file)?);
210 }
211
212 let mut removed: HashSet<Arc<str>> = HashSet::new();
213 for item in &data.map_get("removed")?.read_set_or_nil()? {
214 removed.insert(item.read_str()?.into());
215 }
216
217 let mut changed: HashMap<Arc<str>, FileChangeInfo> = HashMap::new();
218 for (ns, file) in &data.map_get("changed")?.read_map_or_nil()? {
219 changed.insert(ns.read_str()?.into(), extract_changed_info(file)?);
220 }
221
222 Ok(ChangesDict { added, removed, changed })
223}
224
225pub fn extract_changed_info(data: &Edn) -> Result<FileChangeInfo, String> {
226 let ns_info = match data.map_get("ns")? {
227 Edn::Nil => None,
228 Edn::Quote(code) => Some(code),
229 a => return Err(format!("invalid information for ns code: {}", a)),
230 };
231
232 let mut added_defs: HashMap<Arc<str>, Cirru> = HashMap::new();
233
234 for (def, code) in data.map_get("added-defs")?.read_map_or_nil()? {
235 added_defs.insert((*def.read_str()?).into(), code.read_quoted_cirru()?);
236 }
237
238 let mut removed_defs: HashSet<Arc<str>> = HashSet::new();
239
240 for def in data.map_get("removed-defs")?.read_set_or_nil()? {
241 removed_defs.insert((*def.read_str()?).into());
242 }
243
244 let mut changed_defs: HashMap<Arc<str>, Cirru> = HashMap::new();
245 for (def, code) in data.map_get("changed-defs")?.read_map_or_nil()? {
246 changed_defs.insert((*(def.read_str()?)).into(), code.read_quoted_cirru()?);
247 }
248
249 Ok(FileChangeInfo {
250 ns: ns_info,
251 added_defs,
252 removed_defs,
253 changed_defs,
254 })
255}