Skip to main content

konjure_sdk/data/
buffer.rs

1use alloc::{string::String, sync::Arc, vec::Vec};
2use core::{fmt, ops::Deref};
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4
5use super::DataError;
6
7/// Immutable UTF-8 text; cloning shares the allocation.
8#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct Str(Arc<str>);
10
11impl Str {
12    /// Borrows the UTF-8 text without copying.
13    pub fn as_str(&self) -> &str {
14        &self.0
15    }
16
17    /// Returns a new string from a byte range at UTF-8 boundaries.
18    ///
19    /// # Errors
20    /// Returns [`DataError::Bounds`] for invalid ranges or split code points.
21    pub fn slice(&self, start: usize, end: usize) -> Result<Self, DataError> {
22        self.0
23            .get(start..end)
24            .map(Self::from)
25            .ok_or(DataError::Bounds)
26    }
27
28    /// Number of bytes in the UTF-8 encoding, independent of character count.
29    pub fn byte_len(&self) -> usize {
30        self.0.len()
31    }
32}
33
34impl From<&str> for Str {
35    fn from(value: &str) -> Self {
36        Self(Arc::from(value))
37    }
38}
39
40impl From<String> for Str {
41    fn from(value: String) -> Self {
42        Self(Arc::from(value))
43    }
44}
45
46impl Deref for Str {
47    type Target = str;
48    fn deref(&self) -> &Self::Target {
49        self.as_str()
50    }
51}
52
53impl fmt::Display for Str {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        f.write_str(self.as_str())
56    }
57}
58
59impl Serialize for Str {
60    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
61        serializer.serialize_str(self.as_str())
62    }
63}
64
65impl<'de> Deserialize<'de> for Str {
66    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
67        String::deserialize(deserializer).map(Self::from)
68    }
69}
70
71/// Immutable arbitrary bytes; cloning shares the allocation.
72#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
73pub struct Bin(Arc<[u8]>);
74
75impl Bin {
76    /// Borrows the bytes without copying or UTF-8 interpretation.
77    pub fn as_bytes(&self) -> &[u8] {
78        &self.0
79    }
80
81    /// Copies a valid byte range into a new immutable value.
82    ///
83    /// # Errors
84    /// Returns [`DataError::Bounds`] for invalid ranges.
85    pub fn slice(&self, start: usize, end: usize) -> Result<Self, DataError> {
86        self.0
87            .get(start..end)
88            .map(Self::from)
89            .ok_or(DataError::Bounds)
90    }
91
92    /// Returns the byte count.
93    pub fn byte_len(&self) -> usize {
94        self.0.len()
95    }
96}
97
98impl From<&[u8]> for Bin {
99    fn from(value: &[u8]) -> Self {
100        Self(Arc::from(value))
101    }
102}
103
104impl From<Vec<u8>> for Bin {
105    fn from(value: Vec<u8>) -> Self {
106        Self(Arc::from(value))
107    }
108}
109
110impl Deref for Bin {
111    type Target = [u8];
112    fn deref(&self) -> &Self::Target {
113        self.as_bytes()
114    }
115}
116
117impl Serialize for Bin {
118    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
119        serializer.serialize_bytes(self.as_bytes())
120    }
121}
122
123impl<'de> Deserialize<'de> for Bin {
124    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
125        Vec::<u8>::deserialize(deserializer).map(Self::from)
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn immutable_buffers_share_clones_and_validate_text_boundaries() {
135        let text = Str::from("λx");
136        let copy = text.clone();
137        assert!(Arc::ptr_eq(&text.0, &copy.0));
138        assert_eq!(text.slice(0, 1), Err(DataError::Bounds));
139        assert_eq!(text.slice(0, 2).unwrap().as_str(), "λ");
140        let bytes = Bin::from(alloc::vec![0, 255, 42]);
141        assert!(Arc::ptr_eq(&bytes.0, &bytes.clone().0));
142        let json = serde_json::to_string(&bytes).unwrap();
143        assert_eq!(serde_json::from_str::<Bin>(&json).unwrap(), bytes);
144    }
145}