ordinary_modify/
content.rs1use ordinary_config::{Content, ContentDefinition, OrdinaryConfig};
6use ordinary_types::{ContentObject, Field};
7use std::io::Write;
8use std::path::Path;
9use tracing::instrument;
10
11#[instrument(err)]
12pub fn add_def(path: &str, name: &String, json_fields: &str) -> anyhow::Result<()> {
13 let proj_path = Path::new(path);
14 let mut app_config = OrdinaryConfig::get(path, false)?;
15
16 let mut file_path = "./content.json".to_string();
17
18 let mut content_defs = if let Some(d) = app_config.content {
19 file_path = d.file_path;
20 d.definitions
21 } else {
22 let mut file = fs_err::File::create(proj_path.join("content.json"))?;
23 file.write_all(b"[]")?;
24
25 vec![]
26 };
27
28 if content_defs.len() < 255
29 && let Ok(fields) = serde_json::from_str::<Vec<Field>>(json_fields)
30 {
31 let next_idx = u8::try_from(content_defs.len())?;
32
33 content_defs.push(ContentDefinition {
34 idx: next_idx,
35 name: name.clone(),
36 fields,
37 lifecycle: None,
38 });
39
40 app_config.content = Some(Content {
41 definitions: content_defs,
42 file_path,
43 update: None,
44 });
45
46 let ordinary_json = serde_json::to_string_pretty(&app_config)?;
47
48 let mut file = fs_err::File::create(proj_path.join("ordinary.json"))?;
49 file.write_all(ordinary_json.as_bytes())?;
50 } else {
51 tracing::error!("cannot support more than 255 content definitions for a single project.");
52 }
53
54 Ok(())
55}
56
57#[instrument(err)]
58pub fn edit_def(
59 path: &str,
60 idx: u8,
61 name: Option<String>,
62 json_fields: &str,
63) -> anyhow::Result<()> {
64 let proj_path = Path::new(path);
65 let mut app_config = OrdinaryConfig::get(path, false)?;
66
67 if let Some(content) = app_config.content.as_mut()
68 && let Some(def) = content.definitions.iter_mut().find(|v| v.idx == idx)
69 && let Ok(fields) = serde_json::from_str::<Vec<Field>>(json_fields)
70 {
71 def.fields = fields;
72
73 if let Some(name) = name {
74 def.name = name;
75 }
76 }
77
78 let ordinary_json = serde_json::to_string_pretty(&app_config)?;
79
80 let mut file = fs_err::File::create(proj_path.join("ordinary.json"))?;
81 file.write_all(ordinary_json.as_bytes())?;
82
83 Ok(())
84}
85
86#[instrument(skip_all, err)]
87fn before_all(
88 proj_path: &Path,
89 content_def: &ContentDefinition,
90 config: &OrdinaryConfig,
91) -> anyhow::Result<()> {
92 if let Some(lifecycle_config) = &config.lifecycle
93 && let Some(before_all) = &lifecycle_config.before_all
94 {
95 OrdinaryConfig::exec_script(proj_path, &None, "all", "before", before_all)?;
96 }
97
98 if let Some(lifecycle_config) = &content_def.lifecycle
99 && let Some(before_all) = &lifecycle_config.before_all
100 {
101 OrdinaryConfig::exec_script(
102 proj_path,
103 &None,
104 &content_def.name,
105 "before_all",
106 before_all,
107 )?;
108 }
109
110 Ok(())
111}
112
113#[instrument(skip_all, err)]
114pub fn add_obj(path: &str, object_json: &str) -> anyhow::Result<()> {
115 let proj_path = Path::new(path);
116 let app_config = OrdinaryConfig::get(path, false)?;
117
118 let new_obj: ContentObject = serde_json::from_str(object_json)?;
119
120 if let Some(content) = &app_config.content
121 && let Some(content_def) = content
122 .definitions
123 .iter()
124 .find(|d| d.name == new_obj.instance_of)
125 {
126 let content_json = fs_err::read_to_string(proj_path.join(&content.file_path))?;
127 let mut content_objects: Vec<ContentObject> = serde_json::from_str(&content_json)?;
128
129 before_all(proj_path, content_def, &app_config)?;
130
131 if let Some(lifecycle_config) = &content_def.lifecycle
132 && let Some(on_add) = &lifecycle_config.on_add
133 && let Some(before) = &on_add.before
134 {
135 OrdinaryConfig::exec_script(proj_path, &None, &content_def.name, "before", before)?;
136 }
137
138 content_objects.push(new_obj.clone());
141
142 let new_objects = serde_json::to_string_pretty(&content_objects)?;
143
144 let mut file = fs_err::File::create(proj_path.join(&content.file_path))?;
145 file.write_all(new_objects.as_bytes())?;
146
147 if let Some(lifecycle_config) = &content_def.lifecycle
148 && let Some(on_add) = &lifecycle_config.on_add
149 && let Some(after) = &on_add.after
150 {
151 OrdinaryConfig::exec_script(
152 proj_path,
153 &Some(serde_json::to_string(&new_obj)?),
154 &content_def.name,
155 "after",
156 after,
157 )?;
158 }
159 }
160
161 Ok(())
162}
163
164#[instrument(skip_all, err)]
165pub fn edit_obj(path: &str, object_json: &str) -> anyhow::Result<()> {
166 let proj_path = Path::new(path);
167 let app_config = OrdinaryConfig::get(path, false)?;
168 let new_obj: ContentObject = serde_json::from_str(object_json)?;
169
170 if let Some(content) = &app_config.content
171 && let Some(content_def) = content
172 .definitions
173 .iter()
174 .find(|d| d.name == new_obj.instance_of)
175 {
176 let content_json = fs_err::read_to_string(proj_path.join(&content.file_path))?;
177 let mut content_objects: Vec<ContentObject> = serde_json::from_str(&content_json)?;
178
179 before_all(proj_path, content_def, &app_config)?;
180
181 if let Some(obj) = content_objects
182 .iter_mut()
183 .find(|obj| obj.uuid == new_obj.uuid && obj.instance_of == new_obj.instance_of)
184 {
185 if let Some(lifecycle_config) = &content_def.lifecycle
186 && let Some(on_edit) = &lifecycle_config.on_edit
187 && let Some(before) = &on_edit.before
188 {
189 OrdinaryConfig::exec_script(proj_path, &None, &content_def.name, "before", before)?;
190 }
191
192 obj.fields.clone_from(&new_obj.fields);
193 }
194
195 let new_objects = serde_json::to_string_pretty(&content_objects)?;
196
197 let mut file = fs_err::File::create(proj_path.join(&content.file_path))?;
198 file.write_all(new_objects.as_bytes())?;
199
200 if let Some(lifecycle_config) = &content_def.lifecycle
201 && let Some(on_edit) = &lifecycle_config.on_edit
202 && let Some(after) = &on_edit.after
203 {
204 OrdinaryConfig::exec_script(
205 proj_path,
206 &Some(serde_json::to_string(&new_obj)?),
207 &content_def.name,
208 "after",
209 after,
210 )?;
211 }
212 }
213 Ok(())
214}
215
216#[instrument(skip_all, err)]
217pub fn delete_obj(path: &str, instance_of: &str, uuid: &str) -> anyhow::Result<()> {
218 let proj_path = Path::new(path);
219 let app_config = OrdinaryConfig::get(path, false)?;
220
221 if let Some(content) = &app_config.content
222 && let Some(content_def) = content.definitions.iter().find(|d| d.name == instance_of)
223 {
224 let content_json = fs_err::read_to_string(proj_path.join(&content.file_path))?;
225 let mut content_objects: Vec<ContentObject> = serde_json::from_str(&content_json)?;
226
227 before_all(proj_path, content_def, &app_config)?;
228
229 let mut removed = None;
230
231 if let Some(pos) = content_objects
232 .iter()
233 .position(|obj| obj.uuid == uuid && obj.instance_of == instance_of)
234 {
235 if let Some(lifecycle_config) = &content_def.lifecycle
236 && let Some(on_delete) = &lifecycle_config.on_delete
237 && let Some(before) = &on_delete.before
238 {
239 OrdinaryConfig::exec_script(proj_path, &None, &content_def.name, "before", before)?;
240 }
241
242 removed = Some(content_objects.remove(pos));
243 }
244
245 let new_objects = serde_json::to_string_pretty(&content_objects)?;
246
247 let mut file = fs_err::File::create(proj_path.join(&content.file_path))?;
248 file.write_all(new_objects.as_bytes())?;
249
250 if let Some(removed) = removed
251 && let Some(lifecycle_config) = &content_def.lifecycle
252 && let Some(on_delete) = &lifecycle_config.on_delete
253 && let Some(after) = &on_delete.after
254 {
255 OrdinaryConfig::exec_script(
256 proj_path,
257 &Some(serde_json::to_string(&removed)?),
258 &content_def.name,
259 "after",
260 after,
261 )?;
262 }
263 }
264
265 Ok(())
266}