diff --git a/radicle-cli/src/commands/patch/edit.rs b/radicle-cli/src/commands/patch/edit.rs index 3b49f79e..a3ac8631 100644 --- a/radicle-cli/src/commands/patch/edit.rs +++ b/radicle-cli/src/commands/patch/edit.rs @@ -1,6 +1,6 @@ use super::*; -use radicle::cob::{self, patch, resolve_embed}; +use radicle::cob::{self, patch}; use radicle::crypto; use radicle::prelude::*; use radicle::storage::git::Repository; @@ -19,12 +19,11 @@ pub fn run( let Ok(patch) = patches.get_mut(patch_id) else { anyhow::bail!("Patch `{patch_id}` not found"); }; - let (title, description) = term::patch::get_edit_message(message, &patch)?; match revision_id { - Some(id) => edit_revision(patch, id, title, description, repository, &signer), - None => edit_root(patch, title, description, repository, &signer), + Some(id) => edit_revision(patch, id, title, description, &signer), + None => edit_root(patch, title, description, &signer), } } @@ -32,7 +31,6 @@ fn edit_root( mut patch: patch::PatchMut<'_, '_, Repository, cob::cache::StoreWriter>, title: String, description: String, - repository: &Repository, signer: &G, ) -> anyhow::Result<()> where @@ -56,11 +54,7 @@ where let (root, _) = patch.root(); let target = patch.target(); - let embeds = patch - .embeds() - .iter() - .filter_map(|embed| resolve_embed(repository, embed.clone())) - .collect::>(); + let embeds = patch.embeds().to_owned(); patch.transaction("Edit root", signer, |tx| { if let Some(t) = title { @@ -80,17 +74,12 @@ fn edit_revision( revision: patch::RevisionId, mut title: String, description: String, - repository: &Repository, signer: &G, ) -> anyhow::Result<()> where G: crypto::Signer, { - let embeds = patch - .embeds() - .iter() - .filter_map(|embed| resolve_embed(repository, embed.clone())) - .collect::>(); + let embeds = patch.embeds().to_owned(); let description = if description.is_empty() { title } else { diff --git a/radicle-cob/src/backend/git/change.rs b/radicle-cob/src/backend/git/change.rs index 7efcaab2..9943c92d 100644 --- a/radicle-cob/src/backend/git/change.rs +++ b/radicle-cob/src/backend/git/change.rs @@ -341,7 +341,7 @@ fn write_commit( fn write_manifest( repo: &git2::Repository, manifest: &store::Manifest, - embeds: Vec, + embeds: Vec>, contents: &NonEmpty>, ) -> Result { let mut root = repo.treebuilder(None)?; @@ -372,10 +372,10 @@ fn write_manifest( let mut embeds_tree = repo.treebuilder(None)?; for embed in embeds { - let oid = repo.blob(&embed.content)?; + let oid = embed.content; let path = PathBuf::from(embed.name); - embeds_tree.insert(path, oid, git2::FileMode::Blob.into())?; + embeds_tree.insert(path, *oid, git2::FileMode::Blob.into())?; } let oid = embeds_tree.write()?; diff --git a/radicle-cob/src/change/store.rs b/radicle-cob/src/change/store.rs index a5f71ad5..3a5c24c6 100644 --- a/radicle-cob/src/change/store.rs +++ b/radicle-cob/src/change/store.rs @@ -45,7 +45,7 @@ pub struct Template { pub type_name: TypeName, pub tips: Vec, pub message: String, - pub embeds: Vec, + pub embeds: Vec>, pub contents: NonEmpty>, } @@ -191,6 +191,22 @@ pub struct Embed> { pub content: T, } +impl> Embed { + /// Create a new embed. + pub fn store( + name: impl ToString, + content: &[u8], + repo: &git2::Repository, + ) -> Result { + let oid = repo.blob(content)?; + + Ok(Self { + name: name.to_string(), + content: T::from(oid.into()), + }) + } +} + impl Embed> { /// Get the object id of the embedded content. pub fn oid(&self) -> Oid { diff --git a/radicle-cob/src/object/collaboration/create.rs b/radicle-cob/src/object/collaboration/create.rs index 43f50aae..68181754 100644 --- a/radicle-cob/src/object/collaboration/create.rs +++ b/radicle-cob/src/object/collaboration/create.rs @@ -18,7 +18,7 @@ pub struct Create { /// The message to add when creating this object. pub message: String, /// Embedded content. - pub embeds: Vec, + pub embeds: Vec>, /// COB version. pub version: Version, } diff --git a/radicle-cob/src/object/collaboration/update.rs b/radicle-cob/src/object/collaboration/update.rs index 7de291c4..8a632cc1 100644 --- a/radicle-cob/src/object/collaboration/update.rs +++ b/radicle-cob/src/object/collaboration/update.rs @@ -34,7 +34,7 @@ pub struct Update { /// The message to add when updating this object. pub message: String, /// Embedded files. - pub embeds: Vec, + pub embeds: Vec>, } /// Update an existing [`CollaborativeObject`]. diff --git a/radicle/src/cob/common.rs b/radicle/src/cob/common.rs index 0d69de15..9c7eec68 100644 --- a/radicle/src/cob/common.rs +++ b/radicle/src/cob/common.rs @@ -8,10 +8,8 @@ use base64::prelude::{Engine, BASE64_STANDARD}; use localtime::LocalTime; use serde::{Deserialize, Serialize}; -use crate::cob::Embed; use crate::git::Oid; use crate::prelude::{Did, PublicKey}; -use crate::storage::ReadRepository; /// Timestamp used for COB operations. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] @@ -348,24 +346,6 @@ impl TryFrom<&Uri> for DataUri { } } -/// Resolve an embed with a URI to one with actual data. -pub fn resolve_embed(repo: &impl ReadRepository, embed: Embed) -> Option>> { - DataUri::try_from(&embed.content) - .ok() - .map(|content| Embed { - name: embed.name.clone(), - content: content.into(), - }) - .or_else(|| { - Oid::try_from(&embed.content).ok().and_then(|oid| { - repo.blob(oid).ok().map(|blob| Embed { - name: embed.name, - content: blob.content().to_vec(), - }) - }) - }) -} - /// The result of an authorization check on an COB action. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Authorization { diff --git a/radicle/src/cob/identity.rs b/radicle/src/cob/identity.rs index bf21aad7..0266ab44 100644 --- a/radicle/src/cob/identity.rs +++ b/radicle/src/cob/identity.rs @@ -3,7 +3,7 @@ use std::{fmt, ops::Deref, str::FromStr}; use crypto::{PublicKey, Signature}; use once_cell::sync::Lazy; -use radicle_cob::{ObjectId, TypeName}; +use radicle_cob::{Embed, ObjectId, TypeName}; use radicle_crypto::{Signer, Verified}; use radicle_git_ext as git_ext; use radicle_git_ext::Oid; @@ -15,7 +15,7 @@ use crate::{ cob::{ op, store, store::{Cob, CobAction, Transaction}, - ActorId, Timestamp, + ActorId, Timestamp, Uri, }, identity::{ doc::{Doc, DocError, RepoId}, @@ -187,10 +187,12 @@ impl Identity { signer: &G, ) -> Result, cob::store::Error> { let mut store = cob::store::Store::open(store)?; - let (id, identity) = - Transaction::::initial("Initialize identity", &mut store, signer, |tx| { - tx.revision("Initial revision", "", doc, None, signer) - })?; + let (id, identity) = Transaction::::initial( + "Initialize identity", + &mut store, + signer, + |tx, repo| tx.revision("Initial revision", "", doc, None, repo, signer), + )?; Ok(IdentityMut { id, @@ -825,22 +827,26 @@ impl store::Transaction { pub fn redact(&mut self, revision: RevisionId) -> Result<(), store::Error> { self.push(Action::RevisionRedact { revision }) } +} +impl store::Transaction { pub fn revision( &mut self, title: impl ToString, description: impl ToString, doc: &Doc, parent: Option, + repo: &R, signer: &G, ) -> Result<(), store::Error> { - let (blob, content, signature) = doc.sign(signer).map_err(store::Error::Identity)?; + let (blob, bytes, signature) = doc.sign(signer).map_err(store::Error::Identity)?; + // Store document blob in repository. + let embed = + Embed::::store("radicle.json", &bytes, repo.raw()).map_err(store::Error::Git)?; + debug_assert_eq!(embed.content, Uri::from(blob)); // Make sure we pre-computed the correct OID for the blob. // Identity document. - self.embed([cob::Embed { - name: String::from("radicle.json"), - content, - }])?; + self.embed([embed])?; // Revision metadata. self.push(Action::Revision { @@ -891,10 +897,10 @@ where ) -> Result where G: Signer, - F: FnOnce(&mut Transaction) -> Result<(), store::Error>, + F: FnOnce(&mut Transaction, &R) -> Result<(), store::Error>, { let mut tx = Transaction::default(); - operations(&mut tx)?; + operations(&mut tx, self.store.as_ref())?; let (doc, commit) = tx.commit(message, self.id, &mut self.store, signer)?; self.identity = doc; @@ -912,8 +918,8 @@ where signer: &G, ) -> Result { let parent = self.current; - let id = self.transaction("Propose revision", signer, |tx| { - tx.revision(title, description, doc, Some(parent), signer) + let id = self.transaction("Propose revision", signer, |tx, repo| { + tx.revision(title, description, doc, Some(parent), repo, signer) })?; Ok(id) @@ -929,7 +935,7 @@ where let revision = self.revision(revision).ok_or(Error::NotFound(id))?; let signature = revision.sign(signer)?; - self.transaction("Accept revision", signer, |tx| tx.accept(id, signature)) + self.transaction("Accept revision", signer, |tx, _| tx.accept(id, signature)) } /// Reject an active revision. @@ -938,7 +944,7 @@ where revision: RevisionId, signer: &G, ) -> Result { - self.transaction("Reject revision", signer, |tx| tx.reject(revision)) + self.transaction("Reject revision", signer, |tx, _| tx.reject(revision)) } /// Redact a revision. @@ -947,7 +953,7 @@ where revision: RevisionId, signer: &G, ) -> Result { - self.transaction("Redact revision", signer, |tx| tx.redact(revision)) + self.transaction("Redact revision", signer, |tx, _| tx.redact(revision)) } /// Edit an active revision's title or description. @@ -958,7 +964,7 @@ where description: String, signer: &G, ) -> Result { - self.transaction("Edit revision", signer, |tx| { + self.transaction("Edit revision", signer, |tx, _| { tx.edit(revision, title, description) }) } diff --git a/radicle/src/cob/issue.rs b/radicle/src/cob/issue.rs index dbdf453a..088cfd3a 100644 --- a/radicle/src/cob/issue.rs +++ b/radicle/src/cob/issue.rs @@ -472,15 +472,13 @@ impl store::Transaction { &mut self, id: CommentId, body: impl ToString, - embeds: Vec, + embeds: Vec>, ) -> Result<(), store::Error> { - let hashed = embeds.iter().map(|e| e.hashed()).collect(); - - self.embed(embeds)?; + self.embed(embeds.clone())?; self.push(Action::CommentEdit { id, body: body.to_string(), - embeds: hashed, + embeds, }) } @@ -506,15 +504,13 @@ impl store::Transaction { &mut self, body: S, reply_to: CommentId, - embeds: Vec, + embeds: Vec>, ) -> Result<(), store::Error> { - let hashed = embeds.iter().map(|e| e.hashed()).collect(); - - self.embed(embeds)?; + self.embed(embeds.clone())?; self.push(Action::Comment { body: body.to_string(), reply_to: Some(reply_to), - embeds: hashed, + embeds, }) } @@ -545,16 +541,15 @@ impl store::Transaction { fn thread( &mut self, body: S, - embeds: impl IntoIterator, + embeds: impl IntoIterator>, ) -> Result<(), store::Error> { let embeds = embeds.into_iter().collect::>(); - let hashed = embeds.iter().map(|e| e.hashed()).collect(); - self.embed(embeds)?; + self.embed(embeds.clone())?; self.push(Action::Comment { body: body.to_string(), reply_to: None, - embeds: hashed, + embeds, }) } } @@ -613,7 +608,7 @@ where pub fn edit_description( &mut self, description: impl ToString, - embeds: impl IntoIterator, + embeds: impl IntoIterator>, signer: &G, ) -> Result { let (id, _) = self.root(); @@ -633,7 +628,7 @@ where &mut self, body: S, reply_to: CommentId, - embeds: impl IntoIterator, + embeds: impl IntoIterator>, signer: &G, ) -> Result { self.transaction("Comment", signer, |tx| { @@ -646,7 +641,7 @@ where &mut self, id: CommentId, body: S, - embeds: impl IntoIterator, + embeds: impl IntoIterator>, signer: &G, ) -> Result { self.transaction("Edit comment", signer, |tx| { @@ -777,7 +772,7 @@ where description: impl ToString, labels: &[Label], assignees: &[Did], - embeds: impl IntoIterator, + embeds: impl IntoIterator>, cache: &'g mut C, signer: &G, ) -> Result, Error> @@ -785,7 +780,7 @@ where G: Signer, C: cob::cache::Update, { - let (id, issue) = Transaction::initial("Create issue", &mut self.raw, signer, |tx| { + let (id, issue) = Transaction::initial("Create issue", &mut self.raw, signer, |tx, _| { tx.thread(description, embeds)?; tx.edit(title)?; @@ -1511,17 +1506,22 @@ mod test { fn test_embeds() { let test::setup::NodeWithRepo { node, repo, .. } = test::setup::NodeWithRepo::default(); let mut issues = Cache::no_cache(&*repo).unwrap(); + + let content1 = repo.backend.blob(b"Hello World!").unwrap(); + let content2 = repo.backend.blob(b"Hello Radicle!").unwrap(); + let content3 = repo.backend.blob(b"body { color: red }").unwrap(); + let embed1 = Embed { name: String::from("example.html"), - content: b"Hello World!".to_vec(), + content: Uri::from(Oid::from(content1)), }; let embed2 = Embed { name: String::from("style.css"), - content: b"body { color: red }".to_vec(), + content: Uri::from(Oid::from(content2)), }; let embed3 = Embed { name: String::from("bin"), - content: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + content: Uri::from(Oid::from(content3)), }; let mut issue = issues .create( @@ -1552,34 +1552,35 @@ mod test { let e2 = &c0.embeds()[1]; let e3 = &c1.embeds()[0]; - let b1 = repo.blob(Oid::try_from(&e1.content).unwrap()).unwrap(); - let b2 = repo.blob(Oid::try_from(&e2.content).unwrap()).unwrap(); - let b3 = repo.blob(Oid::try_from(&e3.content).unwrap()).unwrap(); + let b1 = Oid::try_from(&e1.content).unwrap(); + let b2 = Oid::try_from(&e2.content).unwrap(); + let b3 = Oid::try_from(&e3.content).unwrap(); - assert_eq!(b1.content(), &embed1.content); - assert_eq!(b2.content(), &embed2.content); - assert_eq!(b3.content(), &embed3.content); - - assert_eq!(b1.is_binary(), false); - assert_eq!(b2.is_binary(), false); - assert_eq!(b3.is_binary(), true); + assert_eq!(b1, Oid::try_from(&embed1.content).unwrap()); + assert_eq!(b2, Oid::try_from(&embed2.content).unwrap()); + assert_eq!(b3, Oid::try_from(&embed3.content).unwrap()); } #[test] fn test_embeds_edit() { let test::setup::NodeWithRepo { node, repo, .. } = test::setup::NodeWithRepo::default(); let mut issues = Cache::no_cache(&*repo).unwrap(); + + let content1 = repo.backend.blob(b"Hello World!").unwrap(); + let content1_edited = repo.backend.blob(b"Hello Radicle!").unwrap(); + let content2 = repo.backend.blob(b"body { color: red }").unwrap(); + let embed1 = Embed { name: String::from("example.html"), - content: b"Hello World!".to_vec(), + content: Uri::from(Oid::from(content1)), }; let embed1_edited = Embed { - name: String::from("example.html"), - content: b"Hello Radicle!".to_vec(), + name: String::from("style.css"), + content: Uri::from(Oid::from(content1_edited)), }; let embed2 = Embed { - name: String::from("style.css"), - content: b"body { color: red }".to_vec(), + name: String::from("bin"), + content: Uri::from(Oid::from(content2)), }; let mut issue = issues .create( @@ -1603,10 +1604,10 @@ mod test { assert_eq!(c0.embeds().len(), 1); let e1 = &c0.embeds()[0]; - let b1 = repo.blob(Oid::try_from(&e1.content).unwrap()).unwrap(); + let b1 = Oid::try_from(&e1.content).unwrap(); - assert_eq!(e1.content, Uri::from(embed1_edited.oid())); - assert_eq!(b1.content(), &embed1_edited.content); + assert_eq!(e1.content, embed1_edited.content); + assert_eq!(b1, Oid::try_from(&embed1_edited.content).unwrap()); } #[test] diff --git a/radicle/src/cob/issue/cache.rs b/radicle/src/cob/issue/cache.rs index 80a3122b..30f3eea4 100644 --- a/radicle/src/cob/issue/cache.rs +++ b/radicle/src/cob/issue/cache.rs @@ -8,7 +8,7 @@ use crate::cob; use crate::cob::cache; use crate::cob::cache::{Remove, StoreReader, StoreWriter, Update}; use crate::cob::store; -use crate::cob::{Embed, Label, ObjectId, TypeName}; +use crate::cob::{Embed, Label, ObjectId, TypeName, Uri}; use crate::crypto::Signer; use crate::prelude::{Did, RepoId}; use crate::storage::{HasRepoId, ReadRepository, RepositoryError, SignRepository, WriteRepository}; @@ -79,7 +79,7 @@ impl<'a, R, C> Cache, C> { description: impl ToString, labels: &[Label], assignees: &[Did], - embeds: impl IntoIterator, + embeds: impl IntoIterator>, signer: &G, ) -> Result, super::Error> where diff --git a/radicle/src/cob/patch.rs b/radicle/src/cob/patch.rs index 4c22bf3c..4008ff73 100644 --- a/radicle/src/cob/patch.rs +++ b/radicle/src/cob/patch.rs @@ -475,7 +475,7 @@ impl Patch { } /// Patch embeds. - pub fn embeds(&self) -> &Vec> { + pub fn embeds(&self) -> &[Embed] { let (_, r) = self.root(); r.embeds() } @@ -1410,7 +1410,7 @@ impl Revision { self.description.iter() } - pub fn embeds(&self) -> &Vec> { + pub fn embeds(&self) -> &[Embed] { &self.description.last().embeds } @@ -1663,15 +1663,13 @@ impl store::Transaction { &mut self, revision: RevisionId, description: impl ToString, - embeds: Vec, + embeds: Vec>, ) -> Result<(), store::Error> { - let hashed = embeds.iter().map(|e| e.hashed()).collect(); - - self.embed(embeds)?; + self.embed(embeds.clone())?; self.push(Action::RevisionEdit { revision, description: description.to_string(), - embeds: hashed, + embeds, }) } @@ -1718,17 +1716,15 @@ impl store::Transaction { body: S, reply_to: Option, location: Option, - embeds: Vec, + embeds: Vec>, ) -> Result<(), store::Error> { - let hashed = embeds.iter().map(|e| e.hashed()).collect(); - - self.embed(embeds)?; + self.embed(embeds.clone())?; self.push(Action::RevisionComment { revision, body: body.to_string(), reply_to, location, - embeds: hashed, + embeds, }) } @@ -1738,16 +1734,14 @@ impl store::Transaction { revision: RevisionId, comment: CommentId, body: S, - embeds: Vec, + embeds: Vec>, ) -> Result<(), store::Error> { - let hashed = embeds.iter().map(|e| e.hashed()).collect(); - - self.embed(embeds)?; + self.embed(embeds.clone())?; self.push(Action::RevisionCommentEdit { revision, comment, body: body.to_string(), - embeds: hashed, + embeds, }) } @@ -1783,17 +1777,15 @@ impl store::Transaction { body: S, location: Option, reply_to: Option, - embeds: Vec, + embeds: Vec>, ) -> Result<(), store::Error> { - let hashed = embeds.iter().map(|e| e.hashed()).collect(); - self.embed(embeds)?; - + self.embed(embeds.clone())?; self.push(Action::ReviewComment { review, body: body.to_string(), location, reply_to, - embeds: hashed, + embeds, }) } @@ -1821,16 +1813,14 @@ impl store::Transaction { review: ReviewId, comment: EntryId, body: S, - embeds: Vec, + embeds: Vec>, ) -> Result<(), store::Error> { - let hashed = embeds.iter().map(|e| e.hashed()).collect(); - - self.embed(embeds)?; + self.embed(embeds.clone())?; self.push(Action::ReviewCommentEdit { review, comment, body: body.to_string(), - embeds: hashed, + embeds, }) } @@ -2011,7 +2001,7 @@ where &mut self, revision: RevisionId, description: String, - embeds: impl IntoIterator, + embeds: impl IntoIterator>, signer: &G, ) -> Result { self.transaction("Edit revision", signer, |tx| { @@ -2045,7 +2035,7 @@ where body: S, reply_to: Option, location: Option, - embeds: impl IntoIterator, + embeds: impl IntoIterator>, signer: &G, ) -> Result { self.transaction("Comment", signer, |tx| { @@ -2079,7 +2069,7 @@ where revision: RevisionId, comment: CommentId, body: S, - embeds: impl IntoIterator, + embeds: impl IntoIterator>, signer: &G, ) -> Result { self.transaction("Edit comment", signer, |tx| { @@ -2120,7 +2110,7 @@ where body: S, location: Option, reply_to: Option, - embeds: impl IntoIterator, + embeds: impl IntoIterator>, signer: &G, ) -> Result { self.transaction("Review comment", signer, |tx| { @@ -2140,7 +2130,7 @@ where review: ReviewId, comment: EntryId, body: S, - embeds: impl IntoIterator, + embeds: impl IntoIterator>, signer: &G, ) -> Result { self.transaction("Edit review comment", signer, |tx| { @@ -2567,7 +2557,7 @@ where where C: cob::cache::Update, { - let (id, patch) = Transaction::initial("Create patch", &mut self.raw, signer, |tx| { + let (id, patch) = Transaction::initial("Create patch", &mut self.raw, signer, |tx, _| { tx.revision(description, base, oid)?; tx.edit(title, target)?; diff --git a/radicle/src/cob/store.rs b/radicle/src/cob/store.rs index c34a7fb0..0984ee73 100644 --- a/radicle/src/cob/store.rs +++ b/radicle/src/cob/store.rs @@ -9,7 +9,7 @@ use radicle_cob::CollaborativeObject; use serde::{Deserialize, Serialize}; use crate::cob::op::Op; -use crate::cob::{Create, Embed, EntryId, ObjectId, TypeName, Update, Updated, Version}; +use crate::cob::{Create, Embed, EntryId, ObjectId, TypeName, Update, Updated, Uri, Version}; use crate::git; use crate::prelude::*; use crate::storage::git as storage; @@ -92,6 +92,10 @@ pub enum Error { NotFound(TypeName, ObjectId), #[error("signed refs: {0}")] SignRefs(#[from] storage::Error), + #[error("invalid or unknown embed URI: {0}")] + EmbedUri(Uri), + #[error(transparent)] + Git(git::raw::Error), #[error("failed to find reference '{name}': {err}")] RefLookup { name: git::RefString, @@ -148,12 +152,21 @@ where object_id: ObjectId, message: &str, actions: impl Into>, - embeds: Vec, + embeds: Vec>, signer: &G, ) -> Result, Error> { let actions = actions.into(); let related = actions.iter().flat_map(T::Action::parents).collect(); let changes = actions.try_map(encoding::encode)?; + let embeds = embeds + .into_iter() + .map(|e| { + Ok::<_, Error>(Embed { + content: git::Oid::try_from(&e.content).map_err(Error::EmbedUri)?, + name: e.name.clone(), + }) + }) + .collect::>()?; let updated = cob::update( self.repo, signer, @@ -178,12 +191,21 @@ where &self, message: &str, actions: impl Into>, - embeds: Vec, + embeds: Vec>, signer: &G, ) -> Result<(ObjectId, T), Error> { let actions = actions.into(); let parents = actions.iter().flat_map(T::Action::parents).collect(); let contents = actions.try_map(encoding::encode)?; + let embeds = embeds + .into_iter() + .map(|e| { + Ok::<_, Error>(Embed { + content: git::Oid::try_from(&e.content).map_err(Error::EmbedUri)?, + name: e.name.clone(), + }) + }) + .collect::>()?; let cob = cob::create::( self.repo, signer, @@ -263,7 +285,7 @@ where #[derive(Debug)] pub struct Transaction, R> { actions: Vec, - embeds: Vec, + embeds: Vec>, repo: PhantomData, } @@ -290,12 +312,12 @@ where ) -> Result<(ObjectId, T), Error> where G: Signer, - F: FnOnce(&mut Self) -> Result<(), Error>, + F: FnOnce(&mut Self, &R) -> Result<(), Error>, R: ReadRepository + SignRepository + cob::Store, T::Action: Serialize + Clone, { let mut tx = Transaction::default(); - operations(&mut tx)?; + operations(&mut tx, store.as_ref())?; let actions = NonEmpty::from_vec(tx.actions) .expect("Transaction::initial: transaction must contain at least one action"); @@ -311,7 +333,7 @@ where } /// Embed media into the transaction. - pub fn embed(&mut self, embeds: impl IntoIterator) -> Result<(), Error> { + pub fn embed(&mut self, embeds: impl IntoIterator>) -> Result<(), Error> { self.embeds.extend(embeds); Ok(()) diff --git a/radicle/src/storage/git.rs b/radicle/src/storage/git.rs index aa7abfa3..d65f5932 100644 --- a/radicle/src/storage/git.rs +++ b/radicle/src/storage/git.rs @@ -439,9 +439,13 @@ impl Repository { storage: &S, signer: &G, ) -> Result<(Self, git::Oid), RepositoryError> { - let (doc_oid, _) = doc.encode()?; + let (doc_oid, doc_bytes) = doc.encode()?; let id = RepoId::from(doc_oid); let repo = Self::create(paths::repository(storage, &id), id, storage.info())?; + let oid = repo.backend.blob(&doc_bytes)?; // Store document blob in repository. + + debug_assert_eq!(oid, *doc_oid); + let commit = doc.init(&repo, signer)?; Ok((repo, commit))