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(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
18pub struct Text(pub(crate) Repr);
19
20#[macro_export]
24macro_rules! text {
25 () => {
26 $crate::Text::new()
27 };
28 ($($e:tt)+) => {
29 unsafe {
31 $crate::Text::__from_literal($crate::__hidden::validate_text!($($e)+))
32 }
33 };
34}
35
36impl Text {
37 pub(crate) fn validate(s: &str) -> Result<(), InvalidText> {
38 if let Some(index) = s.bytes().position(|b| b == 0) {
39 return Err(InvalidText(InvalidTextRepr::ContainsNul { index }));
40 }
41 Ok(())
42 }
43
44 pub const fn new() -> Self {
46 Self(Repr::empty())
47 }
48
49 #[doc(hidden)]
51 pub const unsafe fn __from_literal(lit: &'static str) -> Self {
52 Self(Repr::from_static(lit))
53 }
54
55 pub const fn const_eq(&self, other: &Self) -> bool {
59 let lhs = self.0.as_str().as_bytes();
60 let rhs = other.0.as_str().as_bytes();
61 if lhs.len() != rhs.len() {
62 return false;
63 }
64 let mut i = 0;
65 while i < lhs.len() && i < rhs.len() {
66 if lhs[i] != rhs[i] {
67 return false;
68 }
69 #[allow(clippy::arithmetic_side_effects)]
73 {
74 i += 1;
75 }
76 }
77 true
78 }
79
80 pub const fn as_str(&self) -> &str {
82 self.0.as_str()
83 }
84}
85
86impl fmt::Display for Text {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 self.0.as_str().fmt(f)
89 }
90}
91
92impl fmt::Debug for Text {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 self.0.as_str().fmt(f)
95 }
96}
97
98impl PartialEq<str> for Text {
99 fn eq(&self, other: &str) -> bool {
100 self.0.as_str().eq(other)
101 }
102}
103impl PartialEq<&str> for Text {
104 fn eq(&self, other: &&str) -> bool {
105 self.0.as_str().eq(*other)
106 }
107}
108
109impl FromStr for Text {
110 type Err = InvalidText;
111 fn from_str(value: &str) -> Result<Self, Self::Err> {
112 Self::validate(value)?;
113 Ok(Self(Repr::from_str(value)))
114 }
115}
116
117impl TryFrom<String> for Text {
118 type Error = InvalidText;
119 fn try_from(value: String) -> Result<Self, Self::Error> {
120 value.as_str().parse()
121 }
122}
123
124impl TryFrom<&CStr> for Text {
125 type Error = core::str::Utf8Error;
126 fn try_from(value: &CStr) -> Result<Self, Self::Error> {
127 let s: &str = value.to_str()?;
128 Ok(Self(Repr::from_str(s)))
130 }
131}
132
133impl Add for &Text {
134 type Output = Text;
135 fn add(self, rhs: Self) -> Self::Output {
136 let mut s = String::from(self.0.as_str());
137 s.push_str(rhs.as_str());
138 debug_assert!(
139 Text::validate(&s).is_ok(),
140 "text should stay valid under concatenation"
141 );
142 Text(Repr::from_str(&s))
143 }
144}
145
146impl serde::Serialize for Text {
147 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
148 where
149 S: serde::Serializer,
150 {
151 self.0.serialize(serializer)
152 }
153}
154
155impl<'de> serde::Deserialize<'de> for Text {
156 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
157 where
158 D: serde::Deserializer<'de>,
159 {
160 let r = Repr::deserialize(deserializer)?;
161 Self::validate(r.as_str()).map_err(|_| {
162 de::Error::invalid_value(de::Unexpected::Str(r.as_str()), &"no nul bytes")
163 })?;
164 Ok(Self(r))
165 }
166}
167
168impl Deref for Text {
169 type Target = str;
170
171 fn deref(&self) -> &Self::Target {
172 self.as_str()
173 }
174}
175
176impl<T> AsRef<T> for Text
177where
178 T: ?Sized,
179 <Text as Deref>::Target: AsRef<T>,
180{
181 fn as_ref(&self) -> &T {
182 self.deref().as_ref()
183 }
184}