1mod flee;
2
3use std::{
4 borrow::Cow,
5 fmt::{Debug, Display},
6 marker::PhantomData,
7 ops::Deref,
8};
9
10use exn::{Exn, ResultExt};
11
12#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct PathGen;
14
15#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub struct Prefix<Inner, Segment> {
17 inner: Inner,
18 segment: PhantomData<Segment>,
19}
20
21#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub struct Node<Inner, Segment> {
23 inner: Inner,
24 segment: Segment,
25}
26
27impl<'a, Inner, Segment> Debug for Prefix<Inner, Segment>
28where
29 Inner: Sealed + Debug,
30 Segment: PathNode<'a>,
31{
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 write!(
34 f,
35 "{:?}.{:?}",
36 self.inner,
37 String::from_utf8_lossy(Segment::NAME)
38 )
39 }
40}
41
42impl<'a, Segment> Debug for Prefix<PathGen, Segment>
43where
44 Segment: PathNode<'a>,
45{
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 write!(f, "{:?}", String::from_utf8_lossy(Segment::NAME))
48 }
49}
50
51impl<'a, Inner, Segment> Debug for Node<Inner, Segment>
52where
53 Inner: Sealed + Debug,
54 Segment: PathNode<'a>,
55 Segment: Debug,
56{
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 write!(
59 f,
60 "{:?}.{:?}.{:?}",
61 self.inner,
62 String::from_utf8_lossy(Segment::NAME),
63 self.segment
64 )
65 }
66}
67
68impl<'a, Segment> Debug for Node<PathGen, Segment>
69where
70 Segment: PathNode<'a>,
71 Segment: Debug,
72{
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 write!(
75 f,
76 "{:?}.{:?}",
77 String::from_utf8_lossy(Segment::NAME),
78 self.segment
79 )
80 }
81}
82
83#[derive(Debug)]
84pub enum DeconstructError {
85 Format,
86 PathItem { kind: &'static str },
87}
88
89impl Display for DeconstructError {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 match self {
92 Self::Format => f.write_str("Invalid path format"),
93 Self::PathItem { kind } => write!(f, "Invalid bytes for PathItem {kind}"),
94 }
95 }
96}
97
98impl std::error::Error for DeconstructError {}
99
100pub struct PathParser<Inner> {
101 phantom: PhantomData<Inner>,
102}
103
104impl<Inner> std::fmt::Debug for PathParser<Inner> {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 f.debug_struct("PathParser")
107 .field("phantom", &self.phantom)
108 .finish()
109 }
110}
111
112impl<Inner> std::fmt::Display for PathParser<Inner> {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 write!(f, "PathParser<{}>", std::any::type_name::<Inner>())
115 }
116}
117
118pub trait PathItem<'a> {
119 type Error: std::error::Error + Send + Sync + 'static;
120
121 fn parse(bytes: Cow<'a, [u8]>) -> Result<Self, Self::Error>
122 where
123 Self: Sized + 'a;
124
125 fn to_bytes(&self) -> Cow<'_, [u8]>;
126}
127
128pub trait PathNode<'a>: PathItem<'a> {
129 const NAME: &'static [u8];
130}
131
132impl PathGen {
133 pub const fn parser() -> PathParser<Self> {
134 PathParser {
135 phantom: PhantomData,
136 }
137 }
138
139 pub const fn push<'a, N>(self, node: N) -> Node<Self, N>
140 where
141 N: PathNode<'a>,
142 {
143 Node {
144 inner: self,
145 segment: node,
146 }
147 }
148
149 pub const fn prefix<'a, N>(self) -> Prefix<Self, N>
150 where
151 N: PathNode<'a>,
152 {
153 Prefix {
154 inner: self,
155 segment: PhantomData,
156 }
157 }
158}
159
160impl<Inner, Segment> Node<Inner, Segment> {
161 pub const fn push<'a, N>(self, node: N) -> Node<Self, N>
162 where
163 N: PathNode<'a>,
164 {
165 Node {
166 inner: self,
167 segment: node,
168 }
169 }
170
171 pub fn split(self) -> (Inner, Segment) {
172 (self.inner, self.segment)
173 }
174
175 pub const fn segment(&self) -> &Segment {
176 &self.segment
177 }
178
179 pub const fn prefix<'a, N>(self) -> Prefix<Self, N>
180 where
181 N: PathNode<'a>,
182 {
183 Prefix {
184 inner: self,
185 segment: PhantomData,
186 }
187 }
188}
189
190impl<Inner, Segment> Prefix<Inner, Segment> {
191 pub fn pop(self) -> Inner {
192 self.inner
193 }
194}
195
196impl<Inner, Segment> Prefix<Inner, Segment>
197where
198 Self: Construct,
199{
200 pub fn to_bytes(&self) -> Vec<u8> {
201 Construct::construct(self)
202 }
203}
204
205impl<Inner> PathParser<Inner> {
206 pub const fn push<'a, N>(self) -> PathParser<Node<Inner, N>>
207 where
208 N: PathNode<'a>,
209 {
210 PathParser {
211 phantom: PhantomData,
212 }
213 }
214
215 pub const fn prefix<'a, N>(self) -> PathParser<Prefix<Inner, N>>
216 where
217 N: PathNode<'a>,
218 {
219 PathParser {
220 phantom: PhantomData,
221 }
222 }
223}
224
225impl<Inner> PathParser<Inner> {
226 pub fn parse<'a>(&self, s: &'a [u8]) -> Result<Inner, Exn<DeconstructError>>
227 where
228 Inner: Deconstruct<'a> + 'a,
229 {
230 Inner::deconstruct(s)
231 }
232}
233
234impl<Inner, Segment> Deref for Node<Inner, Segment> {
235 type Target = Segment;
236
237 fn deref(&self) -> &Self::Target {
238 self.segment()
239 }
240}
241
242mod hidden {
243 use super::DeconstructError;
244
245 use exn::Exn;
246
247 pub trait Construct {
248 fn construct(&self) -> Vec<u8>;
249 }
250
251 pub trait Sealed {}
252
253 pub trait Deconstruct<'a> {
254 fn deconstruct(s: &'a [u8]) -> Result<Self, Exn<DeconstructError>>
255 where
256 Self: Sized + 'a;
257 }
258}
259use hidden::{Construct, Deconstruct, Sealed};
260
261impl<'a, Inner, N> Sealed for Node<Inner, N>
262where
263 Inner: Sealed,
264 N: PathNode<'a>,
265{
266}
267
268impl<'a, N> Sealed for Node<PathGen, N> where N: PathNode<'a> {}
269
270impl<'a, Inner, N> Deconstruct<'a> for Node<Inner, N>
271where
272 Inner: Deconstruct<'a> + Sealed,
273 N: PathNode<'a>,
274{
275 fn deconstruct(s: &'a [u8]) -> Result<Self, Exn<DeconstructError>>
276 where
277 Self: Sized + 'a,
278 {
279 if let Some((rest, value)) = rsplit_once_escaped(s)
280 && let Some((rest, name)) = rsplit_once_escaped(rest)
281 && unescape(name) == N::NAME
282 {
283 let inner = Inner::deconstruct(rest)?;
284 let unescaped = unescape(value);
285 let segment = N::parse(unescaped).or_raise(|| DeconstructError::PathItem {
286 kind: std::any::type_name::<N>(),
287 })?;
288 return Ok(Node { inner, segment });
289 }
290
291 Err(Exn::new(DeconstructError::Format))
292 }
293}
294
295impl<'a, N> Deconstruct<'a> for Node<PathGen, N>
296where
297 N: PathNode<'a>,
298{
299 fn deconstruct(s: &'a [u8]) -> Result<Self, Exn<DeconstructError>>
300 where
301 Self: Sized + 'a,
302 {
303 if let Some((name, value)) = rsplit_once_escaped(s)
304 && unescape(name) == N::NAME
305 {
306 let unescaped = unescape(value);
307 let segment = N::parse(unescaped).or_raise(|| DeconstructError::PathItem {
308 kind: std::any::type_name::<N>(),
309 })?;
310 return Ok(Node {
311 inner: PathGen,
312 segment,
313 });
314 }
315
316 Err(Exn::new(DeconstructError::Format))
317 }
318}
319
320impl<'a, Inner, N> Sealed for Prefix<Inner, N>
321where
322 Inner: Sealed,
323 N: PathNode<'a>,
324{
325}
326impl<'a, N> Sealed for Prefix<PathGen, N> where N: PathNode<'a> {}
327
328impl<'a, Inner, N> Deconstruct<'a> for Prefix<Inner, N>
329where
330 Inner: Deconstruct<'a> + Sealed,
331 N: PathNode<'a>,
332{
333 fn deconstruct(s: &'a [u8]) -> Result<Self, Exn<DeconstructError>>
334 where
335 Self: Sized + 'a,
336 {
337 if let Some((rest, name)) = rsplit_once_escaped(s)
338 && unescape(name) == N::NAME
339 {
340 let inner = Inner::deconstruct(rest)?;
341 return Ok(Prefix {
342 inner,
343 segment: PhantomData,
344 });
345 }
346
347 Err(Exn::new(DeconstructError::Format))
348 }
349}
350
351impl<'a, N> Deconstruct<'a> for Prefix<PathGen, N>
352where
353 N: PathNode<'a>,
354{
355 fn deconstruct(s: &'a [u8]) -> Result<Self, Exn<DeconstructError>>
356 where
357 Self: Sized + 'a,
358 {
359 if unescape(s) == N::NAME {
360 return Ok(Prefix {
361 inner: PathGen,
362 segment: PhantomData,
363 });
364 }
365
366 Err(Exn::new(DeconstructError::Format))
367 }
368}
369
370impl<'a, Inner, N> Construct for Node<Inner, N>
371where
372 Inner: Construct,
373 N: PathNode<'a>,
374{
375 fn construct(&self) -> Vec<u8> {
376 let mut vec = self.inner.construct();
377 vec.extend_from_slice(b".");
378 vec.extend_from_slice(&escape(N::NAME));
379 vec.extend_from_slice(b".");
380 vec.extend_from_slice(&escape(&self.segment.to_bytes()));
381 vec
382 }
383}
384
385impl<'a, N> Construct for Node<PathGen, N>
386where
387 N: PathNode<'a>,
388{
389 fn construct(&self) -> Vec<u8> {
390 let mut vec = escape(N::NAME).into_owned();
391 vec.extend_from_slice(b".");
392 vec.extend_from_slice(&escape(&self.segment.to_bytes()));
393 vec
394 }
395}
396
397impl<'a, Inner, N> Construct for Prefix<Inner, N>
398where
399 Inner: Construct,
400 N: PathNode<'a>,
401{
402 fn construct(&self) -> Vec<u8> {
403 let mut vec = self.inner.construct();
404 vec.extend_from_slice(b".");
405 vec.extend_from_slice(&escape(N::NAME));
406 vec
407 }
408}
409
410impl<'a, N> Construct for Prefix<PathGen, N>
411where
412 N: PathNode<'a>,
413{
414 fn construct(&self) -> Vec<u8> {
415 escape(N::NAME).into_owned()
416 }
417}
418
419const DELIMITER: u8 = b'.';
420const ESCAPE_TOKEN: u8 = b'\\';
421
422fn escape(s: &[u8]) -> Cow<'_, [u8]> {
423 flee::escape(s, DELIMITER, ESCAPE_TOKEN)
424}
425
426fn unescape(s: &[u8]) -> Cow<'_, [u8]> {
427 flee::unescape(s, DELIMITER, ESCAPE_TOKEN)
428}
429
430fn rsplit_once_escaped(s: &[u8]) -> Option<(&[u8], &[u8])> {
431 flee::rsplit_once_escaped(s, DELIMITER, ESCAPE_TOKEN)
432}
433
434#[cfg(test)]
435mod tests {
436 use std::{
437 borrow::Cow,
438 fmt::Debug,
439 num::ParseIntError,
440 str::{Utf8Error, from_utf8},
441 };
442
443 fn test_pathnode<'a, 'b, N, N2, E>(path_node: N)
444 where
445 N: PathNode<'a, Error = E> + Debug,
446 N2: PathNode<'b, Error = E> + Debug + PartialEq<N> + 'b,
447 E: Debug,
448 {
449 test_roundtrip::<'a, 'b, N, N2, E>(path_node);
450 }
451
452 fn test_roundtrip<'a, 'b, T, T2, E>(item: T)
453 where
454 T: PathItem<'a, Error = E> + Debug,
455 T2: PathItem<'b, Error = E> + Debug + PartialEq<T> + 'b,
456 E: Debug,
457 {
458 let vec = item.to_bytes().to_vec();
459 let round_trip = T2::parse(Cow::Owned(vec.clone())).unwrap();
460 let plus_one = round_trip.to_bytes().to_vec();
461
462 assert_eq!(round_trip, item);
463 assert_eq!(vec, plus_one);
464 }
465
466 use super::{PathGen, PathItem, PathNode};
467
468 #[test]
469 fn construct_prefix() {
470 let s = PathGen
471 .push(Deck("deck-id".into()))
472 .prefix::<Key>()
473 .to_bytes();
474
475 assert_eq!(&s, b"deck.deck-id.key")
476 }
477
478 #[test]
479 fn construct_escaped_prefix() {
480 let s = PathGen
481 .push(Deck("deck.id".into()))
482 .prefix::<Key>()
483 .to_bytes();
484
485 assert_eq!(&s, b"deck.deck\\.id.key")
486 }
487
488 #[test]
489 fn deconstruct_prefix() {
490 let parser = PathGen::parser().push::<Deck>().prefix::<Key>();
491
492 let s = b"deck.deck-id.key";
493
494 let path = parser.parse(s).unwrap();
495
496 assert_eq!(path, PathGen.push(Deck("deck-id".into())).prefix::<Key>());
497 }
498
499 #[test]
500 fn deconstruct_escaped_prefix() {
501 let parser = PathGen::parser().push::<Deck>().prefix::<Key>();
502
503 let s = b"deck.deck\\.id.key";
504
505 let path = parser.parse(s).unwrap();
506
507 assert_eq!(path, PathGen.push(Deck("deck.id".into())).prefix::<Key>());
508 }
509
510 #[test]
511 fn test_parts() {
512 let string = String::from("hello");
513 let brw: &str = &string;
514
515 test_pathnode::<_, Deck<'_>, _>(Deck(Cow::Borrowed(brw)));
516 test_pathnode::<_, Key, _>(Key(5));
517 }
518
519 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
520 struct Deck<'a>(Cow<'a, str>);
521 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
522 struct Key(u8);
523
524 impl<'a> PathItem<'a> for Deck<'a> {
525 type Error = Utf8Error;
526
527 fn parse(bytes: Cow<'a, [u8]>) -> Result<Self, Self::Error>
528 where
529 Self: Sized + 'a,
530 {
531 let cow = match bytes {
532 Cow::Owned(bytes) => {
533 from_utf8(&bytes)?;
534 Cow::Owned(String::from_utf8(bytes).unwrap())
535 }
536 Cow::Borrowed(bytes) => Cow::Borrowed(from_utf8(bytes)?),
537 };
538
539 Ok(Deck(cow))
540 }
541
542 fn to_bytes(&self) -> Cow<'_, [u8]> {
543 Cow::Borrowed(self.0.as_bytes())
544 }
545 }
546
547 impl<'a> PathItem<'a> for Key {
548 type Error = ParseIntError;
549
550 fn parse(bytes: Cow<'a, [u8]>) -> Result<Self, Self::Error>
551 where
552 Self: Sized + 'a,
553 {
554 Ok(Key(String::from_utf8_lossy(&bytes).parse()?))
555 }
556
557 fn to_bytes(&self) -> Cow<'_, [u8]> {
558 Cow::Owned(self.0.to_string().into_bytes())
559 }
560 }
561
562 impl<'a> PathNode<'a> for Deck<'a> {
563 const NAME: &'static [u8] = b"deck";
564 }
565
566 impl PathNode<'_> for Key {
567 const NAME: &'static [u8] = b"key";
568 }
569}