From 01ad0fb31d77c32c5fb3434d543943848bb9938c Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Thu, 24 Nov 2022 13:36:35 +0100 Subject: [PATCH] cob: Split generic code from automerge-specific --- radicle-cli/src/commands/issue.rs | 4 +- radicle-cli/src/commands/merge.rs | 4 +- radicle-cli/src/commands/patch/common.rs | 2 +- radicle-cli/src/commands/patch/create.rs | 5 +- radicle-cli/src/commands/patch/list.rs | 4 +- radicle-cli/src/commands/review.rs | 4 +- radicle-cli/src/terminal/io.rs | 2 +- radicle-crdt/src/thread.rs | 2 +- radicle/src/cob.rs | 11 +- radicle/src/cob/automerge.rs | 10 + radicle/src/cob/{ => automerge}/doc.rs | 2 +- radicle/src/cob/automerge/issue.rs | 682 ++++++++++++++ radicle/src/cob/{ => automerge}/label.rs | 11 +- radicle/src/cob/automerge/patch.rs | 835 +++++++++++++++++ radicle/src/cob/automerge/shared.rs | 185 ++++ radicle/src/cob/{ => automerge}/store.rs | 20 +- .../src/cob/{ => automerge}/transaction.rs | 2 +- radicle/src/cob/{ => automerge}/value.rs | 20 + radicle/src/cob/common.rs | 223 +++++ radicle/src/cob/issue.rs | 681 +------------- radicle/src/cob/patch.rs | 849 +----------------- radicle/src/cob/shared.rs | 394 -------- 22 files changed, 1992 insertions(+), 1960 deletions(-) create mode 100644 radicle/src/cob/automerge.rs rename radicle/src/cob/{ => automerge}/doc.rs (99%) create mode 100644 radicle/src/cob/automerge/issue.rs rename radicle/src/cob/{ => automerge}/label.rs (93%) create mode 100644 radicle/src/cob/automerge/patch.rs create mode 100644 radicle/src/cob/automerge/shared.rs rename radicle/src/cob/{ => automerge}/store.rs (90%) rename radicle/src/cob/{ => automerge}/transaction.rs (97%) rename radicle/src/cob/{ => automerge}/value.rs (78%) create mode 100644 radicle/src/cob/common.rs delete mode 100644 radicle/src/cob/shared.rs diff --git a/radicle-cli/src/commands/issue.rs b/radicle-cli/src/commands/issue.rs index afac4bbd..102523ee 100644 --- a/radicle-cli/src/commands/issue.rs +++ b/radicle-cli/src/commands/issue.rs @@ -7,9 +7,9 @@ use anyhow::{anyhow, Context as _}; use crate::terminal as term; use crate::terminal::args::{Args, Error, Help}; +use radicle::cob::automerge::store::Store; +use radicle::cob::common::{Label, Reaction}; use radicle::cob::issue::{CloseReason, IssueId, State}; -use radicle::cob::shared::{Label, Reaction}; -use radicle::cob::store::Store; use radicle::storage::WriteStorage; pub const HELP: Help = Help { diff --git a/radicle-cli/src/commands/merge.rs b/radicle-cli/src/commands/merge.rs index 0f381ba3..81d21548 100644 --- a/radicle-cli/src/commands/merge.rs +++ b/radicle-cli/src/commands/merge.rs @@ -7,7 +7,7 @@ use anyhow::{anyhow, Context}; use crate::terminal as term; use crate::terminal::args::{Args, Error, Help}; -use radicle::cob; +use radicle::cob::automerge; use radicle::cob::patch::RevisionIx; use radicle::cob::patch::{Patch, PatchId}; use radicle::git; @@ -141,7 +141,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { .project_of(profile.id()) .context(format!("couldn't load project {} from local state", id))?; let repository = profile.storage.repository(id)?; - let cobs = cob::Store::open(*profile.id(), &repository)?; + let cobs = automerge::Store::open(*profile.id(), &repository)?; let patches = cobs.patches(); if repo.head_detached()? { diff --git a/radicle-cli/src/commands/patch/common.rs b/radicle-cli/src/commands/patch/common.rs index d8f1d982..48178096 100644 --- a/radicle-cli/src/commands/patch/common.rs +++ b/radicle-cli/src/commands/patch/common.rs @@ -1,4 +1,4 @@ -use radicle::cob::patch::{MergeTarget, Patch, PatchId, PatchStore}; +use radicle::cob::automerge::patch::{MergeTarget, Patch, PatchId, PatchStore}; use radicle::git; use radicle::git::raw::Oid; use radicle::prelude::*; diff --git a/radicle-cli/src/commands/patch/create.rs b/radicle-cli/src/commands/patch/create.rs index 68579db9..eefe4d8f 100644 --- a/radicle-cli/src/commands/patch/create.rs +++ b/radicle-cli/src/commands/patch/create.rs @@ -2,7 +2,8 @@ use std::path::Path; use anyhow::{anyhow, Context}; -use radicle::cob::patch::{MergeTarget, Patch, PatchId, PatchStore}; +use radicle::cob::automerge; +use radicle::cob::automerge::patch::{MergeTarget, Patch, PatchId, PatchStore}; use radicle::git; use radicle::git::raw::Oid; use radicle::prelude::*; @@ -51,7 +52,7 @@ pub fn run( )); let signer = term::signer(profile)?; - let cobs = radicle::cob::Store::open(profile.public_key, storage)?; + let cobs = automerge::Store::open(profile.public_key, storage)?; let patches = cobs.patches(); // `HEAD`; This is what we are proposing as a patch. diff --git a/radicle-cli/src/commands/patch/list.rs b/radicle-cli/src/commands/patch/list.rs index a1866700..3e321653 100644 --- a/radicle-cli/src/commands/patch/list.rs +++ b/radicle-cli/src/commands/patch/list.rs @@ -1,4 +1,4 @@ -use radicle::cob; +use radicle::cob::automerge; use radicle::cob::patch::{Patch, PatchId, Verdict}; use radicle::git; use radicle::prelude::*; @@ -22,7 +22,7 @@ pub fn run( } let me = *profile.id(); - let cobs = cob::Store::open(*profile.id(), storage)?; + let cobs = automerge::Store::open(*profile.id(), storage)?; let patches = cobs.patches(); let proposed = patches.proposed()?; diff --git a/radicle-cli/src/commands/review.rs b/radicle-cli/src/commands/review.rs index 50b0c7d8..34c79bf3 100644 --- a/radicle-cli/src/commands/review.rs +++ b/radicle-cli/src/commands/review.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use anyhow::{anyhow, Context}; use radicle::cob; -use radicle::cob::patch::{PatchId, RevisionIx, Verdict}; +use radicle::cob::automerge::patch::{PatchId, RevisionIx, Verdict}; use radicle::prelude::*; use radicle::rad; @@ -138,7 +138,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { let _project = repository .project_of(profile.id()) .context(format!("couldn't load project {} from local state", id))?; - let cobs = cob::Store::open(*profile.id(), &repository)?; + let cobs = cob::automerge::Store::open(*profile.id(), &repository)?; let patches = cobs.patches(); let patch_id = options.id; diff --git a/radicle-cli/src/terminal/io.rs b/radicle-cli/src/terminal/io.rs index 86832245..da7f17a4 100644 --- a/radicle-cli/src/terminal/io.rs +++ b/radicle-cli/src/terminal/io.rs @@ -3,8 +3,8 @@ use std::str::FromStr; use dialoguer::{console::style, console::Style, theme::ColorfulTheme, Input, Password}; +use radicle::cob::common::CommentId; use radicle::cob::issue::Issue; -use radicle::cob::shared::CommentId; use radicle::crypto::ssh::keystore::Passphrase; use radicle::crypto::Signer; use radicle::profile::env::RAD_PASSPHRASE; diff --git a/radicle-crdt/src/thread.rs b/radicle-crdt/src/thread.rs index aa46ac15..8419c31a 100644 --- a/radicle-crdt/src/thread.rs +++ b/radicle-crdt/src/thread.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use serde::{Deserialize, Serialize}; -use radicle::cob::shared::Reaction; +use radicle::cob::common::Reaction; use radicle::cob::Timestamp; use radicle::crypto::{PublicKey, Signature, Signer}; use radicle::hash; diff --git a/radicle/src/cob.rs b/radicle/src/cob.rs index 27291e6d..fde7ed9c 100644 --- a/radicle/src/cob.rs +++ b/radicle/src/cob.rs @@ -1,18 +1,13 @@ -pub mod doc; +pub mod automerge; +pub mod common; pub mod issue; -pub mod label; pub mod patch; -pub mod shared; -pub mod store; -pub mod transaction; -pub mod value; pub use cob::{ identity, object::collaboration::error, CollaborativeObject, Contents, Create, Entry, History, HistoryType, ObjectId, TypeName, Update, }; -pub use shared::Timestamp; -pub use store::Store; +pub use common::*; use radicle_cob as cob; use radicle_git_ext::Oid; diff --git a/radicle/src/cob/automerge.rs b/radicle/src/cob/automerge.rs new file mode 100644 index 00000000..476e5a13 --- /dev/null +++ b/radicle/src/cob/automerge.rs @@ -0,0 +1,10 @@ +pub mod doc; +pub mod issue; +pub mod label; +pub mod patch; +pub mod shared; +pub mod store; +pub mod transaction; +pub mod value; + +pub use store::Store; diff --git a/radicle/src/cob/doc.rs b/radicle/src/cob/automerge/doc.rs similarity index 99% rename from radicle/src/cob/doc.rs rename to radicle/src/cob/automerge/doc.rs index 44b52e4f..a8b1524d 100644 --- a/radicle/src/cob/doc.rs +++ b/radicle/src/cob/automerge/doc.rs @@ -6,7 +6,7 @@ use std::str::FromStr; use automerge::{Automerge, AutomergeError, ObjType}; -use crate::cob::value::{FromValue, ValueError}; +use crate::cob::automerge::value::{FromValue, ValueError}; /// Error decoding a document. #[derive(thiserror::Error, Debug)] diff --git a/radicle/src/cob/automerge/issue.rs b/radicle/src/cob/automerge/issue.rs new file mode 100644 index 00000000..6479b39d --- /dev/null +++ b/radicle/src/cob/automerge/issue.rs @@ -0,0 +1,682 @@ +#![allow(clippy::large_enum_variant)] +use std::collections::HashSet; +use std::convert::TryFrom; +use std::ops::ControlFlow; + +use automerge::{Automerge, ObjType, ScalarValue, Value}; + +use crate::cob::automerge::doc::{Document, DocumentError}; +use crate::cob::automerge::shared; +use crate::cob::automerge::shared::*; +use crate::cob::automerge::store::{Error, Store}; +use crate::cob::automerge::transaction::{Transaction, TransactionError}; +use crate::cob::automerge::value::{FromValue, ValueError}; +use crate::cob::common::*; +use crate::cob::issue::*; +use crate::cob::{Contents, History, ObjectId, Timestamp, TypeName}; +use crate::prelude::*; + +impl From for ScalarValue { + fn from(state: State) -> Self { + match state { + State::Open => ScalarValue::from("open"), + State::Closed { + reason: CloseReason::Solved, + } => ScalarValue::from("solved"), + State::Closed { + reason: CloseReason::Other, + } => ScalarValue::from("closed"), + } + } +} + +impl<'a> FromValue<'a> for State { + fn from_value(value: Value) -> Result { + let state = value.to_str().ok_or(ValueError::InvalidType)?; + + match state { + "open" => Ok(Self::Open), + "closed" => Ok(Self::Closed { + reason: CloseReason::Other, + }), + "solved" => Ok(Self::Closed { + reason: CloseReason::Solved, + }), + _ => Err(ValueError::InvalidValue(value.to_string())), + } + } +} + +impl FromHistory for Issue { + fn type_name() -> &'static TypeName { + &TYPENAME + } + + fn from_history(history: &History) -> Result { + let doc = history.traverse(Automerge::new(), |mut doc, entry| { + let bytes = entry.contents(); + match automerge::Change::from_bytes(bytes.clone()) { + Ok(change) => { + doc.apply_changes([change]).ok(); + } + Err(_err) => { + // Ignore + } + } + ControlFlow::Continue(doc) + }); + let issue = Issue::try_from(doc)?; + + Ok(issue) + } +} + +impl TryFrom<&History> for Issue { + type Error = Error; + + fn try_from(history: &History) -> Result { + Issue::from_history(history) + } +} + +impl TryFrom for Issue { + type Error = DocumentError; + + fn try_from(doc: Automerge) -> Result { + let doc = Document::new(&doc); + let obj_id = doc.get_id(automerge::ObjId::Root, "issue")?; + let title = doc.get(&obj_id, "title")?; + let comment_id = doc.get_id(&obj_id, "comment")?; + let author = doc.get(&obj_id, "author").map(Author::new)?; + let state = doc.get(&obj_id, "state")?; + let timestamp = doc.get(&obj_id, "timestamp")?; + + let comment = shared::lookup::comment(doc, &comment_id)?; + let discussion: Discussion = doc.list(&obj_id, "discussion", shared::lookup::thread)?; + let labels: HashSet