Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
Add Op ID string formatting
Slack Coder committed 3 years ago
commit 1cb89868305760a2c8ee536a5e34efeedcbe2c7b
parent 98f7fe1db8ade2f8db2b314ba9f41c7f2ebbfde9
4 files changed +103 -0
modified Cargo.lock
@@ -1886,6 +1886,7 @@ dependencies = [
 "radicle-crypto",
 "serde",
 "tempfile",
+
 "thiserror",
]

[[package]]
modified radicle-crdt/Cargo.toml
@@ -11,6 +11,7 @@ fastrand = { version = "1.8.0", optional = true }
num-traits = { version = "0.2.15", default-features = false, features = ["std"] }
qcheck = { version = "1", optional = true }
serde = { version = "1" }
+
thiserror = { version = "1" }

[dependencies.radicle-crypto]
path = "../radicle-crypto"
modified radicle-crdt/src/clock.rs
@@ -1,8 +1,11 @@
+
use std::fmt;
+
use std::str::FromStr;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;

use num_traits::Bounded;
use serde::{Deserialize, Serialize};
+
use thiserror::Error;

use crate::ord::Max;
use crate::Semilattice as _;
@@ -45,6 +48,12 @@ impl Lamport {
    }
}

+
impl fmt::Display for Lamport {
+
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+
        write!(f, "{}", self.get())
+
    }
+
}
+

impl From<u64> for Lamport {
    fn from(counter: u64) -> Self {
        Self {
@@ -53,6 +62,36 @@ impl From<u64> for Lamport {
    }
}

+
impl From<Lamport> for u64 {
+
    fn from(arg: Lamport) -> Self {
+
        arg.get()
+
    }
+
}
+

+
/// Error decoding an operation from an entry.
+
#[derive(Error, Debug)]
+
pub enum LamportError {
+
    #[error("invalid lamport clock value")]
+
    Invalid,
+
}
+

+
impl FromStr for Lamport {
+
    type Err = LamportError;
+

+
    fn from_str(s: &str) -> Result<Self, Self::Err> {
+
        Self::try_from(s)
+
    }
+
}
+

+
impl TryFrom<&str> for Lamport {
+
    type Error = LamportError;
+

+
    fn try_from(s: &str) -> Result<Self, Self::Error> {
+
        let v = s.parse::<u64>().map_err(|_| LamportError::Invalid)?;
+
        Ok(v.into())
+
    }
+
}
+

impl Bounded for Lamport {
    fn min_value() -> Self {
        Self::from(u64::min_value())
modified radicle/src/cob/op.rs
@@ -1,4 +1,7 @@
use std::collections::BTreeMap;
+
use std::fmt;
+
use std::str;
+
use std::str::FromStr;

use nonempty::NonEmpty;
use serde::{Deserialize, Serialize};
@@ -34,6 +37,47 @@ impl OpId {
    }
}

+
impl fmt::Display for OpId {
+
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+
        write!(f, "{}/{}", self.1, self.0)
+
    }
+
}
+

+
/// Error decoding an operation from an entry.
+
#[derive(Error, Debug)]
+
pub enum OpIdError {
+
    #[error("cannot parse op id from empty string")]
+
    Empty,
+
    #[error("badly formatted op id")]
+
    BadFormat,
+
}
+

+
impl FromStr for OpId {
+
    type Err = OpIdError;
+

+
    fn from_str(s: &str) -> Result<Self, Self::Err> {
+
        Self::try_from(s)
+
    }
+
}
+

+
impl TryFrom<&str> for OpId {
+
    type Error = OpIdError;
+

+
    fn try_from(s: &str) -> Result<Self, Self::Error> {
+
        if s.is_empty() {
+
            return Err(OpIdError::Empty);
+
        }
+

+
        let Some((actor_id, clock)) = s.split_once('/') else {
+
            return Err(OpIdError::BadFormat);
+
        };
+
        Ok(Self(
+
            Lamport::from_str(clock).map_err(|_| OpIdError::BadFormat)?,
+
            ActorId::from_str(actor_id).map_err(|_| OpIdError::BadFormat)?,
+
        ))
+
    }
+
}
+

/// The author of an [`Op`].
pub type ActorId = PublicKey;

@@ -182,3 +226,21 @@ impl<G: Signer, A: Clone> Actor<G, A> {
        op
    }
}
+

+
#[cfg(test)]
+
mod test {
+
    use super::*;
+

+
    #[test]
+
    fn test_opid_try_from_str() {
+
        let str = "z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT/12";
+
        let id = OpId::try_from(str).expect("Op ID parses string");
+
        assert_eq!(str, id.to_string(), "string conversion is consistent");
+

+
        let str = "";
+
        assert!(OpId::try_from(str).is_err(), "empty strings are invalid");
+

+
        let str = "jlkjfksgi";
+
        assert!(OpId::try_from(str).is_err(), "badly formatted string");
+
    }
+
}