use crate::raw_token::RawToken;
use std::fmt::Display;
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum Token {
Null,
Bool(bool),
String(String),
ObjectKey(String),
Number(Vec<u8>),
ObjectStart,
ObjectEnd,
ArrayStart,
ArrayEnd,
Comma,
Colon,
}
mod private {
pub trait Sealed {}
impl Sealed for bool {}
impl Sealed for String {}
impl Sealed for &str {}
impl Sealed for u8 {}
impl Sealed for u16 {}
impl Sealed for u32 {}
impl Sealed for u64 {}
impl Sealed for usize {}
impl Sealed for i8 {}
impl Sealed for i16 {}
impl Sealed for i32 {}
impl Sealed for i64 {}
impl Sealed for isize {}
impl Sealed for f64 {}
}
pub trait FromJson<'o>: private::Sealed
where
Self: Sized,
Self: 'o,
{
fn from_json(value: &'o Token) -> Option<Self>;
}
impl FromJson<'_> for bool {
fn from_json(value: &Token) -> Option<Self> {
match value {
Token::Bool(b) => Some(*b),
_ => None,
}
}
}
impl FromJson<'_> for String {
fn from_json(value: &Token) -> Option<Self> {
match value {
Token::String(ref s) => Some(s.clone()),
_ => None,
}
}
}
impl<'o> FromJson<'o> for &'o str {
fn from_json(value: &'o Token) -> Option<Self> {
match value {
Token::String(ref s) => Some(s.as_str()),
_ => None,
}
}
}
macro_rules! from_json_number_impl {
($e:ty) => {
impl FromJson<'_> for $e {
fn from_json(value: &crate::token::Token) -> Option<Self> {
match value {
Token::Number(ref n) => lexical::parse(n).ok(),
_ => None,
}
}
}
};
}
from_json_number_impl!(u8);
from_json_number_impl!(u16);
from_json_number_impl!(u32);
from_json_number_impl!(u64);
from_json_number_impl!(usize);
from_json_number_impl!(i8);
from_json_number_impl!(i16);
from_json_number_impl!(i32);
from_json_number_impl!(i64);
from_json_number_impl!(isize);
from_json_number_impl!(f64);
impl PartialEq<&str> for Token {
fn eq(&self, other: &&str) -> bool {
match self {
Token::String(ref s) => s == other,
Token::ObjectKey(ref s) => s == other,
_ => false,
}
}
}
impl PartialEq<String> for Token {
fn eq(&self, other: &String) -> bool {
match self {
Token::String(ref s) => s == other,
Token::ObjectKey(ref s) => s == other,
_ => false,
}
}
}
impl PartialEq<bool> for Token {
fn eq(&self, other: &bool) -> bool {
match self {
Token::Bool(ref b) => b == other,
_ => false,
}
}
}
macro_rules! partialeq_number_impl {
($e:ty) => {
impl PartialEq<$e> for Token {
fn eq(&self, other: &$e) -> bool {
match &self {
Token::Number(_) => self.get::<$e>().map(|n| &n == other).unwrap_or(false),
_ => false,
}
}
}
};
}
partialeq_number_impl!(u8);
partialeq_number_impl!(u16);
partialeq_number_impl!(u32);
partialeq_number_impl!(u64);
partialeq_number_impl!(usize);
partialeq_number_impl!(i8);
partialeq_number_impl!(i16);
partialeq_number_impl!(i32);
partialeq_number_impl!(i64);
partialeq_number_impl!(isize);
partialeq_number_impl!(f64);
impl Token {
pub fn get<'o, T: FromJson<'o>>(&'o self) -> Option<T> {
T::from_json(self)
}
pub fn is_object_key(&self) -> bool {
matches!(self, Token::ObjectKey(_))
}
pub fn is_string(&self) -> bool {
matches!(self, Token::String(_))
}
pub fn is_bool(&self) -> bool {
matches!(self, Token::Bool(_))
}
pub fn is_number(&self) -> bool {
matches!(self, Token::Number(_))
}
}
impl From<RawToken<'_>> for Token {
fn from(value: RawToken<'_>) -> Self {
match value {
RawToken::Eof => panic!("EOF should cause the iterator to return None"),
RawToken::ArrayStart => Token::ArrayStart,
RawToken::ArrayEnd => Token::ArrayEnd,
RawToken::ObjectStart => Token::ObjectStart,
RawToken::ObjectEnd => Token::ObjectEnd,
RawToken::Colon => Token::Colon,
RawToken::Comma => Token::Comma,
RawToken::ObjectKey(s) => Token::ObjectKey(String::from(s)),
RawToken::Number(n) => Token::Number(n.to_vec()),
RawToken::String(s) => Token::String(String::from(s)),
RawToken::Bool(b) => Token::Bool(b),
RawToken::Null => Token::Null,
}
}
}
impl Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Token::Null => write!(f, "null"),
Token::ArrayStart => write!(f, "["),
Token::ObjectStart => write!(f, "{{"),
Token::ArrayEnd => write!(f, "]"),
Token::ObjectEnd => write!(f, "}}"),
Token::Comma => write!(f, ","),
Token::Colon => write!(f, ":"),
Token::Number(n) => write!(
f,
"{}",
std::str::from_utf8(n).expect("numbers should be utf8")
),
Token::String(s) | Token::ObjectKey(s) => write!(f, "\"{}\"", s),
Token::Bool(true) => write!(f, "true"),
Token::Bool(false) => write!(f, "false"),
}
}
}
#[cfg(test)]
mod token_tests {
use super::*;
#[test]
fn token_equality() {
assert_eq!(Token::Number(b"12".to_vec()), 12u8);
assert_eq!(Token::String("hello".into()), "hello");
assert_eq!(Token::Bool(true), true);
}
#[test]
fn token_conversion() {
assert_eq!(Some(12u8), Token::Number(b"12".to_vec()).get());
assert_eq!(Some(-12i8), Token::Number(b"-12".to_vec()).get());
assert_eq!(Some(300u16), Token::Number(b"300".to_vec()).get());
assert_eq!(Some(-300i16), Token::Number(b"-300".to_vec()).get());
assert_eq!(Some(70_000u32), Token::Number(b"70000".to_vec()).get());
assert_eq!(Some(-70_000i32), Token::Number(b"-70000".to_vec()).get());
assert_eq!(
Some(5_000_000_000u64),
Token::Number(b"5000000000".to_vec()).get()
);
assert_eq!(
Some(-5_000_000_000i64),
Token::Number(b"-5000000000".to_vec()).get()
);
assert_eq!(Some(3.134), Token::Number(b"3.134".to_vec()).get());
assert_eq!(Some(3.134), Token::Number(b"0.3134e1".to_vec()).get());
assert_eq!(Some(3.134), Token::Number(b"3134e-3".to_vec()).get());
assert_eq!(Some(true), Token::Bool(true).get());
assert_eq!(Some(false), Token::Bool(false).get());
assert_eq!(
Some(String::from("hello")),
Token::String(String::from("hello")).get()
);
assert_eq!(Some("hello"), Token::String(String::from("hello")).get());
}
}