cloudfront_logs/referential/
simple.rs

1use crate::{borrowed::UnvalidatedSimpleLogline as BorrowedLine, shared::validate_line, types::*};
2
3/// A (thread safe) line string
4///
5/// We use a [`Arc<str>`] over [`String`] to communicate its immutability and fixedness.
6/// (`Arc<str>`/`Box<str>`/`&str` don't carry any capacity data, only the length.)
7///
8/// While `Box<str>` is also possible, the box is not thread safe and
9/// and both types have pretty similar performance characteristics.
10pub type LineStr = Arc<str>;
11
12self_cell::self_cell!(
13    struct Container {
14        owner: LineStr,
15
16        #[covariant]
17        dependent: BorrowedLine,
18    }
19
20    impl {Debug, PartialEq}
21);
22
23impl Clone for Container {
24    fn clone(&self) -> Self {
25        let input = Arc::clone(self.borrow_owner());
26        Self::new(input, |line| {
27            let dependent = BorrowedLine::try_from(line.as_ref())
28                .expect("invalid line input despite validation");
29            dependent
30        })
31    }
32}
33
34pub type UnvalidatedLogline = Logline<Unvalidated>;
35pub type ValidatedLogline = Logline<Validated>;
36
37#[derive(Debug, Clone, PartialEq)]
38pub struct Logline<V> {
39    inner: Container,
40    _marker: PhantomData<V>,
41}
42
43impl<V> Logline<V> {
44    pub fn view(&self) -> &BorrowedLine<'_> {
45        self.inner.borrow_dependent()
46    }
47
48    pub fn as_raw(&self) -> &str {
49        self.inner.borrow_owner()
50    }
51
52    pub fn into_raw(self) -> LineStr {
53        self.inner.into_owner()
54    }
55}
56
57macro_rules! impl_try_from {
58    ($in:ty, $out_v:ident, $out_u:ident) => {
59        impl TryFrom<$in> for $out_v {
60            type Error = &'static str;
61
62            fn try_from(line: $in) -> Result<Self, Self::Error> {
63                let line: LineStr = line.into();
64                validate_line(&line)?;
65                let container = Container::new(line, |line| {
66                    BorrowedLine::try_from(line.as_ref())
67                        .expect("invalid line input despite validation")
68                });
69                Ok($out_v {
70                    inner: container,
71                    _marker: PhantomData,
72                })
73            }
74        }
75
76        impl TryFrom<$in> for $out_u {
77            type Error = &'static str;
78
79            fn try_from(line: $in) -> Result<Self, Self::Error> {
80                let container = Container::new(line.into(), |line| {
81                    BorrowedLine::try_from(line.as_ref())
82                        .expect("invalid line input despite validation")
83                });
84                Ok($out_u {
85                    inner: container,
86                    _marker: PhantomData,
87                })
88            }
89        }
90    };
91}
92
93impl_try_from!(&str, ValidatedLogline, UnvalidatedLogline);
94impl_try_from!(String, ValidatedLogline, UnvalidatedLogline);
95impl_try_from!(Box<str>, ValidatedLogline, UnvalidatedLogline);
96impl_try_from!(Arc<str>, ValidatedLogline, UnvalidatedLogline);