Skip to main content

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    pub fn as_bytes(&self) -> &[u8] {
22        self.0.as_bytes()
23    }
24}
25
26impl std::fmt::Debug for DocumentId {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        let as_string = bs58::encode(&self.0).with_check().into_string();
29        write!(f, "{as_string}")
30    }
31}
32
33impl std::fmt::Display for DocumentId {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        let as_string = bs58::encode(&self.0).with_check().into_string();
36        write!(f, "{as_string}")
37    }
38}
39
40#[derive(Debug, thiserror::Error)]
41#[error("Invalid document ID: {0}")]
42pub struct BadDocumentId(String);
43
44impl TryFrom<Vec<u8>> for DocumentId {
45    type Error = BadDocumentId;
46
47    fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
48        match uuid::Uuid::from_slice(v.as_slice()) {
49            Ok(id) => Ok(Self(id)),
50            Err(e) => Err(BadDocumentId(format!("invalid uuid: {e}"))),
51        }
52    }
53}
54
55impl FromStr for DocumentId {
56    type Err = BadDocumentId;
57
58    fn from_str(s: &str) -> Result<Self, Self::Err> {
59        match bs58::decode(s).with_check(None).into_vec() {
60            Ok(bytes) => Self::try_from(bytes),
61            Err(_) => {
62                // attempt to parse legacy UUID format
63                let uuid = uuid::Uuid::parse_str(s).map_err(|_| {
64                    BadDocumentId(
65                        "expected either a bs58-encoded document ID or a UUID".to_string(),
66                    )
67                })?;
68                Ok(Self(uuid))
69            }
70        }
71    }
72}