1use std::{
2 borrow::Borrow,
3 fmt::{Display, Formatter},
4 ops::Deref,
5 str::FromStr,
6};
7
8use thiserror::Error;
9
10use crate::Scope;
11
12#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
17#[repr(transparent)]
18pub struct SegmentBuf(String);
19
20impl AsRef<Segment> for SegmentBuf {
21 fn as_ref(&self) -> &Segment {
22 self
23 }
24}
25
26impl Borrow<Segment> for SegmentBuf {
27 fn borrow(&self) -> &Segment {
28 self
29 }
30}
31
32impl Deref for SegmentBuf {
33 type Target = Segment;
34
35 fn deref(&self) -> &Self::Target {
36 unsafe { Segment::from_str_unchecked(&self.0) }
37 }
38}
39
40impl Display for SegmentBuf {
41 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
42 write!(f, "{}", self.0)
43 }
44}
45
46impl FromStr for SegmentBuf {
47 type Err = ParseSegmentError;
48
49 fn from_str(s: &str) -> Result<Self, Self::Err> {
50 Ok(Segment::parse(s)?.to_owned())
51 }
52}
53
54impl From<&Segment> for SegmentBuf {
55 fn from(value: &Segment) -> Self {
56 value.to_owned()
57 }
58}
59
60#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
65#[repr(transparent)]
66pub struct Segment(str);
67
68impl Segment {
69 pub const fn parse(value: &str) -> Result<&Self, ParseSegmentError> {
86 if value.is_empty() {
87 Err(ParseSegmentError::Empty)
88 } else {
89 let bytes = value.as_bytes();
90 if Self::leading_whitespace(bytes) || Self::trailing_whitespace(bytes) {
91 Err(ParseSegmentError::TrailingWhitespace)
92 } else if Self::contains_separator(bytes) {
93 Err(ParseSegmentError::ContainsSeparator)
94 } else {
95 unsafe { Ok(Segment::from_str_unchecked(value)) }
96 }
97 }
98 }
99
100 pub fn as_str(&self) -> &str {
115 &self.0
116 }
117
118 pub const unsafe fn from_str_unchecked(s: &str) -> &Self {
126 &*(s as *const _ as *const Self)
127 }
128
129 const fn leading_whitespace(bytes: &[u8]) -> bool {
130 matches!(bytes[0], 9 | 10 | 32)
131 }
132
133 const fn trailing_whitespace(bytes: &[u8]) -> bool {
134 matches!(bytes[bytes.len() - 1], 9 | 10 | 32)
135 }
136
137 const fn contains_separator(bytes: &[u8]) -> bool {
138 let mut index = 0;
139
140 while index < bytes.len() {
141 if bytes[index] == Scope::SEPARATOR as u8 {
142 return true;
143 }
144 index += 1;
145 }
146
147 false
148 }
149}
150
151impl Display for Segment {
152 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
153 write!(f, "{}", &self.0)
154 }
155}
156
157impl ToOwned for Segment {
158 type Owned = SegmentBuf;
159
160 fn to_owned(&self) -> Self::Owned {
161 SegmentBuf(self.0.to_owned())
162 }
163}
164
165#[derive(Debug, Error)]
167pub enum ParseSegmentError {
168 #[error("segments must not start or end with whitespace")]
169 TrailingWhitespace,
170 #[error("segments must be nonempty")]
171 Empty,
172 #[error("segments must not contain scope separators")]
173 ContainsSeparator,
174}
175
176#[cfg(feature = "postgres")]
177mod postgres_impls {
178 use crate::segment::{Segment, SegmentBuf};
179
180 impl postgres::types::ToSql for &Segment {
181 fn to_sql(
182 &self,
183 ty: &postgres_types::Type,
184 out: &mut postgres_types::private::BytesMut,
185 ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>>
186 where
187 Self: Sized,
188 {
189 (&self.0).to_sql(ty, out)
190 }
191
192 fn accepts(ty: &postgres_types::Type) -> bool
193 where
194 Self: Sized,
195 {
196 <&str>::accepts(ty)
197 }
198
199 fn to_sql_checked(
200 &self,
201 ty: &postgres_types::Type,
202 out: &mut postgres_types::private::BytesMut,
203 ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
204 (&self.0).to_sql_checked(ty, out)
205 }
206 }
207
208 impl postgres::types::ToSql for SegmentBuf {
209 fn to_sql(
210 &self,
211 ty: &postgres_types::Type,
212 out: &mut postgres_types::private::BytesMut,
213 ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>>
214 where
215 Self: Sized,
216 {
217 self.0.to_sql(ty, out)
218 }
219
220 fn accepts(ty: &postgres_types::Type) -> bool
221 where
222 Self: Sized,
223 {
224 String::accepts(ty)
225 }
226
227 fn to_sql_checked(
228 &self,
229 ty: &postgres_types::Type,
230 out: &mut postgres_types::private::BytesMut,
231 ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
232 self.0.to_sql_checked(ty, out)
233 }
234 }
235
236 impl<'a> postgres::types::FromSql<'a> for SegmentBuf {
237 fn from_sql(
238 ty: &postgres_types::Type,
239 raw: &'a [u8],
240 ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
241 let value = String::from_sql(ty, raw)?;
242 Ok(Segment::parse(&value)?.to_owned())
243 }
244
245 fn accepts(ty: &postgres_types::Type) -> bool {
246 String::accepts(ty)
247 }
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::{Scope, Segment};
254
255 #[test]
256 fn test_trailing_separator_fails() {
257 assert!(Segment::parse(&format!("test{}", Scope::SEPARATOR)).is_err());
258 }
259
260 #[test]
261 fn test_trailing_space_fails() {
262 assert!(Segment::parse("test ").is_err());
263 }
264
265 #[test]
266 fn test_trailing_tab_fails() {
267 assert!(Segment::parse("test\t").is_err());
268 }
269
270 #[test]
271 fn test_trailing_newline_fails() {
272 assert!(Segment::parse("test\n").is_err());
273 }
274
275 #[test]
276 fn test_leading_separator_fails() {
277 assert!(Segment::parse(&format!("{}test", Scope::SEPARATOR)).is_err());
278 }
279
280 #[test]
281 fn test_leading_space_fails() {
282 assert!(Segment::parse(" test").is_err());
283 }
284
285 #[test]
286 fn test_leading_tab_fails() {
287 assert!(Segment::parse("\ttest").is_err());
288 }
289
290 #[test]
291 fn test_leading_newline_fails() {
292 assert!(Segment::parse("\ntest").is_err());
293 }
294
295 #[test]
296 fn test_containing_separator_fails() {
297 assert!(Segment::parse(&format!("te{}st", Scope::SEPARATOR)).is_err());
298 }
299
300 #[test]
301 fn test_containing_space_succeeds() {
302 assert!(Segment::parse("te st").is_ok());
303 }
304
305 #[test]
306 fn test_containing_tab_succeeds() {
307 assert!(Segment::parse("te\tst").is_ok());
308 }
309
310 #[test]
311 fn test_containing_newline_succeeds() {
312 assert!(Segment::parse("te\nst").is_ok());
313 }
314
315 #[test]
316 fn test_segment_succeeds() {
317 assert!(Segment::parse("test").is_ok())
318 }
319}