Skip to main content

tabnas_alchemy/shared/
event.rs

1//! `JsonEvents/1`: the source protocol every transducer consumes.
2//!
3//! A source (a tabnas parse, a parsed value, a line-delimited reader) emits
4//! the events of one document in order: container boundaries, keys, whole
5//! scalars, and one [`JsonEvent::End`] after the root value. Events borrow
6//! from the source for the duration of one [`crate::shared::Sink::event`] call, so
7//! moving them costs nothing; a stage that keeps anything copies it, and
8//! says so in its retention contract.
9//!
10//! Scalars arrive whole because the tabnas lexer produces whole tokens. A
11//! later `JsonEvents/2` may chunk strings; it will be a separately named
12//! protocol, never a change to this one.
13
14use std::fmt;
15
16/// A number as the source spelled it and as a machine value.
17///
18/// `lexeme` is the source text when the source could hand it over (the
19/// rule-event adapter reads it off the token; the JSON Lines source likewise),
20/// else `None`, and the renderer falls back to the shortest round-trip form
21/// of `value`. Keeping both is how `50.25` stays `50.25` and how a number
22/// beyond f64's exact range keeps its digits.
23#[derive(Clone, Copy, Debug, PartialEq)]
24pub struct Number<'a> {
25    pub value: f64,
26    pub lexeme: Option<&'a str>,
27}
28
29impl<'a> Number<'a> {
30    pub fn new(value: f64) -> Self {
31        Number {
32            value,
33            lexeme: None,
34        }
35    }
36
37    pub fn with_lexeme(value: f64, lexeme: &'a str) -> Self {
38        Number {
39            value,
40            lexeme: Some(lexeme),
41        }
42    }
43}
44
45/// One event of `JsonEvents/1`.
46#[derive(Clone, Copy, Debug, PartialEq)]
47pub enum JsonEvent<'a> {
48    ObjectStart,
49    ObjectEnd,
50    ArrayStart,
51    ArrayEnd,
52    /// The name of the member whose value follows, inside an object.
53    Key(&'a str),
54    Null,
55    Bool(bool),
56    Number(Number<'a>),
57    String(&'a str),
58    /// The document is complete. Exactly one, after the root value, and
59    /// only after the whole source has been validated.
60    End,
61}
62
63impl JsonEvent<'_> {
64    /// An owned copy, for recorders and tests.
65    pub fn to_owned(&self) -> OwnedJsonEvent {
66        match *self {
67            JsonEvent::ObjectStart => OwnedJsonEvent::ObjectStart,
68            JsonEvent::ObjectEnd => OwnedJsonEvent::ObjectEnd,
69            JsonEvent::ArrayStart => OwnedJsonEvent::ArrayStart,
70            JsonEvent::ArrayEnd => OwnedJsonEvent::ArrayEnd,
71            JsonEvent::Key(k) => OwnedJsonEvent::Key(k.into()),
72            JsonEvent::Null => OwnedJsonEvent::Null,
73            JsonEvent::Bool(b) => OwnedJsonEvent::Bool(b),
74            JsonEvent::Number(n) => OwnedJsonEvent::Number {
75                value: n.value,
76                lexeme: n.lexeme.map(Into::into),
77            },
78            JsonEvent::String(s) => OwnedJsonEvent::String(s.into()),
79            JsonEvent::End => OwnedJsonEvent::End,
80        }
81    }
82
83    /// Whether this event opens a container.
84    pub fn is_start(&self) -> bool {
85        matches!(self, JsonEvent::ObjectStart | JsonEvent::ArrayStart)
86    }
87
88    /// Whether this event closes a container.
89    pub fn is_end(&self) -> bool {
90        matches!(self, JsonEvent::ObjectEnd | JsonEvent::ArrayEnd)
91    }
92
93    /// Whether this event is a whole scalar value.
94    pub fn is_scalar(&self) -> bool {
95        matches!(
96            self,
97            JsonEvent::Null | JsonEvent::Bool(_) | JsonEvent::Number(_) | JsonEvent::String(_)
98        )
99    }
100}
101
102/// [`JsonEvent`] with its text owned: what a recorder keeps.
103#[derive(Clone, Debug, PartialEq)]
104pub enum OwnedJsonEvent {
105    ObjectStart,
106    ObjectEnd,
107    ArrayStart,
108    ArrayEnd,
109    Key(Box<str>),
110    Null,
111    Bool(bool),
112    Number {
113        value: f64,
114        lexeme: Option<Box<str>>,
115    },
116    String(Box<str>),
117    End,
118}
119
120impl OwnedJsonEvent {
121    /// The borrowed view, to replay a recording into a sink.
122    pub fn as_event(&self) -> JsonEvent<'_> {
123        match self {
124            OwnedJsonEvent::ObjectStart => JsonEvent::ObjectStart,
125            OwnedJsonEvent::ObjectEnd => JsonEvent::ObjectEnd,
126            OwnedJsonEvent::ArrayStart => JsonEvent::ArrayStart,
127            OwnedJsonEvent::ArrayEnd => JsonEvent::ArrayEnd,
128            OwnedJsonEvent::Key(k) => JsonEvent::Key(k),
129            OwnedJsonEvent::Null => JsonEvent::Null,
130            OwnedJsonEvent::Bool(b) => JsonEvent::Bool(*b),
131            OwnedJsonEvent::Number { value, lexeme } => JsonEvent::Number(Number {
132                value: *value,
133                lexeme: lexeme.as_deref(),
134            }),
135            OwnedJsonEvent::String(s) => JsonEvent::String(s),
136            OwnedJsonEvent::End => JsonEvent::End,
137        }
138    }
139}
140
141impl fmt::Display for OwnedJsonEvent {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        match self {
144            OwnedJsonEvent::ObjectStart => f.write_str("{"),
145            OwnedJsonEvent::ObjectEnd => f.write_str("}"),
146            OwnedJsonEvent::ArrayStart => f.write_str("["),
147            OwnedJsonEvent::ArrayEnd => f.write_str("]"),
148            OwnedJsonEvent::Key(k) => write!(f, "key {k:?}"),
149            OwnedJsonEvent::Null => f.write_str("null"),
150            OwnedJsonEvent::Bool(b) => write!(f, "{b}"),
151            OwnedJsonEvent::Number { value, lexeme } => match lexeme {
152                Some(l) => write!(f, "{l}"),
153                None => write!(f, "{value}"),
154            },
155            OwnedJsonEvent::String(s) => write!(f, "{s:?}"),
156            OwnedJsonEvent::End => f.write_str("end"),
157        }
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn owned_round_trips_through_borrowed() {
167        let events = [
168            JsonEvent::ObjectStart,
169            JsonEvent::Key("a"),
170            JsonEvent::Number(Number::with_lexeme(1.0, "1.0")),
171            JsonEvent::Key("b"),
172            JsonEvent::String("x"),
173            JsonEvent::ObjectEnd,
174            JsonEvent::End,
175        ];
176        for ev in events {
177            let owned = ev.to_owned();
178            assert_eq!(owned.as_event(), ev);
179        }
180    }
181
182    #[test]
183    fn classification() {
184        assert!(JsonEvent::ArrayStart.is_start());
185        assert!(JsonEvent::ObjectEnd.is_end());
186        assert!(JsonEvent::Null.is_scalar());
187        assert!(!JsonEvent::Key("k").is_scalar());
188        assert!(!JsonEvent::End.is_scalar());
189    }
190}