1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use crate::{borrowed::UnvalidatedRawLogline as BorrowedLine, shared::validate_line, types::*};

/// A (thread safe) line string
///
/// We use a [`Arc<str>`] over [`String`] to communicate its immutability and fixedness.
/// (`Arc<str>`/`Box<str>`/`&str` don't carry any capacity data, only the length.)
///
/// While `Box<str>` is also possible, the box is not thread safe and
/// and both types have pretty similar performance characteristics.
pub type LineStr = Arc<str>;

self_cell::self_cell!(
    struct Container {
        owner: LineStr,

        #[covariant]
        dependent: BorrowedLine,
    }

    impl {Debug, PartialEq}
);

impl Clone for Container {
    fn clone(&self) -> Self {
        let input = Arc::clone(self.borrow_owner());
        Self::new(input, |line| BorrowedLine::from(line.as_ref()))
    }
}

pub type UnvalidatedLogline = Logline<Unvalidated>;
pub type ValidatedLogline = Logline<Validated>;

#[derive(Debug, Clone, PartialEq)]
pub struct Logline<V> {
    inner: Container,
    _marker: PhantomData<V>,
}

impl<V> Logline<V> {
    pub fn view(&self) -> &BorrowedLine<'_> {
        self.inner.borrow_dependent()
    }

    pub fn as_raw(&self) -> &str {
        self.inner.borrow_owner()
    }

    pub fn into_raw(self) -> LineStr {
        self.inner.into_owner()
    }
}

macro_rules! impl_try_from {
    ($in:ty, $out_v:ident, $out_u:ident) => {
        impl TryFrom<$in> for $out_v {
            type Error = &'static str;

            fn try_from(line: $in) -> Result<Self, Self::Error> {
                let line: LineStr = line.into();
                validate_line(&line)?;
                let container = Container::new(line, |line| BorrowedLine::from(line.as_ref()));
                Ok($out_v {
                    inner: container,
                    _marker: PhantomData,
                })
            }
        }

        impl TryFrom<$in> for $out_u {
            type Error = &'static str;

            fn try_from(line: $in) -> Result<Self, Self::Error> {
                let container =
                    Container::new(line.into(), |line| BorrowedLine::from(line.as_ref()));
                Ok($out_u {
                    inner: container,
                    _marker: PhantomData,
                })
            }
        }
    };
}

impl_try_from!(&str, ValidatedLogline, UnvalidatedLogline);
impl_try_from!(String, ValidatedLogline, UnvalidatedLogline);
impl_try_from!(Box<str>, ValidatedLogline, UnvalidatedLogline);
impl_try_from!(Arc<str>, ValidatedLogline, UnvalidatedLogline);