aranya_policy_text/
text.rs1use alloc::string::String;
2use core::{
3 ffi::CStr,
4 fmt,
5 ops::{Add, Deref},
6 str::FromStr,
7};
8
9use serde::de;
10
11use crate::{
12 error::{InvalidText, InvalidTextRepr},
13 repr::Repr,
14};
15
16#[derive(
18 Clone,
19 Default,
20 PartialEq,
21 Eq,
22 Hash,
23 PartialOrd,
24 Ord,
25 rkyv::Archive,
26 rkyv::Serialize,
27 rkyv::Deserialize,
28)]
29#[rkyv(bytecheck(verify))]
30#[rkyv(derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord))]
31pub struct Text(pub(crate) Repr);
32
33#[macro_export]
37macro_rules! text {
38 () => {
39 $crate::Text::new()
40 };
41 ($($e:tt)+) => {
42 unsafe {
44 $crate::Text::__from_literal($crate::__hidden::validate_text!($($e)+))
45 }
46 };
47}
48
49impl Text {
50 pub(crate) fn validate(s: &str) -> Result<(), InvalidText> {
51 if let Some(index) = s.bytes().position(|b| b == 0) {
52 return Err(InvalidText(InvalidTextRepr::ContainsNul { index }));
53 }
54 Ok(())
55 }
56
57 pub const fn new() -> Self {
59 Self(Repr::empty())
60 }
61
62 #[doc(hidden)]
64 pub const unsafe fn __from_literal(lit: &'static str) -> Self {
65 Self(Repr::from_static(lit))
66 }
67
68 pub const fn const_eq(&self, other: &Self) -> bool {
72 let lhs = self.0.as_str().as_bytes();
73 let rhs = other.0.as_str().as_bytes();
74 if lhs.len() != rhs.len() {
75 return false;
76 }
77 let mut i = 0;
78 while i < lhs.len() && i < rhs.len() {
79 if lhs[i] != rhs[i] {
80 return false;
81 }
82 #[allow(clippy::arithmetic_side_effects)]
86 {
87 i += 1;
88 }
89 }
90 true
91 }
92
93 pub const fn as_str(&self) -> &str {
95 self.0.as_str()
96 }
97}
98
99impl fmt::Display for Text {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 self.0.as_str().fmt(f)
102 }
103}
104
105impl fmt::Debug for Text {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 self.0.as_str().fmt(f)
108 }
109}
110
111impl PartialEq<str> for Text {
112 fn eq(&self, other: &str) -> bool {
113 self.0.as_str().eq(other)
114 }
115}
116impl PartialEq<&str> for Text {
117 fn eq(&self, other: &&str) -> bool {
118 self.0.as_str().eq(*other)
119 }
120}
121
122impl FromStr for Text {
123 type Err = InvalidText;
124 fn from_str(value: &str) -> Result<Self, Self::Err> {
125 Self::validate(value)?;
126 Ok(Self(Repr::from_str(value)))
127 }
128}
129
130impl TryFrom<String> for Text {
131 type Error = InvalidText;
132 fn try_from(value: String) -> Result<Self, Self::Error> {
133 value.as_str().parse()
134 }
135}
136
137impl TryFrom<&CStr> for Text {
138 type Error = core::str::Utf8Error;
139 fn try_from(value: &CStr) -> Result<Self, Self::Error> {
140 let s: &str = value.to_str()?;
141 Ok(Self(Repr::from_str(s)))
143 }
144}
145
146impl Add for &Text {
147 type Output = Text;
148 fn add(self, rhs: Self) -> Self::Output {
149 let mut s = String::from(self.0.as_str());
150 s.push_str(rhs.as_str());
151 debug_assert!(
152 Text::validate(&s).is_ok(),
153 "text should stay valid under concatenation"
154 );
155 Text(Repr::from_str(&s))
156 }
157}
158
159impl serde::Serialize for Text {
160 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
161 where
162 S: serde::Serializer,
163 {
164 self.0.serialize(serializer)
165 }
166}
167
168impl<'de> serde::Deserialize<'de> for Text {
169 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
170 where
171 D: serde::Deserializer<'de>,
172 {
173 let r = Repr::deserialize(deserializer)?;
174 Self::validate(r.as_str()).map_err(|_| {
175 de::Error::invalid_value(de::Unexpected::Str(r.as_str()), &"no nul bytes")
176 })?;
177 Ok(Self(r))
178 }
179}
180
181impl Deref for Text {
182 type Target = str;
183
184 fn deref(&self) -> &Self::Target {
185 self.as_str()
186 }
187}
188
189impl<T> AsRef<T> for Text
190where
191 T: ?Sized,
192 <Self as Deref>::Target: AsRef<T>,
193{
194 fn as_ref(&self) -> &T {
195 self.deref().as_ref()
196 }
197}
198
199impl ArchivedText {
200 pub fn as_str(&self) -> &str {
201 self.0.as_str()
202 }
203}
204
205unsafe impl<C> rkyv::bytecheck::Verify<C> for ArchivedText
207where
208 C: rkyv::rancor::Fallible<Error: rkyv::rancor::Source> + ?Sized,
209{
210 fn verify(&self, _context: &mut C) -> Result<(), <C as rkyv::rancor::Fallible>::Error> {
211 Text::validate(self.as_str()).map_err(rkyv::rancor::Source::new)
212 }
213}