use super::is_audit_field;
use crate::storage::Error;
use oauth2_client::re_exports::{Deserialize, Serialize};
use pbbson::bson::Bson;
use pbbson::{Model, prost::Message};
pub fn assign(target: &mut Model, source: &Model) {
for key in source.keys() {
if is_audit_field(key) {
continue;
}
target.insert(key, source.get(key));
}
for key in target.clone().keys() {
if is_audit_field(key) {
continue;
}
if !source.contains_field(key) {
target.insert(key, Bson::Null);
}
}
}
pub fn assign_message<T: Message + Clone + Default + for<'a> Deserialize<'a> + Serialize>(
target: &mut T,
source: &T,
) -> Result<(), Error> {
let source_model = super::model_from_message(source)?;
let mut target_model = super::model_from_message(target)?;
assign(&mut target_model, &source_model);
let new_target: T = target_model.try_into().map_err(|e| Error::internal(e.to_string()))?;
new_target.clone_into(target);
Ok(())
}
#[cfg(test)]
mod tests {
use super::assign;
use pbbson::Model;
use pbbson::bson::{Bson, DateTime, doc};
#[test]
fn can_assign_non_audit_fields() {
let source = Model::from(doc! {"name": "Joe", "age": 23_u32});
let mut target = Model::default();
assign(&mut target, &source);
assert_eq!(target.get("name"), Some(&Bson::String("Joe".to_string())));
assert_eq!(target.get("age"), Some(&Bson::Int32(23)));
}
#[test]
fn cannot_clobber_audit_fields() {
let now = DateTime::now();
let source = Model::from(doc! {"name": "Joe"});
let mut target = Model::from(doc! {
"createdAt": now,
"deletedAt": now,
"updatedAt": now,
"createdBy": "abc",
"deletedBy": "def",
"updatedBy": "ghi",
});
assign(&mut target, &source);
assert_eq!(target.get("createdAt"), Some(&Bson::DateTime(now)));
assert_eq!(target.get("deletedAt"), Some(&Bson::DateTime(now)));
assert_eq!(target.get("updatedAt"), Some(&Bson::DateTime(now)));
assert_eq!(target.get("createdBy"), Some(&Bson::String("abc".to_string())));
assert_eq!(target.get("deletedBy"), Some(&Bson::String("def".to_string())));
assert_eq!(target.get("updatedBy"), Some(&Bson::String("ghi".to_string())));
}
#[test]
fn can_unset_an_optional_field() {
let mut target = Model::from(doc! {"name": "Joe", "age": 23_u32});
let source = Model::from(doc! {"name": "Joe"});
assign(&mut target, &source);
assert_eq!(target.get("name"), Some(&Bson::String("Joe".to_string())));
assert_eq!(target.get("age"), Some(&Bson::Null));
}
}