stof/core/libraries/
object.rs

1//
2// Copyright 2024 Formata, Inc. All rights reserved.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//    http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17use std::{cmp::Ordering, collections::{BTreeMap, HashMap, HashSet}, ops::Deref, sync::Arc};
18use crate::{lang::SError, IntoNodeRef, Library, SData, SDoc, SField, SFunc, SMutex, SNodeRef, SNum, SPrototype, SVal};
19
20
21#[derive(Default, Debug)]
22pub struct ObjectLibrary;
23impl ObjectLibrary {
24    /// Call object operation.
25    pub fn operate(&self, pid: &str, doc: &mut SDoc, name: &str, obj: &SNodeRef, parameters: &mut Vec<SVal>) -> Result<SVal, SError> {
26        match name {
27            "len" => {
28                if let Some(node) = obj.node(&doc.graph) {
29                    let refs = node.data_refs::<SField>(&doc.graph);
30                    return Ok(SVal::Number(SNum::I64(refs.len() as i64)));
31                }
32                Ok(SVal::Number(SNum::I64(0)))
33            },
34            "at" => {
35                if parameters.len() == 1 {
36                    match &parameters[0] {
37                        SVal::String(index) => {
38                            if let Some(field) = SField::field(&doc.graph, &index, '.', Some(obj)) {
39                                return Ok(field.value.clone());
40                            } else if let Some(func) = SFunc::func_ref(&doc.graph, &index, '.', Some(obj)) {
41                                return Ok(SVal::FnPtr(func));
42                            }
43                            return Ok(SVal::Null); // Not found
44                        },
45                        SVal::Number(val) => {
46                            let mut fields = SField::fields(&doc.graph, obj);
47                            let index = val.int() as usize;
48                            if index < fields.len() {
49                                let field = fields.remove(index);
50                                let value = field.value.clone();
51                                let key = SVal::String(field.name.clone());
52                                return Ok(SVal::Tuple(vec![key, value]));
53                            }
54                        },
55                        _ => {}
56                    }
57                }
58                if parameters.len() > 1 {
59                    let mut array = Vec::new();
60                    for param in parameters.drain(..) {
61                        match param {
62                            SVal::String(index) => {
63                                if let Some(field) = SField::field(&doc.graph, &index, '.', Some(obj)) {
64                                    array.push(field.value.clone());
65                                } else if let Some(func) = SFunc::func_ref(&doc.graph, &index, '.', Some(obj)) {
66                                    array.push(SVal::FnPtr(func));
67                                }
68                            },
69                            SVal::Number(val) => {
70                                let mut fields = SField::fields(&doc.graph, obj);
71                                let index = val.int() as usize;
72                                if index < fields.len() {
73                                    let field = fields.remove(index);
74                                    let value = field.value.clone();
75                                    let key = SVal::String(field.name.clone());
76                                    array.push(SVal::Tuple(vec![key, value]));
77                                }
78                            },
79                            _ => {}
80                        }
81                    }
82                    return Ok(SVal::Array(array));
83                }
84                Err(SError::obj(pid, &doc, "at", "invalid arguments - index must be a string or number"))
85            },
86            "reference" => {
87                if parameters.len() == 1 {
88                    let field_path = parameters[0].to_string();
89                    if let Some(field_ref) = SField::field_ref(&doc.graph, &field_path, '.', Some(obj)) {
90                        if let Some(field) = SData::get::<SField>(&doc.graph, &field_ref) {
91                            self.operate(pid, doc, "removeField", obj, &mut vec![SVal::String(field.name.clone())])?;
92                        }
93                        SData::attach_existing(&mut doc.graph, obj, field_ref);
94                        return Ok(SVal::Bool(true));
95                    } else if let Some(field_ref) = SField::field_ref(&doc.graph, &field_path, '.', None) {
96                        if let Some(field) = SData::get::<SField>(&doc.graph, &field_ref) {
97                            self.operate(pid, doc, "removeField", obj, &mut vec![SVal::String(field.name.clone())])?;
98                        }
99                        SData::attach_existing(&mut doc.graph, obj, field_ref);
100                        return Ok(SVal::Bool(true));
101                    } else if let Some(func) = SFunc::func_ref(&doc.graph, &field_path, '.', Some(obj)) {
102                        SData::attach_existing(&mut doc.graph, obj, func);
103                        return Ok(SVal::Bool(true));
104                    } else if let Some(func) = SFunc::func_ref(&doc.graph, &field_path, '.', None) {
105                        SData::attach_existing(&mut doc.graph, obj, func);
106                        return Ok(SVal::Bool(true));
107                    }
108                    return Ok(SVal::Bool(false));
109                } else if parameters.len() == 2 {
110                    match &parameters[0] {
111                        SVal::Object(context) => {
112                            let field_path = parameters[1].to_string();
113                            if let Some(field_ref) = SField::field_ref(&doc.graph, &field_path, '.', Some(&context)) {
114                                if let Some(field) = SData::get::<SField>(&doc.graph, &field_ref) {
115                                    self.operate(pid, doc, "removeField", obj, &mut vec![SVal::String(field.name.clone())])?;
116                                }
117                                SData::attach_existing(&mut doc.graph, obj, field_ref);
118                                return Ok(SVal::Bool(true));
119                            } else if let Some(func) = SFunc::func_ref(&doc.graph, &field_path, '.', Some(&context)) {
120                                SData::attach_existing(&mut doc.graph, obj, func);
121                                return Ok(SVal::Bool(true));
122                            }
123                            return Ok(SVal::Bool(false));
124                        },
125                        _ => {}
126                    }
127                }
128                Err(SError::obj(pid, &doc, "reference", "path argument not found"))
129            },
130            "fields" => {
131                let fields = SField::fields(&doc.graph, obj);
132                let mut map = BTreeMap::new();
133                for field in fields {
134                    let value = field.value.clone();
135                    let key = SVal::String(field.name.clone());
136                    map.insert(key, value);
137                }
138                Ok(SVal::Map(map))
139            },
140            "attributes" => {
141                if parameters.len() < 1 {
142                    return Err(SError::obj(pid, &doc, "attributes", "invalid arguments - path not found"));
143                }
144                match &parameters[0] {
145                    SVal::String(index) => {
146                        if let Some(field) = SField::field(&doc.graph, &index, '.', Some(obj)) {
147                            let mut attrs = BTreeMap::new();
148                            for (key, value) in &field.attributes {
149                                attrs.insert(SVal::String(key.clone()), value.clone());
150                            }
151                            return Ok(SVal::Map(attrs));
152                        } else if let Some(func_ref) = SFunc::func_ref(&doc.graph, &index, '.', Some(obj)) {
153                            let mut attrs = BTreeMap::new();
154                            if let Some(func) = SData::get::<SFunc>(&doc.graph, &func_ref) {
155                                for (key, value) in &func.attributes {
156                                    attrs.insert(SVal::String(key.clone()), value.clone());
157                                }
158                            }
159                            return Ok(SVal::Map(attrs));
160                        }
161                        return Ok(SVal::Null); // Not found
162                    },
163                    SVal::Boxed(val) => {
164                        let val = val.lock().unwrap();
165                        let val = val.deref();
166                        match val {
167                            SVal::String(index) => {
168                                if let Some(field) = SField::field(&doc.graph, &index, '.', Some(obj)) {
169                                    let mut attrs = BTreeMap::new();
170                                    for (key, value) in &field.attributes {
171                                        attrs.insert(SVal::String(key.clone()), value.clone());
172                                    }
173                                    return Ok(SVal::Map(attrs));
174                                } else if let Some(func_ref) = SFunc::func_ref(&doc.graph, &index, '.', Some(obj)) {
175                                    let mut attrs = BTreeMap::new();
176                                    if let Some(func) = SData::get::<SFunc>(&doc.graph, &func_ref) {
177                                        for (key, value) in &func.attributes {
178                                            attrs.insert(SVal::String(key.clone()), value.clone());
179                                        }
180                                    }
181                                    return Ok(SVal::Map(attrs));
182                                }
183                                return Ok(SVal::Null); // Not found
184                            },
185                            _ => {
186                                Err(SError::obj(pid, &doc, "attributes", "invalid arguments - path must be a string"))
187                            }
188                        }
189                    },
190                    _ => {
191                        Err(SError::obj(pid, &doc, "attributes", "invalid arguments - path must be a string"))
192                    }
193                }
194            },
195            "funcs" |
196            "functions" => {
197                let funcs = SFunc::func_refs(&doc.graph, obj);
198                let mut map = BTreeMap::new();
199                for func_ref in funcs {
200                    if let Some(func) = SData::get::<SFunc>(&doc.graph, &func_ref) {
201                        let value = SVal::FnPtr(func_ref);
202                        let key = SVal::String(func.name.clone());
203                        map.insert(key, value);
204                    }
205                }
206                Ok(SVal::Map(map))
207            },
208            "keys" => {
209                let fields = SField::fields(&doc.graph, obj);
210                let mut array = Vec::new();
211                for field in fields {
212                    array.push(SVal::String(field.name.clone()));
213                }
214                Ok(SVal::Array(array))
215            },
216            "values" => {
217                let fields = SField::fields(&doc.graph, obj);
218                let mut array = Vec::new();
219                for field in fields {
220                    array.push(field.value.clone());
221                }
222                Ok(SVal::Array(array))
223            },
224            // Unbox a field without an assign operation.
225            // Can be used like "set", but with an unbox operation in the middle.
226            "unbox" => {
227                if parameters.len() < 1 {
228                    return Err(SError::obj(pid, &doc, "unbox", "invalid arguments - expecing a string path to a field that should be unboxed on this object"));
229                }
230                let path = parameters[0].to_string();
231                let mut value = None;
232                if parameters.len() > 1 {
233                    value = Some(parameters.pop().unwrap());
234                }
235
236                // Look for an existing field to unbox
237                if let Some(field_ref) = SField::field_ref(&doc.graph, &path, '.', Some(obj)) {
238                    if !doc.perms.can_write_field(&doc.graph, &field_ref, Some(obj)) {
239                        return Ok(SVal::Bool(false));
240                    }
241                    if let Some(field) = SData::get_mut::<SField>(&mut doc.graph, &field_ref) {
242                        if let Some(value) = value {
243                            field.value = value.unbox();
244                        } else {
245                            field.value.unbox_ref();
246                        }
247                        return Ok(SVal::Bool(true));
248                    }
249                    return Ok(SVal::Bool(false));
250                }
251                if let Some(value) = value {
252                    // val is a dot separated path!
253                    let mut path = path.split('.').collect::<Vec<&str>>();
254                    let name = path.pop().unwrap().to_string();
255
256                    // Ensure the path exists if we need to add objects
257                    let mut fref = obj.clone();
258                    if path.len() > 0 {
259                        fref = doc.graph.ensure_nodes(&path.join("/"), '/', true, Some(obj.clone()));
260                    }
261
262                    // Create the field on fref with the unboxed value
263                    let field = SField::new(&name, value.unbox());
264                    SData::insert_new(&mut doc.graph, &fref, Box::new(field));
265                    return Ok(SVal::Bool(true));
266                }
267                Ok(SVal::Bool(false))
268            },
269            // Box a field without an assign operation.
270            // Can be used like "set", but with a box operation in the middle.
271            "box" => {
272                if parameters.len() < 1 {
273                    return Err(SError::obj(pid, &doc, "box", "invalid arguments - expecing a string path to a field that should be boxed on this object"));
274                }
275                let path = parameters[0].to_string();
276                let mut value = None;
277                if parameters.len() > 1 {
278                    value = Some(parameters.pop().unwrap());
279                }
280
281                // Look for an existing field to box
282                if let Some(field_ref) = SField::field_ref(&doc.graph, &path, '.', Some(obj)) {
283                    if !doc.perms.can_write_field(&doc.graph, &field_ref, Some(obj)) {
284                        return Ok(SVal::Bool(false));
285                    }
286                    if let Some(field) = SData::get_mut::<SField>(&mut doc.graph, &field_ref) {
287                        if let Some(value) = value {
288                            field.value = value.to_box();
289                        } else {
290                            field.value.to_box_ref();
291                        }
292                        return Ok(SVal::Bool(true));
293                    }
294                    return Ok(SVal::Bool(false));
295                }
296
297                // val is a dot separated path!
298                let mut path = path.split('.').collect::<Vec<&str>>();
299                let name = path.pop().unwrap().to_string();
300
301                // Ensure the path exists if we need to add objects
302                let mut fref = obj.clone();
303                if path.len() > 0 {
304                    fref = doc.graph.ensure_nodes(&path.join("/"), '/', true, Some(obj.clone()));
305                }
306
307                // Create the field on fref
308                let mut field = SField::new(&name, SVal::Null.to_box());
309                if let Some(value) = value {
310                    field.value = value.to_box();
311                }
312                SData::insert_new(&mut doc.graph, &fref, Box::new(field));
313                Ok(SVal::Bool(true))
314            },
315            "set" => {
316                if parameters.len() == 2 {
317                    let value = parameters.pop().unwrap();
318                    let name = parameters.pop().unwrap().to_string();
319
320                    // Check for an existing field at this location
321                    if let Some(field_ref) = SField::field_ref(&doc.graph, &name, '.', Some(obj)) {
322                        if !doc.perms.can_write_field(&doc.graph, &field_ref, Some(obj)) {
323                            return Ok(SVal::Bool(false));
324                        }
325                        if let Some(field) = SData::get_mut::<SField>(&mut doc.graph, &field_ref) {
326                            field.value = value;
327                            return Ok(SVal::Bool(true));
328                        }
329                        return Ok(SVal::Bool(false));
330                    }
331
332                    // val is a dot separated path!
333                    let mut path = name.split('.').collect::<Vec<&str>>();
334                    let name = path.pop().unwrap().to_string();
335
336                    // Ensure the path exists if we need to add objects
337                    let mut fref = obj.clone();
338                    if path.len() > 0 {
339                        fref = doc.graph.ensure_nodes(&path.join("/"), '/', true, Some(obj.clone()));
340                    }
341
342                    // Create the field on fref
343                    let field = SField::new(&name, value);
344                    SData::insert_new(&mut doc.graph, &fref, Box::new(field));
345                    return Ok(SVal::Bool(true));
346                }
347                Err(SError::obj(pid, &doc, "set", "invalid arguments - requires a name and value to set a field"))
348            },
349            // Take a map and do rename/moves with all entries.
350            // Signature: Object.mapFields(obj, map: map): map
351            "mapFields" => {
352                if parameters.len() < 1 {
353                    return Err(SError::obj(pid, &doc, "mapFields", "invalid arguments - requires a map argument"));
354                }
355                match &parameters[0] {
356                    SVal::Map(map) => {
357                        let mut mapped_values = BTreeMap::new();
358                        for (k, v) in map {
359                            let res = self.operate(pid, doc, "renameField", obj, &mut vec![k.clone(), v.clone()])?;
360                            if res.truthy() {
361                                mapped_values.insert(k.clone(), v.clone());
362                            }
363                        }
364                        Ok(SVal::Map(mapped_values))
365                    },
366                    SVal::Boxed(val) => {
367                        let val = val.lock().unwrap();
368                        let val = val.deref();
369                        match val {
370                            SVal::Map(map) => {
371                                let mut mapped_values = BTreeMap::new();
372                                for (k, v) in map {
373                                    let res = self.operate(pid, doc, "renameField", obj, &mut vec![k.clone(), v.clone()])?;
374                                    if res.truthy() {
375                                        mapped_values.insert(k.clone(), v.clone());
376                                    }
377                                }
378                                Ok(SVal::Map(mapped_values))
379                            },
380                            _ => {
381                                Err(SError::obj(pid, &doc, "mapFields", "invalid arguments - map argument not found"))
382                            }
383                        }
384                    },
385                    _ => {
386                        Err(SError::obj(pid, &doc, "mapFields", "invalid arguments - map argument not found"))
387                    }
388                }
389            },
390            // Rename/Move a field if this object has it and permissions allow.
391            // Signature: Object.renameField(obj, field_path: str, new_path: str): bool
392            "moveField" |
393            "renameField" => {
394                if parameters.len() < 2 {
395                    return Err(SError::obj(pid, &doc, "moveField", "invalid arguments - requires two paths, a source and a destination"));
396                }
397                let dest = parameters.pop().unwrap().owned_to_string();
398                let source = parameters.pop().unwrap().owned_to_string();
399
400                if let Some(field_ref) = SField::field_ref(&doc.graph, &source, '.', Some(obj)) {
401                    if !doc.perms.can_write_field(&doc.graph, &field_ref, Some(obj)) {
402                        return Ok(SVal::Bool(false));
403                    }
404
405                    // union the destination field if one already exists...
406                    if let Some(existing_ref) = SField::field_ref(&doc.graph, &dest, '.', Some(obj)) {
407                        // Clone the field
408                        let clone;
409                        if let Some(field) = SData::get::<SField>(&doc.graph, &field_ref) {
410                            clone = field.clone();
411                        } else {
412                            return Ok(SVal::Bool(false));
413                        }
414
415                        // remove this field from the graph (everywhere, I know... no way to be sure that field is on obj)
416                        doc.graph.remove_data(&field_ref, None);
417
418                        if let Some(existing) = SData::get_mut::<SField>(&mut doc.graph, existing_ref) {
419                            existing.merge(&clone)?;
420                        }
421                    } else {
422                        // Get the new field name from the destination path
423                        let mut dest_path = dest.split('.').collect::<Vec<&str>>();
424                        let new_field_name = dest_path.pop().unwrap();
425
426                        // If there is a new destination node, do that
427                        if dest_path.len() > 0 {
428                            // Clone the field
429                            let mut clone;
430                            if let Some(field) = SData::get::<SField>(&doc.graph, &field_ref) {
431                                clone = field.clone();
432                            } else {
433                                return Ok(SVal::Bool(false));
434                            }
435
436                            // remove this field from the graph (everywhere, I know... no way to be sure that field is on obj)
437                            doc.graph.remove_data(&field_ref, None);
438
439                            clone.name = new_field_name.to_owned();
440                            let dest_node_path = dest_path.join(".");
441                            let dest_ref = doc.graph.ensure_nodes(&dest_node_path, '.', true, Some(obj.clone()));
442
443                            // If field is an object, move the object to the destination also and rename
444                            match &clone.value {
445                                SVal::Object(nref) => {
446                                    doc.graph.rename_node(nref, new_field_name);
447
448                                    let id_path: HashSet<String> = HashSet::from_iter(nref.id_path(&doc.graph).into_iter());
449                                    if !id_path.contains(&dest_ref.id) && !dest_ref.is_child_of(&doc.graph, &nref) {
450                                        doc.graph.move_node(nref, &dest_ref);
451                                    }
452                                },
453                                _ => {}
454                            }
455
456                            SData::insert_new_id(&mut doc.graph, &dest_ref, Box::new(clone), &field_ref.id); // keep same id
457                        } else {
458                            // We've only renamed the field, so do that only
459                            let mut rename_node = None;
460                            if let Some(field) = SData::get_mut::<SField>(&mut doc.graph, field_ref) {
461                                field.name = new_field_name.to_owned();
462
463                                // If field is an object, rename
464                                match &field.value {
465                                    SVal::Object(nref) => {
466                                        rename_node = Some(nref.clone());
467                                    },
468                                    _ => {}
469                                }
470                            }
471                            if let Some(rename_node) = rename_node {
472                                doc.graph.rename_node(&rename_node, new_field_name);
473                            }
474                        }
475                    }
476                    return Ok(SVal::Bool(true));
477                }
478                Ok(SVal::Bool(false))
479            },
480            // Delete a field (path), starting at this object.
481            // Signature: Object.removeField(obj, path: str, remove_obj: bool): bool
482            "removeField" => {
483                if parameters.len() < 1 {
484                    return Err(SError::obj(pid, &doc, "removeField", "invalid arguments - field path not found"));
485                }
486                let mut remove_obj = false;
487                if parameters.len() > 1 {
488                    remove_obj = parameters.pop().unwrap().truthy();
489                }
490                let path = parameters.pop().unwrap().to_string();
491
492                if let Some(field_ref) = SField::field_ref(&doc.graph, &path, '.', Some(obj)) {
493                    if !doc.perms.can_write_field(&doc.graph, &field_ref, Some(obj)) {
494                        return Ok(SVal::Bool(false));
495                    }
496                    if let Some(field) = SData::get::<SField>(&doc.graph, &field_ref) {
497                        if remove_obj && field.value.is_object() {
498                            match field.value.clone().unbox() {
499                                SVal::Object(nref) => {
500                                    doc.types.drop_types_for(&nref, &doc.graph);
501                                    doc.graph.remove_node(nref);
502                                },
503                                _ => {}
504                            }
505                        }
506                    }
507
508                    if path.contains('.') {
509                        // remove from everywhere
510                        doc.graph.remove_data(field_ref, None);
511                    } else {
512                        // remove only from this node (potentially everywhere)
513                        doc.graph.remove_data(field_ref, Some(obj));
514                    }
515                    return Ok(SVal::Bool(true));
516                }
517                Ok(SVal::Bool(false))
518            },
519            // Delete a function (path), starting at this object.
520            // Signature: Object.removeFunc(obj, path: str): bool
521            "removeFunc" => {
522                if parameters.len() < 1 {
523                    return Err(SError::obj(pid, &doc, "removeFunc", "invalid arguments - func path not found"));
524                }
525                let path = parameters.pop().unwrap().to_string();
526
527                if let Some(func_ref) = SFunc::func_ref(&doc.graph, &path, '.', Some(obj)) {
528                    if !doc.perms.can_write_func(&doc.graph, &func_ref, Some(obj)) {
529                        return Ok(SVal::Bool(false));
530                    }
531                    if path.contains('.') {
532                        doc.graph.remove_data(func_ref, None);
533                    } else {
534                        doc.graph.remove_data(func_ref, Some(obj));
535                    }
536                    return Ok(SVal::Bool(true));
537                }
538                Ok(SVal::Bool(false))
539            },
540            "name" => {
541                if let Some(node) = obj.node(&doc.graph) {
542                    return Ok(SVal::String(node.name.clone()));
543                }
544                Err(SError::obj(pid, &doc, "name", "could not find object"))
545            },
546            "id" => {
547                Ok(SVal::String(obj.id.clone()))
548            },
549            "parent" => {
550                if let Some(node) = obj.node(&doc.graph) {
551                    if let Some(parent) = &node.parent {
552                        return Ok(SVal::Object(parent.clone()));
553                    }
554                }
555                Ok(SVal::Null)
556            },
557            // Return this objects prototype object (if any)
558            "prototype" => {
559                if let Some(prototype) = SPrototype::get(&doc.graph, obj) {
560                    return Ok(SVal::Object(prototype.node_ref()));
561                }
562                Ok(SVal::Null)
563            },
564            // Set this objects prototype explicitly.
565            "setPrototype" => {
566                if parameters.len() < 1 {
567                    return Err(SError::obj(pid, &doc, "setPrototype", "invalid arguments - object prototype not found"));
568                }
569                match &parameters[0] {
570                    SVal::Object(nref) => {
571                        if let Some(prototype_ref) = SPrototype::get_ref(&doc.graph, obj) {
572                            if let Some(prototype) = SData::get_mut::<SPrototype>(&mut doc.graph, prototype_ref) {
573                                prototype.prototype = nref.id.clone();
574                            }
575                        } else {
576                            let prototype = SPrototype::new(nref);
577                            SData::insert_new(&mut doc.graph, obj, Box::new(prototype));
578                        }
579                        return Ok(SVal::Void);
580                    },
581                    SVal::Boxed(val) => {
582                        let val = val.lock().unwrap();
583                        let val = val.deref();
584                        match val {
585                            SVal::Object(nref) => {
586                                if let Some(prototype_ref) = SPrototype::get_ref(&doc.graph, obj) {
587                                    if let Some(prototype) = SData::get_mut::<SPrototype>(&mut doc.graph, prototype_ref) {
588                                        prototype.prototype = nref.id.clone();
589                                    }
590                                } else {
591                                    let prototype = SPrototype::new(nref);
592                                    SData::insert_new(&mut doc.graph, obj, Box::new(prototype));
593                                }
594                                return Ok(SVal::Void);
595                            },
596                            _ => {}
597                        }
598                    },
599                    _ => {}
600                }
601                Err(SError::obj(pid, &doc, "setPrototype", "invalid arguments - object prototype not found"))
602            },
603            // Get the attributes for the prototype of this object (if any)
604            "prototypeAttributes" => {
605                if let Some(prototype) = SPrototype::get(&doc.graph, obj) {
606                    let attributes = prototype.attributes(&doc);
607                    let mut map = BTreeMap::new();
608                    for (k, v) in attributes {
609                        map.insert(SVal::String(k), v);
610                    }
611                    return Ok(SVal::Map(map));
612                }
613                Ok(SVal::Null)
614            }
615            // Return this objects root object.
616            // Signature: Object.root(obj): obj
617            "root" => {
618                if let Some(root) = obj.root(&doc.graph) {
619                    return Ok(SVal::Object(root.node_ref()));
620                }
621                Ok(SVal::Null)
622            },
623            // Is this object a root object?
624            "isRoot" => {
625                if let Some(node) = obj.node(&doc.graph) {
626                    Ok(SVal::Bool(node.parent.is_none()))
627                } else {
628                    Ok(SVal::Bool(true)) // unreachable case
629                }
630            },
631            "path" => {
632                Ok(SVal::String(obj.path(&doc.graph).replace('/', ".")))
633            },
634            "children" => {
635                if let Some(node) = obj.node(&doc.graph) {
636                    let mut children = Vec::new();
637                    for child in &node.children {
638                        children.push(SVal::Object(child.clone()));
639                    }
640                    return Ok(SVal::Array(children));
641                }
642                Ok(SVal::Array(vec![]))
643            },
644            "typename" => {
645                let typename = SVal::Object(obj.clone()).type_name(&doc.graph);
646                Ok(SVal::String(typename))
647            },
648            "typestack" => {
649                let typestack = SVal::Object(obj.clone()).type_stack(&doc.graph);
650                Ok(SVal::Array(typestack.into_iter().map(|x| SVal::String(x)).collect()))
651            },
652            "instanceOf" => {
653                if parameters.len() < 1 {
654                    return Err(SError::obj(pid, &doc, "instanceOf", "invalid arguments - type string not found"));
655                }
656                let iof = SVal::Object(obj.clone()).instance_of(&doc.graph, &parameters[0].to_string());
657                Ok(SVal::Bool(iof))
658            },
659            "upcast" => {
660                if let Some(prototype_ref) = SPrototype::get_ref(&doc.graph, obj) {
661                    let mut parent_id = String::default();
662                    if let Some(prototype) = SData::get::<SPrototype>(&doc.graph, &prototype_ref) {
663                        if let Some(node) = prototype.node_ref().node(&doc.graph) {
664                            if let Some(parent_ref) = &node.parent {
665                                if let Some(parent) = parent_ref.node(&doc.graph) {
666                                    if parent.name != "__stof__" && parent.name != "prototypes" {
667                                        parent_id = parent.id.clone();
668                                    }
669                                }
670                            }
671                        }
672                    }
673                    if parent_id.len() > 0 {
674                        if let Some(prototype) = SData::get_mut::<SPrototype>(&mut doc.graph, prototype_ref) {
675                            prototype.prototype = parent_id;
676                            return Ok(SVal::Bool(true));
677                        }
678                    }
679                }
680                Ok(SVal::Bool(false))
681            },
682            // Remove the prototype for this object if any, returning whether one was removed or not.
683            "removePrototype" => {
684                if let Some(prototype) = SPrototype::get_ref(&doc.graph, obj) {
685                    doc.graph.remove_data(prototype, Some(obj));
686                    return Ok(SVal::Bool(true));
687                }
688                Ok(SVal::Bool(false))
689            },
690
691            // dump this object (testing)
692            "dbg_dump" => {
693                if let Some(node) = obj.node(&doc.graph) {
694                    let dump = node.dump(&doc.graph, 0, true);
695                    println!("{dump}");
696                }
697                Ok(SVal::Void)
698            },
699
700            /*****************************************************************************
701             * Search for fields.
702             *****************************************************************************/
703            // Searches both up and down, looking for the closest field with a given name.
704            // Returns null if not found, otherwise the value and distance from this object.
705            // Signature: Object.search(obj, field_name: str, search_parent_children: bool = true, obj_ignore_set: vec = []): null | (unknown, int)
706            "search" => {
707                if parameters.len() < 1 {
708                    return Err(SError::obj(pid, &doc, "search", "invalid arguments - field name not found"));
709                }
710                let up = self.operate(pid, doc, "searchUp", obj, parameters)?;
711                
712                let mut down_parameters = vec![parameters[0].clone(), SVal::Number(SNum::I64(0))];
713                if parameters.len() > 2 { down_parameters.push(parameters[2].clone()); }
714                let down = self.operate(pid, doc, "searchDown", obj, &mut down_parameters)?;
715                
716                if !up.is_empty() && !down.is_empty() {
717                    match up {
718                        SVal::Tuple(up) => {
719                            match down {
720                                SVal::Tuple(down) => {
721                                    let down_lte_up = down.last().unwrap().lte(up.last().unwrap())?;
722                                    if down_lte_up.truthy() {
723                                        // down is closer (or equal) - return down
724                                        return Ok(SVal::Tuple(down));
725                                    } else {
726                                        // up is closer
727                                        return Ok(SVal::Tuple(up));
728                                    }
729                                },
730                                _ => {}
731                            }
732                        },
733                        _ => {}
734                    }
735                } else if !up.is_empty() {
736                    return Ok(up);
737                } else if !down.is_empty() {
738                    return Ok(down);
739                }
740                Ok(SVal::Null)
741            },
742
743            // Search upwards through our parents to find the closest field with a name.
744            // Returns null if not found, otherwise the value and distance from this object.
745            // Signature: Object.searchUp(obj, field_name: str, search_parent_children: bool = true, obj_ignore_set: vec = []): null | (unknown, int)
746            "searchUp" => {
747                if parameters.len() < 1 {
748                    return Err(SError::obj(pid, &doc, "searchUp", "invalid arguments - field name not found"));
749                }
750                let mut obj_ignore_set = HashSet::new();
751                if parameters.len() > 2 {
752                    match &parameters[2] {
753                        SVal::Array(vals) => {
754                            for v in vals {
755                                match v {
756                                    SVal::Object(nref) => {
757                                        obj_ignore_set.insert(nref.id.clone());
758                                    },
759                                    _ => {}
760                                }
761                            }
762                        },
763                        _ => {}
764                    }
765                }
766
767                let field_name = parameters[0].to_string();
768
769                if !obj_ignore_set.contains(&obj.id) {
770                    if let Some(field_ref) = SField::field_ref(&doc.graph, &field_name, '.', Some(obj)) {
771                        if doc.perms.can_read_field(&doc.graph, &field_ref, Some(obj)) {
772                            if let Some(field) = SData::get::<SField>(&doc.graph, &field_ref) {
773                                return Ok(SVal::Tuple(vec![field.value.clone(), SVal::Number(SNum::I64(0))]));
774                            }
775                        }
776                    }
777                }
778                obj_ignore_set.insert(obj.id.clone()); // already searched in this object
779
780                // Search up, through parent nodes
781                let mut allow_parent_children = true;
782                let mut child_finds = Vec::new();
783                if parameters.len() > 1 {
784                    allow_parent_children = parameters[1].truthy();
785                }
786
787                let mut parent = None;
788                let mut parent_field = None;
789                let mut parent_field_ref = None;
790                if let Some(node) = obj.node(&doc.graph) {
791                    parent = node.parent.clone();
792                }
793                let mut parent_distance = 1;
794                while parent.is_some() {
795                    if let Some(parent) = &parent {
796                        if !obj_ignore_set.contains(&parent.id) {
797                            if let Some(field_ref) = SField::field_ref(&doc.graph, &field_name, '.', Some(parent)) {
798                                if let Some(field) = SData::get::<SField>(&doc.graph, &field_ref) {
799                                    parent_field = Some(field);
800                                }
801                                parent_field_ref = Some(field_ref);
802                                break;
803                            }
804                            obj_ignore_set.insert(parent.id.clone()); // just searched this parent
805                        }
806                    }
807                    if allow_parent_children {
808                        let mut params = vec![parameters[0].clone(), SVal::Number(SNum::I64(parent_distance))];
809                        if obj_ignore_set.len() > 0 {
810                            let vals = obj_ignore_set.iter().map(|id| SVal::Object(SNodeRef::new(id))).collect();
811                            params.push(SVal::Array(vals));
812                        }
813                        let val = self.operate(pid, doc, "searchDown", &parent.clone().unwrap(), &mut params)?;
814                        if !val.is_empty() {
815                            child_finds.push(val);
816                        }
817                    }
818                    
819                    if let Some(node) = parent.unwrap().node(&doc.graph) {
820                        parent = node.parent.clone();
821                    } else {
822                        parent = None;
823                    }
824                    parent_distance += 1;
825                }
826
827                // sort child finds by distance if any
828                if child_finds.len() > 0 {
829                    child_finds.sort_by(|a, b| {
830                        match a {
831                            SVal::Tuple(a) => {
832                                match b {
833                                    SVal::Tuple(b) => {
834                                        let a_lt = a.last().unwrap().lt(b.last().unwrap()).unwrap();
835                                        if a_lt.truthy() {
836                                            return Ordering::Less;
837                                        }
838                                        let a_gt = a.last().unwrap().gt(b.last().unwrap()).unwrap();
839                                        if a_gt.truthy() {
840                                            return Ordering::Greater;
841                                        }
842                                    },
843                                    _ => {}
844                                }
845                            },
846                            _ => {}
847                        }
848                        Ordering::Equal
849                    });
850                }
851
852                if let Some(field) = parent_field { // will be the first in-line value found if any
853                    if child_finds.len() > 0 {
854                        // compare the closest child find to this parent find, preferring the parent find when equal
855                        let first_child = child_finds.remove(0);
856                        let mut return_child = false;
857                        match &first_child {
858                            SVal::Tuple(tup) => {
859                                match &tup.last().unwrap() {
860                                    SVal::Number(num) => {
861                                        let dist = num.int();
862                                        if dist < parent_distance {
863                                            return_child = true;
864                                        }
865                                    },
866                                    _ => {}
867                                }
868                            },
869                            _ => {}
870                        }
871                        if return_child {
872                            return Ok(first_child);
873                        }
874                    }
875                    if let Some(parent_ref) = parent_field_ref {
876                        if doc.perms.can_read_field(&doc.graph, &parent_ref, Some(obj)) {
877                            return Ok(SVal::Tuple(vec![field.value.clone(), SVal::Number(SNum::I64(parent_distance))]));
878                        }
879                    }
880                } else if child_finds.len() > 0 {
881                    return Ok(child_finds.remove(0));
882                }
883                Ok(SVal::Null)
884            },
885
886            // Search downwards through our children to find the closest field with a name.
887            // Returns null if not found, otherwise the value and distance from this object.
888            // Signature: Object.searchDown(obj, field_name: str, current_dist: int = 0, obj_ignore_set: vec = []): null | (unknown, int)
889            "searchDown" => {
890                if parameters.len() < 1 {
891                    return Err(SError::obj(pid, &doc, "searchDown", "invalid arguments - field name not found"));
892                }
893
894                let mut current_distance = 0;
895                if parameters.len() > 1 {
896                    match &parameters[1] {
897                        SVal::Number(num) => {
898                            current_distance = num.int();
899                        },
900                        _ => {}
901                    }
902                }
903                let mut obj_ignore_set = HashSet::new();
904                if parameters.len() > 2 {
905                    match &parameters[2] {
906                        SVal::Array(vals) => {
907                            for v in vals {
908                                match v {
909                                    SVal::Object(nref) => {
910                                        obj_ignore_set.insert(nref.id.clone());
911                                    },
912                                    _ => {}
913                                }
914                            }
915                        },
916                        _ => {}
917                    }
918                }
919
920                let field_name = parameters[0].to_string();
921
922                if !obj_ignore_set.contains(&obj.id) {
923                    if let Some(field_ref) = SField::field_ref(&doc.graph, &field_name, '.', Some(obj)) {
924                        if let Some(field) = SData::get::<SField>(&doc.graph, &field_ref) {
925                            if doc.perms.can_read_field(&doc.graph, &field_ref, Some(obj)) {
926                                return Ok(SVal::Tuple(vec![field.value.clone(), SVal::Number(SNum::I64(current_distance))]));
927                            }
928                        }
929                    }
930                }
931                let children;
932                if let Some(node) = obj.node(&doc.graph) {
933                    children = node.children.clone();
934                } else {
935                    return Ok(SVal::Null); // no children to consider
936                }
937                let mut params = vec![parameters[0].clone(), SVal::Number(SNum::I64(current_distance + 1))];
938                if obj_ignore_set.len() > 0 {
939                    params.push(parameters[2].clone());
940                }
941                let mut child_finds = Vec::new();
942                for child in children {
943                    let val = self.operate(pid, doc, "searchDown", &child, &mut params)?;
944                    if !val.is_empty() {
945                        child_finds.push(val);
946                    }
947                }
948                if child_finds.len() > 0 {
949                    child_finds.sort_by(|a, b| {
950                        match a {
951                            SVal::Tuple(a) => {
952                                match b {
953                                    SVal::Tuple(b) => {
954                                        let a_lt = a.last().unwrap().lt(b.last().unwrap()).unwrap();
955                                        if a_lt.truthy() {
956                                            return Ordering::Less;
957                                        }
958                                        let a_gt = a.last().unwrap().gt(b.last().unwrap()).unwrap();
959                                        if a_gt.truthy() {
960                                            return Ordering::Greater;
961                                        }
962                                    },
963                                    _ => {}
964                                }
965                            },
966                            _ => {}
967                        }
968                        Ordering::Equal
969                    });
970                    return Ok(child_finds.remove(0));
971                }
972                Ok(SVal::Null)
973            },
974
975            /*****************************************************************************
976             * Schemafy another object with this object (as a schema).
977             *****************************************************************************/
978            // Use fields defined on this object with the #[schema(..)] attribute to control the same
979            // fields on a target object.
980            // Signature: Object.schemafy(schema: obj, target: obj, remove_invalid_fields: bool = true, remove_undefined: bool = false): bool
981            "schemafy" => {
982                if parameters.len() < 1 {
983                    return Err(SError::obj(pid, &doc, "schemafy", "invalid arguments - expecting a target object to apply this schema on"));
984                }
985
986                let target;
987                match &parameters[0] {
988                    SVal::Object(nref) => target = nref.clone(),
989                    SVal::Boxed(val) => {
990                        let val = val.lock().unwrap();
991                        let val = val.deref();
992                        match val {
993                            SVal::Object(nref) => target = nref.clone(),
994                            _ => {
995                                return Err(SError::obj(pid, &doc, "schemafy", "invalid arguments - expecting a target object to apply this schema on"));
996                            }
997                        }
998                    },
999                    _ => {
1000                        return Err(SError::obj(pid, &doc, "schemafy", "invalid arguments - expecting a target object to apply this schema on"));
1001                    }
1002                }
1003
1004                let mut remove_invalid_fields = true;
1005                if parameters.len() > 1 {
1006                    remove_invalid_fields = parameters[1].truthy();
1007                }
1008
1009                let mut remove_undefined_fields = false;
1010                if parameters.len() > 2 {
1011                    remove_undefined_fields = parameters[2].truthy();
1012                }
1013
1014                // Get all of the fields on this schema to apply on the target object
1015                // Field name -> #[schema] attribute value
1016                let mut schema_fields: HashMap<String, SVal> = HashMap::new();
1017                let mut schema_field_names = HashSet::new();
1018                for schema_field in SField::fields(&doc.graph, obj) {
1019                    if let Some(schema_val) = schema_field.attributes.get("schema") {
1020                        schema_fields.insert(schema_field.name.clone(), schema_val.clone());
1021                    }
1022                    if remove_undefined_fields {
1023                        schema_field_names.insert(schema_field.name.clone());
1024                    }
1025                }
1026
1027                // Iterate over all schema fields, applying them to the target object as needed
1028                let mut valid = true;
1029                for (field, value) in schema_fields {
1030                    if !self.schemafy_field(doc, pid, &obj, &target, &field, value, remove_invalid_fields, remove_undefined_fields) {
1031                        valid = false;
1032                        if remove_invalid_fields {
1033                            // remove this field, removing the object as well if present
1034                            self.operate(pid, doc, "removeField", &target, &mut vec![SVal::String(field), SVal::Bool(true)])?;
1035                        }
1036                    }
1037                }
1038
1039                // Remove all fields on the target that are not defined in the schema fields
1040                // Make sure to do this after all validations of course...
1041                if remove_undefined_fields {
1042                    let mut to_remove = Vec::new();
1043                    for field_ref in SField::field_refs(&doc.graph, &target) {
1044                        if let Some(field) = SData::get::<SField>(&doc.graph, &field_ref) {
1045                            if !schema_field_names.contains(&field.name) {
1046                                to_remove.push(field.name.clone());
1047                            }
1048                        }
1049                    }
1050                    for field_name in to_remove {
1051                        // remove this field, removing the object as well if present
1052                        self.operate(pid, doc, "removeField", &target, &mut vec![SVal::String(field_name), SVal::Bool(true)])?;
1053                    }
1054                }
1055                Ok(SVal::Bool(valid))
1056            },
1057
1058            /*****************************************************************************
1059             * Execute this object (declarative tasking).
1060             *****************************************************************************/
1061            // Exec calls all sub-objects and functions with a #[run] attribute.
1062            // All functions are expected to not have any parameters.
1063            //
1064            // Each #[run] attribute can have a number value that will be the order of execution.
1065            // By default (if no ordering is given), objects will run before functions.
1066            //
1067            // Signature: Object.exec(obj): void
1068            "exec" => {
1069                // order, object, ID (ref)
1070                let mut tasks = Vec::new();
1071                for field in SField::fields(&doc.graph, obj) {
1072                    if let Some(attr) = field.attributes.get("run") {
1073                        let mut order = -2;
1074                        match &attr {
1075                            SVal::Number(num) => {
1076                                order = num.int();
1077                            },
1078                            SVal::Boxed(val) => {
1079                                let val = val.lock().unwrap();
1080                                let val = val.deref();
1081                                match val {
1082                                    SVal::Number(num) => {
1083                                        order = num.int();
1084                                    },
1085                                    _ => {}
1086                                }
1087                            },
1088                            _ => {}
1089                        }
1090                        match &field.value {
1091                            SVal::Object(other) => {
1092                                tasks.push((order, SVal::Object(other.clone())));
1093                            },
1094                            SVal::Array(_) => {
1095                                tasks.push((order, field.value.clone()));
1096                            },
1097                            SVal::Set(_) => {
1098                                tasks.push((order, field.value.clone()));
1099                            },
1100                            SVal::Map(_) => {
1101                                tasks.push((order, field.value.clone()));
1102                            },
1103                            SVal::Boxed(val) => {
1104                                let val = val.lock().unwrap();
1105                                let val = val.deref();
1106                                match val {
1107                                    SVal::Object(other) => {
1108                                        tasks.push((order, SVal::Object(other.clone())));
1109                                    },
1110                                    SVal::Array(_) => {
1111                                        tasks.push((order, val.clone()));
1112                                    },
1113                                    SVal::Set(_) => {
1114                                        tasks.push((order, val.clone()));
1115                                    },
1116                                    SVal::Map(_) => {
1117                                        tasks.push((order, val.clone()));
1118                                    },
1119                                    _ => {}
1120                                }
1121                            },
1122                            _ => {}
1123                        }
1124                    }
1125                }
1126
1127                let mut func_names = HashSet::new();
1128                for func_ref in SFunc::func_refs(&doc.graph, obj) {
1129                    if let Some(func) = SData::get::<SFunc>(&doc.graph, &func_ref) {
1130                        func_names.insert(func.name.clone());
1131                        if let Some(attr) = func.attributes.get("run") {
1132                            let mut order = -1;
1133                            match &attr {
1134                                SVal::Number(num) => {
1135                                    order = num.int();
1136                                },
1137                                SVal::Boxed(val) => {
1138                                    let val = val.lock().unwrap();
1139                                    let val = val.deref();
1140                                    match val {
1141                                        SVal::Number(num) => {
1142                                            order = num.int();
1143                                        },
1144                                        _ => {}
1145                                    }
1146                                },
1147                                _ => {}
1148                            }
1149                            tasks.push((order, SVal::FnPtr(func_ref)));
1150                        }
1151                    }
1152                }
1153                for nref in SPrototype::get_stack(&doc.graph, obj) {
1154                    for func_ref in SFunc::func_refs(&doc.graph, &nref) {
1155                        if let Some(func) = SData::get::<SFunc>(&doc.graph, &func_ref) {
1156                            if !func_names.contains(&func.name) {
1157                                func_names.insert(func.name.clone());
1158                                if let Some(attr) = func.attributes.get("run") {
1159                                    let mut order = -1;
1160                                    match &attr {
1161                                        SVal::Number(num) => {
1162                                            order = num.int();
1163                                        },
1164                                        SVal::Boxed(val) => {
1165                                            let val = val.lock().unwrap();
1166                                            let val = val.deref();
1167                                            match val {
1168                                                SVal::Number(num) => {
1169                                                    order = num.int();
1170                                                },
1171                                                _ => {}
1172                                            }
1173                                        },
1174                                        _ => {}
1175                                    }
1176                                    tasks.push((order, SVal::FnPtr(func_ref)));
1177                                }
1178                            }
1179                        }
1180                    }
1181                }
1182
1183                tasks.sort_by(|a, b| a.0.cmp(&b.0));
1184                doc.push_self(pid, obj.clone());
1185                for task in tasks {
1186                    self.exec_val(task.1, doc, pid, parameters)?;
1187                }
1188                doc.pop_self(pid);
1189
1190                Ok(SVal::Void)
1191            },
1192
1193            /*****************************************************************************
1194             * Copy object helpers.
1195             *****************************************************************************/
1196            // Make this object a shallow copy of the referenced object by attaching all of its fields.
1197            // Signature: Object.shallowCopy(obj, to_copy: obj): void
1198            "shallowCopy" => {
1199                if parameters.len() < 1 {
1200                    return Err(SError::obj(pid, &doc, "shallowCopy", "invalid arguments - object to copy not found"));
1201                }
1202                match &parameters[0] {
1203                    SVal::Object(to_copy) => {
1204                        let data;
1205                        if let Some(copy_node) = to_copy.node(&doc.graph) {
1206                            data = copy_node.data.clone();
1207                        } else {
1208                            return Err(SError::obj(pid, &doc, "shallowCopy", "invalid arguments - object to copy does not exist"));
1209                        }
1210                        for data in data {
1211                            doc.graph.put_data_ref(obj, data);
1212                        }
1213                        Ok(SVal::Void)
1214                    },
1215                    SVal::Boxed(val) => {
1216                        let val = val.lock().unwrap();
1217                        let val = val.deref();
1218                        match val {
1219                            SVal::Object(to_copy) => {
1220                                let data;
1221                                if let Some(copy_node) = to_copy.node(&doc.graph) {
1222                                    data = copy_node.data.clone();
1223                                } else {
1224                                    return Err(SError::obj(pid, &doc, "shallowCopy", "invalid arguments - object to copy does not exist"));
1225                                }
1226                                for data in data {
1227                                    doc.graph.put_data_ref(obj, data);
1228                                }
1229                                Ok(SVal::Void)
1230                            },
1231                            _ => {
1232                                Err(SError::obj(pid, &doc, "shallowCopy", "invalid arguments - object to copy not found"))
1233                            }
1234                        }
1235                    },
1236                    _ => {
1237                        Err(SError::obj(pid, &doc, "shallowCopy", "invalid arguments - object to copy not found"))
1238                    }
1239                }
1240            },
1241            // Make this object a deep copy of the referenced object (fields only).
1242            // Captures attributes, deep copies sub objects, etc.. as well.
1243            // Signature: Object.deepCopyFields(obj, to_copy: obj): void
1244            "deepCopyFields" => {
1245                if parameters.len() < 1 {
1246                    return Err(SError::obj(pid, &doc, "deepCopyFields", "invalid arguments - object to copy not found"));
1247                }
1248                match &parameters[0] {
1249                    SVal::Object(to_copy) => {
1250                        let mut fields = Vec::new();
1251                        for field in SField::fields(&doc.graph, to_copy) {
1252                            fields.push(field.clone());
1253                        }
1254                        for field in fields {
1255                            if field.is_object() {
1256                                match field.value.clone().unbox() {
1257                                    SVal::Object(nref) => {
1258                                        let deep_copy = SField::new_object(&mut doc.graph, &field.name, obj);
1259                                        let to_copy = SVal::Object(nref);
1260                                        self.operate(pid, doc, "deepCopyFields", &deep_copy, &mut vec![to_copy])?;
1261                                    },
1262                                    _ => {}
1263                                }
1264                            } else {
1265                                SData::insert_new(&mut doc.graph, obj, Box::new(field));
1266                            }
1267                        }
1268                        Ok(SVal::Void)
1269                    },
1270                    SVal::Boxed(val) => {
1271                        let val = val.lock().unwrap();
1272                        let val = val.deref();
1273                        match val {
1274                            SVal::Object(to_copy) => {
1275                                let mut fields = Vec::new();
1276                                for field in SField::fields(&doc.graph, to_copy) {
1277                                    fields.push(field.clone());
1278                                }
1279                                for field in fields {
1280                                    if field.is_object() {
1281                                        match field.value.clone().unbox() {
1282                                            SVal::Object(nref) => {
1283                                                let deep_copy = SField::new_object(&mut doc.graph, &field.name, obj);
1284                                                let to_copy = SVal::Object(nref);
1285                                                self.operate(pid, doc, "deepCopyFields", &deep_copy, &mut vec![to_copy])?;
1286                                            },
1287                                            _ => {}
1288                                        }
1289                                    } else {
1290                                        SData::insert_new(&mut doc.graph, obj, Box::new(field));
1291                                    }
1292                                }
1293                                Ok(SVal::Void)
1294                            },
1295                            _ => {
1296                                Err(SError::obj(pid, &doc, "deepCopyFields", "invalid arguments - object to copy not found"))
1297                            }
1298                        }
1299                    },
1300                    _ => {
1301                        Err(SError::obj(pid, &doc, "deepCopyFields", "invalid arguments - object to copy not found"))
1302                    }
1303                }
1304            },
1305            _ => {
1306                Err(SError::obj(pid, &doc, "NotFound", &format!("{} is not a function in the Object Library", name)))
1307            }
1308        }
1309    }
1310
1311    /// Execute a value.
1312    fn exec_val(&self, value: SVal, doc: &mut SDoc, pid: &str, parameters: &mut Vec<SVal>) -> Result<(), SError> {
1313        match value {
1314            SVal::Object(nref) => {
1315                self.operate(pid, doc, "exec", &nref, parameters)?;
1316            },
1317            SVal::FnPtr(dref) => {
1318                SFunc::call(&dref, pid, doc, vec![], false)?;
1319            },
1320            SVal::Array(vals) => {
1321                for val in vals {
1322                    self.exec_val(val, doc, pid, parameters)?;
1323                }
1324            },
1325            SVal::Set(vals) => {
1326                for val in vals {
1327                    self.exec_val(val, doc, pid, parameters)?;
1328                }
1329            },
1330            SVal::Map(map) => {
1331                for val in map {
1332                    self.exec_val(val.1, doc, pid, parameters)?;
1333                }
1334            },
1335            _ => {}
1336        }
1337        Ok(())
1338    }
1339
1340    /// Schemafy an individual field on a target object.
1341    fn schemafy_field(&self, doc: &mut SDoc, pid: &str, schema: &SNodeRef, target: &SNodeRef, field: &str, value: SVal, remove_invalid: bool, remove_undefined: bool) -> bool {
1342        match value {
1343            SVal::Void |
1344            SVal::Null => {
1345                // if the field is an object on the schema and on the target, use the object to "schemafy" with
1346                let mut schema_field_object = None;
1347                let mut target_field_object = None;
1348                if let Some(field) = SField::field(&doc.graph, field, '.', Some(schema)) {
1349                    match &field.value {
1350                        SVal::Object(nref) => {
1351                            schema_field_object = Some(nref.clone());
1352                        },
1353                        SVal::Boxed(val) => {
1354                            let val = val.lock().unwrap();
1355                            let val = val.deref();
1356                            match val {
1357                                SVal::Object(nref) => {
1358                                    schema_field_object = Some(nref.clone());
1359                                },
1360                                _ => {}
1361                            }
1362                        },
1363                        _ => {}
1364                    }
1365                }
1366                if let Some(field) = SField::field(&doc.graph, field, '.', Some(target)) {
1367                    match &field.value {
1368                        SVal::Object(nref) => {
1369                            target_field_object = Some(nref.clone());
1370                        },
1371                        SVal::Boxed(val) => {
1372                            let val = val.lock().unwrap();
1373                            let val = val.deref();
1374                            match val {
1375                                SVal::Object(nref) => {
1376                                    target_field_object = Some(nref.clone());
1377                                },
1378                                _ => {}
1379                            }
1380                        },
1381                        _ => {}
1382                    }
1383                }
1384                if let Some(schema_object) = schema_field_object {
1385                    if let Some(target_object) = target_field_object {
1386                        if let Ok(res) = self.operate(pid, doc, "schemafy", &schema_object, &mut vec![SVal::Object(target_object), SVal::Bool(remove_invalid), SVal::Bool(remove_undefined)]) {
1387                            return res.truthy();
1388                        }
1389                        return false;
1390                    }
1391                }
1392                true
1393            },
1394            SVal::Object(another_schema) => {
1395                let mut another_value = None;
1396                if let Some(field) = SField::field(&doc.graph, field, '.', Some(&another_schema)) {
1397                    if let Some(schema_val) = field.attributes.get("schema") {
1398                        another_value = Some(schema_val.clone());
1399                    }
1400                }
1401                if let Some(value) = another_value {
1402                    return self.schemafy_field(doc, pid, &another_schema, target, field, value, remove_invalid, remove_undefined);
1403                }
1404                false // other schema does not implement/define this field as a schema
1405            },
1406            SVal::FnPtr(func_ref) => {
1407                let mut parameters = Vec::new();
1408                let mut valid_check = false; // func result treated as truthy valid or a value to set on the field..
1409                let mut value_index = None;
1410                let mut box_target_field_value = false;
1411                let mut boxed_field_name = None;
1412                
1413                if let Some(func) = SData::get::<SFunc>(&doc.graph, &func_ref) {
1414                    if func.rtype.is_bool() { valid_check = true; }
1415
1416                    let mut added_target = false;
1417                    let mut added_schema = false;
1418                    let mut added_field = false;
1419                    let mut added_value = false;
1420                    for param_index in 0..func.params.len() {
1421                        let mut done = false;
1422                        let param = &func.params[param_index];
1423
1424                        // First two objects in parameters are the target, then the schema
1425                        if param.ptype.is_object() && param.name != "value" && (!added_target || !added_schema) {
1426                            if param.name == "target" || (param.name != "schema" && !added_target) {
1427                                added_target = true;
1428                                parameters.push(SVal::Object(target.clone()));
1429                            } else if param.name == "schema" || !added_schema {
1430                                parameters.push(SVal::Object(schema.clone()));
1431                                added_schema = true;
1432                            }
1433                            done = true;
1434                        }
1435
1436                        // First string parameter is the field name
1437                        if !done && !added_field && param.name != "value" && param.ptype.is_string() {
1438                            if param.ptype.is_boxed() {
1439                                let val = SVal::String(field.to_string());
1440                                let boxed = SVal::Boxed(Arc::new(SMutex::new(val)));
1441                                parameters.push(boxed.clone());
1442                                boxed_field_name = Some(boxed);
1443                            } else {
1444                                parameters.push(SVal::String(field.to_string()));
1445                            }
1446                            added_field = true;
1447                            done = true;
1448                        }
1449
1450                        // Any other value is the target's current field value
1451                        if !done && !added_value {
1452                            value_index = Some(param_index);
1453                            added_value = true;
1454                            
1455                            // If parameter is boxed, go ahead and box the target object's field
1456                            if param.ptype.is_boxed() {
1457                                box_target_field_value = true;    
1458                            }
1459                        }
1460                    }
1461                }
1462                
1463                // Add the target's field value if needed, boxing the field if specified
1464                if let Some(value_index) = value_index {
1465                    if let Some(field_ref) = SField::field_ref(&doc.graph, field, '.', Some(target)) {
1466                        if let Some(field) = SData::get_mut::<SField>(&mut doc.graph, &field_ref) {
1467                            if box_target_field_value {
1468                                field.value.to_box_ref();
1469                            }
1470                            parameters.insert(value_index, field.value.clone());
1471                        }
1472                    } else {
1473                        // no field, so insert null in place of it
1474                        parameters.insert(value_index, SVal::Null);
1475                    }
1476                }
1477                
1478                if let Ok(res) = SFunc::call(&func_ref, pid, doc, parameters, true) {
1479                    if let Some(field_ref) = SField::field_ref(&doc.graph, field, '.', Some(target)) {
1480                        if let Some(field) = SData::get_mut::<SField>(&mut doc.graph, &field_ref) {
1481                            if let Some(new_name) = boxed_field_name {
1482                                field.name = new_name.to_string();
1483                            }
1484                            if valid_check {
1485                                return res.truthy();
1486                            } else if !res.is_empty() {
1487                                field.value = res;
1488                            }
1489                        }
1490                    } else if valid_check {
1491                        return res.truthy();
1492                    } else if !res.is_empty() {
1493                        // create a new field since one doesn't exist, but we have a new value for it!
1494                        let mut field = SField::new(field, res);
1495                        if let Some(new_name) = boxed_field_name {
1496                            field.name = new_name.to_string();
1497                        }
1498                        SData::insert_new(&mut doc.graph, target, Box::new(field));
1499                    }
1500                    return true;
1501                }
1502                false
1503            },
1504            SVal::Tuple(vals) |
1505            SVal::Array(vals) => {
1506                for val in vals {
1507                    if !self.schemafy_field(doc, pid, schema, target, field, val, remove_invalid, remove_undefined) {
1508                        return false;
1509                    }
1510                }
1511                true
1512            },
1513            SVal::Set(set) => {
1514                for val in set {
1515                    if !self.schemafy_field(doc, pid, schema, target, field, val, remove_invalid, remove_undefined) {
1516                        return false;
1517                    }
1518                }
1519                true
1520            },
1521            SVal::Boxed(val) => {
1522                let cloned;
1523                {
1524                    let val = val.lock().unwrap();
1525                    cloned = val.deref().clone();
1526                }
1527                self.schemafy_field(doc, pid, schema, target, field, cloned, remove_invalid, remove_undefined)
1528            },
1529            _ => false, // no other value is valid as a schema attribute
1530        }
1531    }
1532}
1533impl Library for ObjectLibrary {
1534    fn scope(&self) -> String {
1535        "Object".into()
1536    }
1537    fn call(&self, pid: &str, doc: &mut SDoc, name: &str, parameters: &mut Vec<SVal>) -> Result<SVal, SError> {
1538        if parameters.len() > 0 {
1539            match name {
1540                "toString" => {
1541                    return Ok(SVal::String(parameters[0].print(doc)));
1542                },
1543                "or" => {
1544                    for param in parameters.drain(..) {
1545                        if !param.is_empty() {
1546                            return Ok(param);
1547                        }
1548                    }
1549                    return Ok(SVal::Null);
1550                },
1551                _ => {}
1552            }
1553
1554            let mut params;
1555            if parameters.len() > 1 {
1556                params = parameters.drain(1..).collect();
1557            } else {
1558                params = Vec::new();
1559            }
1560            match &parameters[0] {
1561                SVal::Object(nref) => {
1562                    return self.operate(pid, doc, name, nref, &mut params);
1563                },
1564                SVal::Boxed(val) => {
1565                    let val = val.lock().unwrap();
1566                    let val = val.deref();
1567                    match val {
1568                        SVal::Object(nref) => {
1569                            return self.operate(pid, doc, name, nref, &mut params);
1570                        },
1571                        _ => {
1572                            return Err(SError::obj(pid, &doc, "InvalidArgument", "object argument not found"));
1573                        }
1574                    }
1575                },
1576                _ => {
1577                    return Err(SError::obj(pid, &doc, "InvalidArgument", "object argument not found"));
1578                }
1579            }
1580        } else {
1581            return Err(SError::obj(pid, &doc, "InvalidArgument", "object argument not found"));
1582        }
1583    }
1584}