use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Number<'a> {
pub value: f64,
pub lexeme: Option<&'a str>,
}
impl<'a> Number<'a> {
pub fn new(value: f64) -> Self {
Number {
value,
lexeme: None,
}
}
pub fn with_lexeme(value: f64, lexeme: &'a str) -> Self {
Number {
value,
lexeme: Some(lexeme),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum JsonEvent<'a> {
ObjectStart,
ObjectEnd,
ArrayStart,
ArrayEnd,
Key(&'a str),
Null,
Bool(bool),
Number(Number<'a>),
String(&'a str),
End,
}
impl JsonEvent<'_> {
pub fn to_owned(&self) -> OwnedJsonEvent {
match *self {
JsonEvent::ObjectStart => OwnedJsonEvent::ObjectStart,
JsonEvent::ObjectEnd => OwnedJsonEvent::ObjectEnd,
JsonEvent::ArrayStart => OwnedJsonEvent::ArrayStart,
JsonEvent::ArrayEnd => OwnedJsonEvent::ArrayEnd,
JsonEvent::Key(k) => OwnedJsonEvent::Key(k.into()),
JsonEvent::Null => OwnedJsonEvent::Null,
JsonEvent::Bool(b) => OwnedJsonEvent::Bool(b),
JsonEvent::Number(n) => OwnedJsonEvent::Number {
value: n.value,
lexeme: n.lexeme.map(Into::into),
},
JsonEvent::String(s) => OwnedJsonEvent::String(s.into()),
JsonEvent::End => OwnedJsonEvent::End,
}
}
pub fn is_start(&self) -> bool {
matches!(self, JsonEvent::ObjectStart | JsonEvent::ArrayStart)
}
pub fn is_end(&self) -> bool {
matches!(self, JsonEvent::ObjectEnd | JsonEvent::ArrayEnd)
}
pub fn is_scalar(&self) -> bool {
matches!(
self,
JsonEvent::Null | JsonEvent::Bool(_) | JsonEvent::Number(_) | JsonEvent::String(_)
)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum OwnedJsonEvent {
ObjectStart,
ObjectEnd,
ArrayStart,
ArrayEnd,
Key(Box<str>),
Null,
Bool(bool),
Number {
value: f64,
lexeme: Option<Box<str>>,
},
String(Box<str>),
End,
}
impl OwnedJsonEvent {
pub fn as_event(&self) -> JsonEvent<'_> {
match self {
OwnedJsonEvent::ObjectStart => JsonEvent::ObjectStart,
OwnedJsonEvent::ObjectEnd => JsonEvent::ObjectEnd,
OwnedJsonEvent::ArrayStart => JsonEvent::ArrayStart,
OwnedJsonEvent::ArrayEnd => JsonEvent::ArrayEnd,
OwnedJsonEvent::Key(k) => JsonEvent::Key(k),
OwnedJsonEvent::Null => JsonEvent::Null,
OwnedJsonEvent::Bool(b) => JsonEvent::Bool(*b),
OwnedJsonEvent::Number { value, lexeme } => JsonEvent::Number(Number {
value: *value,
lexeme: lexeme.as_deref(),
}),
OwnedJsonEvent::String(s) => JsonEvent::String(s),
OwnedJsonEvent::End => JsonEvent::End,
}
}
}
impl fmt::Display for OwnedJsonEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OwnedJsonEvent::ObjectStart => f.write_str("{"),
OwnedJsonEvent::ObjectEnd => f.write_str("}"),
OwnedJsonEvent::ArrayStart => f.write_str("["),
OwnedJsonEvent::ArrayEnd => f.write_str("]"),
OwnedJsonEvent::Key(k) => write!(f, "key {k:?}"),
OwnedJsonEvent::Null => f.write_str("null"),
OwnedJsonEvent::Bool(b) => write!(f, "{b}"),
OwnedJsonEvent::Number { value, lexeme } => match lexeme {
Some(l) => write!(f, "{l}"),
None => write!(f, "{value}"),
},
OwnedJsonEvent::String(s) => write!(f, "{s:?}"),
OwnedJsonEvent::End => f.write_str("end"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn owned_round_trips_through_borrowed() {
let events = [
JsonEvent::ObjectStart,
JsonEvent::Key("a"),
JsonEvent::Number(Number::with_lexeme(1.0, "1.0")),
JsonEvent::Key("b"),
JsonEvent::String("x"),
JsonEvent::ObjectEnd,
JsonEvent::End,
];
for ev in events {
let owned = ev.to_owned();
assert_eq!(owned.as_event(), ev);
}
}
#[test]
fn classification() {
assert!(JsonEvent::ArrayStart.is_start());
assert!(JsonEvent::ObjectEnd.is_end());
assert!(JsonEvent::Null.is_scalar());
assert!(!JsonEvent::Key("k").is_scalar());
assert!(!JsonEvent::End.is_scalar());
}
}