1use std::mem::take;
26use std::sync::Arc;
27
28use anyhow::Result;
29use indexmap::IndexMap;
30use std::marker::Send;
31use std::marker::Sync;
32
33use crate::{instance::*, repository::*};
34
35#[derive(Clone)]
45pub struct Class {
46 pub name: String,
47 pub attrs: IndexMap<String, Attr>,
48 pub subclasses: SubclassesSpecifier,
49 pub hierarchy: Vec<String>,
50 pub collects: Vec<CollectionSpecifier>,
51 pub html_body: Option<String>,
52 pub html_header: Option<String>,
53}
54
55#[derive(Clone, PartialEq)]
74pub enum SubclassesSpecifier {
75 List(Vec<String>),
76 Var(String),
77 Empty(),
78}
79
80#[derive(Clone, PartialEq)]
89pub struct CollectionSpecifier {
90 pub class_name: String,
91 pub virtual_attribute: Option<CollectionAttribute>,
92}
93
94#[derive(Clone, PartialEq)]
102pub struct CollectionAttribute {
103 pub attr_name: String,
104 pub is_public: bool,
105 pub is_optional: bool,
106 pub is_array: bool,
107}
108
109#[derive(Clone)]
120pub struct Attr {
121 pub cmd: std::sync::Arc<dyn AttrCommand + Sync + Send>,
122 pub is_public: bool,
123 pub is_optional: bool,
124 pub is_array: bool,
125}
126
127pub trait AttrCommand {
130 fn apply(
131 &self,
132 ctx: &mut Context,
133 instance: &SandboxBuilder,
134 tx: &mut ReadWriteTransaction,
135 entity: &str,
136 ) -> Result<()>;
137 fn revert(
138 &self,
139 ctx: &mut Context,
140 instance: &SandboxBuilder,
141 tx: &mut ReadWriteTransaction,
142 entity: &str,
143 ) -> Result<()>;
144 fn value(&self) -> Option<String> {
145 None
146 }
147}
148
149pub trait InjectCommand {
152 fn inject(
153 &self,
154 instance: &SandboxBuilder,
155 tx: &mut ReadWriteTransaction,
156 euid: &str,
157 caller: &str,
158 ) -> Result<()>;
159 fn eject(
160 &self,
161 instance: &SandboxBuilder,
162 tx: &mut ReadWriteTransaction,
163 euid: &str,
164 caller: &str,
165 ) -> Result<()>;
166}
167
168#[derive(Clone)]
169pub struct Injectors {
170 pub prependers: Vec<Arc<dyn InjectCommand + Send + Sync>>,
171 pub appenders: Vec<Arc<dyn InjectCommand + Send + Sync>>,
172}
173
174pub struct ClassBuilder {
177 pub name: String,
178 pub parent: String,
179 pub attrs: IndexMap<String, Attr>,
180 pub subclasses: SubclassesSpecifier,
181 pub hierarchy: Vec<String>,
182 pub collects: Vec<CollectionSpecifier>,
183 pub html_body: Option<String>,
184 pub html_header: Option<String>,
185 expanded: bool,
186}
187
188impl ClassBuilder {
189 pub fn new() -> Self {
191 ClassBuilder {
192 name: String::new(),
193 parent: String::new(),
194 attrs: indexmap::IndexMap::new(),
195 subclasses: SubclassesSpecifier::Empty(),
196 hierarchy: vec![],
197 collects: vec![],
198 expanded: false,
199 html_body: None,
200 html_header: None,
201 }
202 }
203
204 pub fn name(&mut self, name: &str) -> &mut Self {
206 self.name = name.to_string();
207 if self.hierarchy.contains(&self.name) {
208 log::error!("Invalid hierarchy detected for class {}", name)
209 } else {
210 self.hierarchy.push(self.name.clone());
211 }
212 self
213 }
214
215 pub fn add_attr(&mut self, key: String, attr: Attr) -> &mut Self {
217 if self.attrs.contains_key(&key) {
218 let existing_attr = &self.attrs[&key];
219 if (
220 existing_attr.is_array,
221 existing_attr.is_public,
222 existing_attr.is_optional,
223 ) != (attr.is_array, attr.is_public, attr.is_optional)
224 {
225 log::warn!(
226 "Overriding attribute {} in {} has conflicting metadata.",
227 key,
228 self.name
229 );
230 }
231 self.attrs[&key] = attr;
232 } else {
233 self.attrs.insert(key.to_owned(), attr);
234 }
235 self
236 }
237
238 pub fn html_body(&mut self, body: String) -> &mut Self {
240 self.html_body = Some(body);
241 self
242 }
243
244 pub fn html_header(&mut self, header: String) -> &mut Self {
246 self.html_header = Some(header);
247 self
248 }
249
250 pub fn subclass_item(&mut self, class_name_to_collect: &str) {
252 if let SubclassesSpecifier::List(subclasses_list) = &mut self.subclasses {
253 subclasses_list.push(class_name_to_collect.to_string());
254 } else {
255 self.subclasses = SubclassesSpecifier::List(vec![class_name_to_collect.to_string()]);
256 }
257 }
258
259 pub fn subclass_var(&mut self, class_name_to_collect: &str) {
261 self.subclasses = SubclassesSpecifier::Var(class_name_to_collect.to_string());
262 }
263
264 pub fn collect(&mut self, spec: CollectionSpecifier) {
266 self.collects.push(spec);
267 }
268
269 pub fn expand(
271 &mut self,
272 instance: &SandboxInstance,
273 expand_with_class_name: &str,
274 ) -> &mut Self {
275 let expand_class = &instance.classes.get(expand_with_class_name).unwrap();
276 for (k, v) in &expand_class.attrs {
277 self.attrs.insert(k.to_string(), v.clone());
278 }
279 self.expanded = true;
280 self
281 }
282
283 pub fn extends(&mut self, instance: &SandboxInstance, parent_class_name: &str) -> &mut Self {
285 self.parent = parent_class_name.to_string();
286 let mut parent_class_name_mut = parent_class_name;
287
288 while !parent_class_name_mut.is_empty() {
289 self.hierarchy.push(parent_class_name_mut.to_string());
290 let parent_class = instance.classes.get(parent_class_name_mut).unwrap();
291 parent_class_name_mut = if parent_class.hierarchy.len() < 2 {
292 ""
293 } else {
294 &parent_class.hierarchy[1]
295 };
296 if let Some(parent_html_body) = &parent_class.html_body {
297 self.html_body = Some(parent_html_body.clone());
298 }
299 if let Some(parent_html_header) = &parent_class.html_header {
300 self.html_header = Some(parent_html_header.clone());
301 }
302 self.collects = parent_class.collects.clone();
303 }
304 self
305 }
306
307 pub fn conclude(&mut self, instance: &SandboxInstance) -> &mut Self {
309 if !self.expanded && !self.parent.is_empty() {
310 let my_attrs = take(&mut self.attrs);
311 self.expand(instance, &self.parent.clone());
312 self.attrs.extend(my_attrs);
313 }
314 self
315 }
316
317 pub fn build(self) -> Class {
319 Class {
320 name: self.name,
321 attrs: self.attrs,
322 subclasses: self.subclasses,
323 hierarchy: self.hierarchy,
324 collects: self.collects,
325 html_body: self.html_body,
326 html_header: self.html_header,
327 }
328 }
329}
330
331impl Default for ClassBuilder {
332 fn default() -> Self {
333 Self::new()
334 }
335}
336
337#[derive(Debug, PartialEq)]
338pub struct AppendPayload<'a> {
339 pub class_override: Option<&'a str>,
340 pub appended_uid: Option<String>,
341}
342
343#[derive(Debug, PartialEq)]
344pub struct RerollPayload<'a> {
345 pub existing_uid: String,
346 pub class_override: Option<&'a str>,
347 pub new_uid: Option<String>,
348}
349
350#[derive(Debug, PartialEq)]
354pub enum Context<'a> {
355 Rolling,
356 Appending(AppendPayload<'a>),
357 Rerolling(RerollPayload<'a>),
358 Unrolling,
359 Restoring,
360}