event/entry parsing

This commit is contained in:
Robin Appelman 2023-12-17 22:20:56 +01:00
commit 26c9b82b8e
6 changed files with 172 additions and 51 deletions

View file

@ -1,4 +1,16 @@
mod array;
mod statement;
mod table;
mod value;
use crate::error::{ParseEntryError, ParseItemError};
use crate::Item;
pub use array::Array;
pub use statement::Statement;
use std::any::type_name;
use std::slice;
pub use table::Table;
pub use value::Value;
/// The kinds of entry.
#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
@ -57,12 +69,8 @@ impl Entry {
}
/// Try to convert the entry to the given type.
pub fn to<T: Parse>(&self) -> Option<T> {
if let Entry::Value(value) = self {
value.to::<T>()
} else {
None
}
pub fn to<T: ParseItem>(self) -> Result<T, ParseEntryError> {
T::from_entry(self)
}
/// Try to take the entry as a table.
@ -114,9 +122,12 @@ impl Entry {
}
/// Parsable types.
pub trait Parse: Sized {
/// Try to parse the string.
fn parse(string: &str) -> Option<Self>;
pub trait ParseItem: Sized {
/// Try to cast the entry into a concrete type
fn from_entry(entry: Entry) -> Result<Self, ParseEntryError>;
/// Try to cast the item into a concrete type
fn from_item(item: Item) -> Result<Self, ParseItemError>;
}
macro_rules! from_str {
@ -128,10 +139,20 @@ macro_rules! from_str {
);
($ty:ident) => (
impl Parse for $ty {
fn parse(string: &str) -> Option<Self> {
string.parse::<$ty>().ok()
impl ParseItem for $ty {
fn from_entry(entry: Entry) -> Result<Self, ParseEntryError> {
let string = match entry.as_str() {
Some(string) => string,
None => {
return Err(ParseEntryError::new(type_name::<Self>(), entry));
}
};
string.parse::<$ty>().map_err(|_| ParseEntryError::new(type_name::<Self>(), entry))
}
fn from_item(item: Item) -> Result<Self, ParseItemError> {
item.as_str().parse::<$ty>().map_err(|_| ParseItemError::new(type_name::<Self>(), item))
}
}
);
}
@ -141,25 +162,61 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV
from_str!(for IpAddr Ipv4Addr Ipv6Addr SocketAddr SocketAddrV4 SocketAddrV6);
from_str!(for i8 i16 i32 i64 isize u8 u16 u32 u64 usize f32 f64);
impl Parse for bool {
fn parse(string: &str) -> Option<Self> {
impl ParseItem for bool {
fn from_entry(entry: Entry) -> Result<Self, ParseEntryError> {
let string = match entry.as_str() {
Some(string) => string,
None => {
return Err(ParseEntryError::new(type_name::<Self>(), entry));
}
};
match string {
"0" => Some(false),
"1" => Some(true),
v => v.parse::<bool>().ok(),
"0" => Ok(false),
"1" => Ok(true),
v => v
.parse::<bool>()
.map_err(|_| ParseEntryError::new(type_name::<Self>(), entry)),
}
}
fn from_item(item: Item) -> Result<Self, ParseItemError> {
match item.as_str() {
"0" => Ok(false),
"1" => Ok(true),
v => v
.parse::<bool>()
.map_err(|_| ParseItemError::new(type_name::<Self>(), item)),
}
}
}
mod table;
pub use table::Table;
impl ParseItem for String {
fn from_entry(entry: Entry) -> Result<Self, ParseEntryError> {
match entry {
Entry::Table(entry) => Err(ParseEntryError::new(
type_name::<Self>(),
Entry::Table(entry),
)),
Entry::Array(entry) => Err(ParseEntryError::new(
type_name::<Self>(),
Entry::Array(entry),
)),
Entry::Statement(statement) => Ok(statement.into()),
Entry::Value(value) => Ok(value.into()),
}
}
mod array;
pub use array::Array;
fn from_item(item: Item) -> Result<Self, ParseItemError> {
Ok(item.into_content().into())
}
}
mod statement;
pub use statement::Statement;
impl<T: ParseItem> ParseItem for Option<T> {
fn from_entry(entry: Entry) -> Result<Self, ParseEntryError> {
T::from_entry(entry).map(Some)
}
mod value;
use crate::Item;
pub use value::Value;
fn from_item(item: Item) -> Result<Self, ParseItemError> {
T::from_item(item).map(Some)
}
}

View file

@ -19,6 +19,12 @@ impl From<Statement> for Entry {
}
}
impl From<Statement> for String {
fn from(value: Statement) -> Self {
value.0
}
}
impl Deref for Statement {
type Target = str;

View file

@ -1,4 +1,4 @@
use super::{Entry, Parse};
use super::Entry;
use serde::Serialize;
use std::borrow::Cow;
use std::ops::Deref;
@ -18,6 +18,12 @@ impl From<Value> for Entry {
}
}
impl From<Value> for String {
fn from(value: Value) -> Self {
value.0
}
}
impl Deref for Value {
type Target = str;
@ -25,10 +31,3 @@ impl Deref for Value {
&self.0
}
}
impl Value {
/// Try to convert the value to the given type.
pub fn to<T: Parse>(&self) -> Option<T> {
T::parse(&self.0)
}
}

View file

@ -1,4 +1,5 @@
use crate::{Event, Token};
use crate::entry::Entry;
use crate::{Event, Item, Token};
use miette::{Diagnostic, SourceSpan};
use std::error::Error;
use std::fmt::{Display, Formatter};
@ -19,6 +20,14 @@ pub enum VdfError {
#[diagnostic(transparent)]
/// Wrong event to for conversion
WrongEntryType(#[from] WrongEventTypeError),
#[error(transparent)]
#[diagnostic(transparent)]
/// Failed to parse entry into type
ParseEntry(#[from] ParseEntryError),
#[error(transparent)]
#[diagnostic(transparent)]
/// Failed to parse item into type
ParseItem(#[from] ParseItemError),
}
struct ExpectedTokens<'a>(&'a [Token]);
@ -134,8 +143,53 @@ impl WrongEventTypeError {
src: String::new(),
}
}
pub fn new_with_source(
event: Event,
expected: &'static str,
got: &'static str,
src: String,
) -> Self {
WrongEventTypeError {
err_span: event.span().into(),
event: event.into_owned(),
expected,
got,
src,
}
}
pub fn with_source(self, src: String) -> Self {
WrongEventTypeError { src, ..self }
}
}
#[derive(Debug, Clone, Error, Diagnostic)]
#[error("Can't parse entry {value:?} as {ty}")]
#[diagnostic(code(vmt_parser::eof))]
pub struct ParseEntryError {
pub ty: &'static str,
pub value: Entry,
}
impl ParseEntryError {
pub fn new(ty: &'static str, value: Entry) -> Self {
ParseEntryError { ty, value }
}
}
#[derive(Debug, Clone, Error, Diagnostic)]
#[error("Can't parse entry {value:?} as {ty}")]
#[diagnostic(code(vmt_parser::eof))]
pub struct ParseItemError {
pub ty: &'static str,
pub value: Item<'static>,
}
impl ParseItemError {
pub fn new(ty: &'static str, value: Item) -> Self {
ParseItemError {
ty,
value: value.into_owned(),
}
}
}

View file

@ -13,21 +13,6 @@ pub enum Item<'a> {
Item { content: Cow<'a, str>, span: Span },
}
impl Item<'_> {
pub fn into_owned(self) -> Item<'static> {
match self {
Item::Statement { content, span } => Item::Statement {
content: content.into_owned().into(),
span,
},
Item::Item { content, span } => Item::Item {
content: content.into_owned().into(),
span,
},
}
}
}
impl<'a> Item<'a> {
pub fn span(&self) -> Span {
match self {
@ -42,6 +27,26 @@ impl<'a> Item<'a> {
Item::Item { content, .. } => content,
}
}
pub fn as_str(&self) -> &str {
match self {
Item::Statement { content, .. } => content.as_ref(),
Item::Item { content, .. } => content.as_ref(),
}
}
pub fn into_owned(self) -> Item<'static> {
match self {
Item::Statement { content, span } => Item::Statement {
content: content.into_owned().into(),
span,
},
Item::Item { content, span } => Item::Item {
content: content.into_owned().into(),
span,
},
}
}
}
/// Reader event.

View file

@ -1,5 +1,5 @@
pub mod entry;
mod error;
pub mod error;
mod event;
mod parser;
mod reader;