nodecraft/id/impls/
id_ref.rs

1use core::borrow::Borrow;
2
3use super::ParseNodeIdError;
4
5/// A unique string identifying a server for all time.
6/// The maximum length of an id is 512 bytes.
7#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub struct NodeIdRef<'a, const N: usize = { u8::MAX as usize }>(&'a str);
9
10impl<'a, const N: usize> NodeIdRef<'a, N> {
11  /// The maximum length of an id is `N` bytes.
12  pub const MAX_SIZE: usize = N;
13
14  /// Creates a new `Id` from the source.
15  pub fn new(src: &'a str) -> Result<Self, ParseNodeIdError> {
16    if src.is_empty() {
17      return Err(ParseNodeIdError::Empty);
18    }
19
20    if src.len() > Self::MAX_SIZE {
21      return Err(ParseNodeIdError::too_large(Self::MAX_SIZE, src.len()));
22    }
23
24    Ok(Self(src))
25  }
26
27  /// converts the `Id` into a `&str`.
28  #[inline]
29  pub const fn as_str(&self) -> &'a str {
30    self.0
31  }
32
33  /// Returns a byte slice.
34  /// To convert the byte slice back into a string slice, use the [`core::str::from_utf8`] function.
35  #[inline]
36  pub const fn as_bytes(&self) -> &'a [u8] {
37    self.0.as_bytes()
38  }
39
40  /// Converts to owned [`NodeId`](super::NodeId).
41  #[cfg(any(feature = "std", feature = "alloc"))]
42  #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
43  #[inline]
44  pub fn to_owned(&self) -> super::NodeId<N> {
45    super::NodeId(self.0.into())
46  }
47}
48
49impl<const N: usize> Borrow<str> for NodeIdRef<'_, N> {
50  fn borrow(&self) -> &str {
51    self.0
52  }
53}
54
55impl<const N: usize> AsRef<str> for NodeIdRef<'_, N> {
56  fn as_ref(&self) -> &str {
57    self.0
58  }
59}
60
61impl<const N: usize> core::fmt::Display for NodeIdRef<'_, N> {
62  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
63    self.0.fmt(f)
64  }
65}
66
67impl<const N: usize> core::fmt::Debug for NodeIdRef<'_, N> {
68  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69    self.0.fmt(f)
70  }
71}
72
73impl<const N: usize> cheap_clone::CheapClone for NodeIdRef<'_, N> {}
74
75impl<'a, const N: usize> TryFrom<&'a [u8]> for NodeIdRef<'a, N> {
76  type Error = ParseNodeIdError;
77
78  fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
79    Self::new(core::str::from_utf8(value)?)
80  }
81}
82
83impl<'a, const N: usize> TryFrom<&'a str> for NodeIdRef<'a, N> {
84  type Error = ParseNodeIdError;
85
86  fn try_from(value: &'a str) -> Result<Self, Self::Error> {
87    Self::new(value)
88  }
89}
90
91#[cfg(feature = "serde")]
92const _: () = {
93  use serde::{Deserialize, Deserializer, Serialize, Serializer};
94
95  impl<const N: usize> Serialize for NodeIdRef<'_, N> {
96    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
97    where
98      S: Serializer,
99    {
100      self.0.serialize(serializer)
101    }
102  }
103
104  impl<'de, const N: usize> Deserialize<'de> for NodeIdRef<'de, N> {
105    fn deserialize<D>(deserializer: D) -> Result<NodeIdRef<'de, N>, D::Error>
106    where
107      D: Deserializer<'de>,
108    {
109      let s = <&str>::deserialize(deserializer)?;
110      NodeIdRef::new(s).map_err(serde::de::Error::custom)
111    }
112  }
113};
114
115#[cfg(test)]
116mod tests {
117  use super::*;
118
119  #[test]
120  fn test_basic() {
121    let id = NodeIdRef::<16>::try_from(b"test".as_slice()).unwrap();
122    assert_eq!(id.as_str(), "test");
123    assert_eq!(id.as_ref(), "test");
124    assert_eq!(id.as_bytes(), b"test");
125    println!("{id}");
126    println!("{id:?}");
127
128    let _id = NodeIdRef::<16>::try_from("test1").unwrap();
129
130    assert!(NodeIdRef::<20>::new("").is_err());
131    assert!(NodeIdRef::<4>::new("aaaaa").is_err());
132  }
133
134  #[test]
135  #[cfg(any(feature = "alloc", feature = "std"))]
136  fn test_try_from() {
137    let id = NodeIdRef::<16>::try_from("test").unwrap();
138    assert_eq!(id.as_str(), "test");
139    assert_eq!(id.as_ref(), "test");
140    assert!(NodeIdRef::<16>::try_from("").is_err());
141    assert!(NodeIdRef::<4>::try_from("aaaaa").is_err());
142
143    let id = NodeIdRef::<16>::try_from("test".as_bytes()).unwrap();
144
145    assert_eq!(id.as_str(), "test");
146    assert_eq!(id.as_ref(), "test");
147    assert!(NodeIdRef::<16>::try_from([].as_slice()).is_err());
148    assert!(NodeIdRef::<4>::try_from("aaaaa").is_err());
149
150    let id = id.to_owned();
151    assert_eq!(id.as_str(), "test");
152  }
153
154  #[test]
155  #[cfg(any(feature = "std", feature = "alloc"))]
156  fn test_borrow() {
157    use std::collections::HashSet;
158
159    let mut set = HashSet::new();
160    let id = NodeIdRef::<16>::try_from(b"test".as_slice()).unwrap();
161    set.insert(id);
162    assert!(set.contains("test"));
163  }
164
165  #[cfg(feature = "serde")]
166  #[test]
167  fn test_serde() {
168    let id = NodeIdRef::try_from("32").unwrap();
169    let s = serde_json::to_string(&id).unwrap();
170    let decoded: NodeIdRef = serde_json::from_str(&s).unwrap();
171    assert_eq!(id, decoded);
172  }
173}