use std::iter;
use nonempty::NonEmpty;
use oid::Oid;
use crate::{
CollaborativeObject, Embed, Evaluate, ExtendedSignature, ObjectId, TypeName, change,
change_graph::ChangeGraph, history::EntryId,
};
use super::error;
#[derive(Debug)]
pub struct Updated<T> {
pub head: Oid,
pub object: CollaborativeObject<T>,
pub parents: Vec<EntryId>,
}
pub struct Update {
pub changes: NonEmpty<Vec<u8>>,
pub object_id: ObjectId,
pub type_name: TypeName,
pub message: String,
pub embeds: Vec<Embed<Oid>>,
}
pub fn update<T, S, G>(
storage: &S,
signer: &G,
resource: Option<Oid>,
related: Vec<Oid>,
identifier: &S::Namespace,
args: Update,
) -> Result<Updated<T>, error::Update>
where
T: Evaluate<S>,
S: crate::object::Storage,
S: change::Storage<ObjectId = Oid, Parent = Oid, Signatures = ExtendedSignature>,
G: signature::Signer<ExtendedSignature>,
{
let Update {
type_name: ref typename,
object_id,
embeds,
changes,
message,
} = args;
let existing_refs = storage
.objects(typename, &object_id)
.map_err(|err| error::Update::Refs { err: Box::new(err) })?;
let graph = ChangeGraph::load(storage, existing_refs.iter(), typename, &object_id)
.ok_or(error::Update::NoSuchObject)?;
let mut object: CollaborativeObject<T> =
graph.evaluate(storage).map_err(error::Update::evaluate)?;
let entry = storage
.store(
resource,
related,
signer,
change::Template {
tips: object.history.tips().into_iter().collect(),
embeds,
contents: changes,
type_name: typename.clone(),
message,
},
)
.map_err(Into::<error::Update>::into)?;
let head = entry.id;
let parents = entry.parents.to_vec();
object
.object
.apply(&entry, iter::empty(), storage)
.map_err(error::Update::evaluate)?;
object.history.extend(entry);
storage
.update(identifier, typename, &object_id, &head)
.map_err(|err| error::Update::Refs { err: Box::new(err) })?;
Ok(Updated {
object,
head,
parents,
})
}