samod_core/
document_id.rs

1use serde::{Deserialize, Serialize};
2use std::str::FromStr;
3use uuid::Uuid;
4
5/// Unique identifier for an automerge document.
6///
7/// Document IDs are used throughout the sync protocol to identify which
8/// document sync messages relate to. They are arbitrary byte arrays that
9/// uniquely identify a document.
10#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
11pub struct DocumentId(Uuid);
12
13impl DocumentId {
14    /// Creates a new random document ID.
15    pub fn new<R: rand::Rng>(rng: &mut R) -> Self {
16        let bytes: [u8; 16] = rng.random();
17        let uuid = uuid::Builder::from_random_bytes(bytes).into_uuid();
18        Self(uuid)
19    }
20}
21
22impl std::fmt::Debug for DocumentId {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        let as_string = bs58::encode(&self.0).with_check().into_string();
25        write!(f, "{as_string}")
26    }
27}
28
29impl std::fmt::Display for DocumentId {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        let as_string = bs58::encode(&self.0).with_check().into_string();
32        write!(f, "{as_string}")
33    }
34}
35
36#[derive(Debug, thiserror::Error)]
37#[error("Invalid document ID: {0}")]
38pub struct BadDocumentId(String);
39
40impl TryFrom<Vec<u8>> for DocumentId {
41    type Error = BadDocumentId;
42
43    fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
44        match uuid::Uuid::from_slice(v.as_slice()) {
45            Ok(id) => Ok(Self(id)),
46            Err(e) => Err(BadDocumentId(format!("invalid uuid: {e}"))),
47        }
48    }
49}
50
51impl FromStr for DocumentId {
52    type Err = BadDocumentId;
53
54    fn from_str(s: &str) -> Result<Self, Self::Err> {
55        match bs58::decode(s).with_check(None).into_vec() {
56            Ok(bytes) => Self::try_from(bytes),
57            Err(_) => {
58                // attempt to parse legacy UUID format
59                let uuid = uuid::Uuid::parse_str(s).map_err(|_| {
60                    BadDocumentId(
61                        "expected either a bs58-encoded document ID or a UUID".to_string(),
62                    )
63                })?;
64                Ok(Self(uuid))
65            }
66        }
67    }
68}