Skip to main content

valence_core/
record_id.rs

1//! Backend-agnostic record identifier (`table` + `id`).
2
3use serde::de::{self, MapAccess, Visitor};
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use std::fmt;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub struct RecordId {
9    table: String,
10    id: String,
11}
12
13impl RecordId {
14    pub fn new(table: impl Into<String>, id: impl Into<String>) -> Self {
15        Self {
16            table: table.into(),
17            id: id.into(),
18        }
19    }
20
21    pub fn table(&self) -> &str {
22        &self.table
23    }
24
25    pub fn id(&self) -> &str {
26        &self.id
27    }
28
29    /// Parse `table:id` wire form (Surreal / SQL document adapters).
30    pub fn parse(s: &str) -> Option<Self> {
31        let (table, id) = s.split_once(':')?;
32        if table.is_empty() || id.is_empty() {
33            return None;
34        }
35        Some(Self::new(table, id))
36    }
37}
38
39impl fmt::Display for RecordId {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        write!(f, "{}:{}", self.table, self.id)
42    }
43}
44
45impl Serialize for RecordId {
46    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
47    where
48        S: Serializer,
49    {
50        use serde::ser::SerializeStruct;
51        let mut state = serializer.serialize_struct("RecordId", 2)?;
52        state.serialize_field("table", &self.table)?;
53        state.serialize_field("id", &self.id)?;
54        state.end()
55    }
56}
57
58impl<'de> Deserialize<'de> for RecordId {
59    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
60    where
61        D: Deserializer<'de>,
62    {
63        struct RecordIdVisitor;
64
65        impl<'de> Visitor<'de> for RecordIdVisitor {
66            type Value = RecordId;
67
68            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
69                f.write_str("RecordId object or \"table:id\" string")
70            }
71
72            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
73            where
74                E: de::Error,
75            {
76                RecordId::parse(v).ok_or_else(|| E::custom(format!("invalid RecordId string: {v}")))
77            }
78
79            fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
80            where
81                E: de::Error,
82            {
83                self.visit_str(&v)
84            }
85
86            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
87            where
88                A: MapAccess<'de>,
89            {
90                let mut table: Option<String> = None;
91                let mut id: Option<String> = None;
92                while let Some(key) = map.next_key::<String>()? {
93                    match key.as_str() {
94                        "table" | "tb" => table = Some(map.next_value::<String>()?),
95                        "id" => id = Some(map.next_value::<String>()?),
96                        _ => {
97                            let _: de::IgnoredAny = map.next_value()?;
98                        }
99                    }
100                }
101                let table = table.ok_or_else(|| de::Error::missing_field("table"))?;
102                let id = id.ok_or_else(|| de::Error::missing_field("id"))?;
103                Ok(RecordId::new(table, id))
104            }
105        }
106
107        deserializer.deserialize_any(RecordIdVisitor)
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn deserializes_object_and_colon_string() {
117        let obj: RecordId = serde_json::from_str(r#"{"table":"task","id":"1"}"#).expect("object");
118        assert_eq!(obj.table(), "task");
119        assert_eq!(obj.id(), "1");
120
121        let s: RecordId = serde_json::from_str(r#""task:1""#).expect("string");
122        assert_eq!(s.table(), "task");
123        assert_eq!(s.id(), "1");
124    }
125}