use std::collections::HashMap;
use crate::{Hit, HitError, Id, IndexEntryProperty, ObjectValue, ObjectValues, Reference};
fn uuid() -> String {
(0..10)
.map(|_| (0x20u8 + (rand::random::<f32>() * 96.0) as u8) as char)
.collect()
}
fn _copy_object(
hit: &mut Hit,
id: &Id,
target: IndexEntryProperty,
before_id: Option<String>,
references_to_update: &mut Vec<ReferenceToUpdate>,
updated_ids: &mut HashMap<Id, Id>,
) -> Result<Id, HitError> {
let new_id = uuid();
let entry = hit.get(id).ok_or(HitError::IDNotFound(id.to_string(), "copy_object entry".to_string()))?;
let model = hit
.get_model(id)
.ok_or(HitError::ModelDoesNotExist(id.into()))?;
let mut values = ObjectValues::new();
for (field_name, _field) in model.get_fields().iter() {
let value = entry.get(field_name);
match value {
crate::ObjectValue::Reference(r) => {
references_to_update.push(ReferenceToUpdate {
target: IndexEntryProperty {
id: new_id.clone(),
property: field_name.clone(),
},
reference: r.id,
vec: false,
});
}
crate::ObjectValue::VecReference(refs) => {
for r in refs.iter() {
references_to_update.push(ReferenceToUpdate {
target: IndexEntryProperty {
id: new_id.clone(),
property: field_name.clone(),
},
reference: r.id.clone(),
vec: true,
});
}
}
crate::ObjectValue::SubObject(_) => {}
crate::ObjectValue::VecSubObjects(_) => {}
_ => {
values.insert(field_name.to_string(), value.clone());
}
}
}
hit.insert(
entry.get_model().get_name(),
&new_id,
values,
target,
before_id,
)?;
updated_ids.insert(id.clone(), new_id.clone());
for (field_name, _field) in model.get_fields().iter() {
let value = entry.get(field_name);
match value {
crate::ObjectValue::Reference(_) => {}
crate::ObjectValue::VecReference(_) => {}
crate::ObjectValue::SubObject(subobject) => {
_copy_object(
hit,
&subobject.id,
IndexEntryProperty {
id: new_id.clone(),
property: field_name.clone(),
},
None,
references_to_update,
updated_ids,
)?;
}
crate::ObjectValue::VecSubObjects(subobjects) => {
for subobject in subobjects.iter() {
_copy_object(
hit,
&subobject.id,
IndexEntryProperty {
id: new_id.clone(),
property: field_name.clone(),
},
None,
references_to_update,
updated_ids,
)?;
}
}
_ => {}
}
}
Ok(new_id)
}
struct ReferenceToUpdate {
target: IndexEntryProperty,
reference: Id,
vec: bool,
}
fn get_updated_id(id: Id, updated_ids: &HashMap<Id, Id>) -> Id {
match updated_ids.get(&id.clone()) {
Some(updated_id) => updated_id.clone(),
None => id,
}
}
pub fn copy_object(
hit: &mut Hit,
id: &Id,
target: IndexEntryProperty,
before_id: Option<String>,
) -> Result<Id, HitError> {
let mut references_to_update: Vec<ReferenceToUpdate> = vec![];
let mut updated_ids: HashMap<Id, Id> = HashMap::new();
let new_id = {
_copy_object(
hit,
id,
target,
before_id,
&mut references_to_update,
&mut updated_ids,
)?
};
for reference in references_to_update.iter() {
let updated_id = get_updated_id(reference.reference.clone(), &updated_ids);
if reference.vec {
hit.insert_reference(&updated_id, reference.target.clone(), None)?;
} else {
hit.set(
&reference.target.id,
&reference.target.property,
ObjectValue::Reference(Reference { id: updated_id }),
)?;
}
}
Ok(new_id)
}