use super::MemoryConfigStore;
use crate::storage::Error;
use pbbson::Model;
use pbbson::bson::Bson;
use std::fmt::Debug;
use std::str::FromStr;
pub(crate) async fn perform<B: Clone + Debug + FromStr + Send + Sync + ToString>(
this: &MemoryConfigStore<B>,
bucket: B,
model: Model,
) -> Result<Model, Error> {
let model_id = model.id()?;
let doc_holder = this.doc_holder(&bucket)?;
let mut docs = doc_holder
.docs
.lock()
.map_err(|e| Error::failed_precondition(e.to_string()))?;
let existing = match docs.get(&model_id) {
None => return Err(Error::not_found("No such record")),
Some(existing) => existing.clone(),
};
docs.insert(model.id()?, model.clone());
let belongs_tos = this
.belongs_tos_by_bucket
.get(&bucket.to_string())
.cloned()
.unwrap_or_default();
for belongs_to in belongs_tos.iter() {
let existing_fk = existing.get(&belongs_to.local);
let new_fk = model.get(&belongs_to.local).cloned();
match existing_fk {
None => continue,
Some(existing_fk_value) => {
if existing_fk == new_fk.as_ref() {
continue;
}
let existing_fk_id = match existing_fk_value.as_str() {
Some(existing_fk_id) => existing_fk_id,
None => continue,
};
let remote_bucket = this.doc_holder(&belongs_to.remote)?;
let mut remote_docs = remote_bucket
.docs
.lock()
.map_err(|e| Error::failed_precondition(e.to_string()))?;
let remote_model = match remote_docs.get(existing_fk_id) {
None => continue,
Some(remote_model) => remote_model,
};
if let Some(inverse) = belongs_to.inverse.as_ref() {
let remote_fk_value = remote_model.get(inverse.clone());
if let Some(Bson::Array(array)) = remote_fk_value {
let array: Vec<_> = array
.iter()
.filter(|remote_fk| *remote_fk != &Bson::String(model_id.clone()))
.collect();
let mut remote_model = remote_model.clone();
remote_model.insert(inverse.clone(), array);
remote_docs.insert(existing_fk_id.to_string(), remote_model);
}
}
}
}
}
for belongs_to in belongs_tos.iter() {
let existing_fk = existing.get(&belongs_to.local);
let new_fk = model.get(&belongs_to.local);
match new_fk {
None => continue,
Some(new_fk_value) => {
if existing_fk == new_fk {
continue;
}
let new_fk_object_id = match new_fk_value.as_str() {
None => continue,
Some(s) => s,
};
let remote_bucket = this.doc_holder(&belongs_to.remote)?;
let mut remote_docs = remote_bucket
.docs
.lock()
.map_err(|e| Error::failed_precondition(e.to_string()))?;
let remote_model = match remote_docs.get(new_fk_object_id) {
None => continue,
Some(remote_model) => remote_model,
};
if let Some(inverse) = belongs_to.inverse.as_ref() {
let remote_fk_value = remote_model.get(inverse.clone());
if let Some(Bson::Array(array)) = remote_fk_value {
let mut array = array.clone();
array.push(Bson::String(model_id.clone()));
let mut remote_model = remote_model.clone();
remote_model.insert(inverse.clone(), array);
remote_docs.insert(new_fk_object_id.to_string(), remote_model);
}
}
}
}
}
for has_many in this
.has_manys_by_bucket
.get(&bucket.to_string())
.cloned()
.unwrap_or_default()
{
let existing_local_value = existing.get(&has_many.local);
let local_value = model.get(&has_many.local);
match (existing_local_value, local_value) {
(Some(Bson::Array(existing_array)), Some(Bson::Array(array))) => {
match has_many.inverse.clone() {
None => {
}
Some(inverse) => {
let remote_bucket = this.doc_holder(&has_many.remote)?;
let mut remote_docs = remote_bucket
.docs
.lock()
.map_err(|e| Error::failed_precondition(e.to_string()))?;
for existing_id in existing_array.iter() {
if array.contains(existing_id) {
continue;
}
if let Bson::String(id) = existing_id {
let foreign_model = match remote_docs.get_mut(id.as_str()) {
None => {
continue;
}
Some(foreign_model) => foreign_model,
};
match foreign_model.get_mut(&inverse) {
Some(Bson::Array(_array)) => {
}
_ => {
foreign_model.remove(&inverse);
}
};
}
}
for id in array.iter() {
if existing_array.contains(id) {
continue;
}
if let Bson::String(id) = id {
let foreign_model = match remote_docs.get_mut(id.as_str()) {
None => {
continue;
}
Some(foreign_model) => foreign_model,
};
match foreign_model.get_mut(&inverse) {
Some(Bson::Array(_array)) => {
}
_ => {
foreign_model.insert(&inverse, Bson::String(model_id.clone()));
}
};
}
}
}
}
}
_ => {
log::error!("Unknown fk type: {local_value:?}");
}
}
}
Ok(model)
}