mirror of
https://codeberg.org/icewind/vdf-reader.git
synced 2026-06-03 18:14:07 +02:00
event casts
This commit is contained in:
parent
ff98651b2a
commit
909cd4a18d
5 changed files with 222 additions and 86 deletions
|
|
@ -1,5 +1,6 @@
|
|||
use super::{Array, Entry};
|
||||
use crate::entry::{Statement, Value};
|
||||
use crate::event::{EntryEvent, GroupStartEvent};
|
||||
use crate::{Event, Item, Reader, Result};
|
||||
use serde::{Serialize, Serializer};
|
||||
use std::collections::hash_map;
|
||||
|
|
@ -51,21 +52,23 @@ impl Table {
|
|||
|
||||
while let Some(event) = reader.event() {
|
||||
match event? {
|
||||
Event::Entry {
|
||||
Event::Entry(EntryEvent {
|
||||
key: Item::Item { content: key, .. },
|
||||
value,
|
||||
..
|
||||
} => insert(&mut map, key, Value::from(value.into_content())),
|
||||
}) => insert(&mut map, key, Value::from(value.into_content())),
|
||||
|
||||
Event::Entry {
|
||||
Event::Entry(EntryEvent {
|
||||
key: Item::Statement { content: key, .. },
|
||||
value,
|
||||
..
|
||||
} => insert(&mut map, key, Statement::from(value.into_content())),
|
||||
}) => insert(&mut map, key, Statement::from(value.into_content())),
|
||||
|
||||
Event::GroupStart { name, .. } => insert(&mut map, name, Table::load(reader)?),
|
||||
Event::GroupStart(GroupStartEvent { name, .. }) => {
|
||||
insert(&mut map, name, Table::load(reader)?)
|
||||
}
|
||||
|
||||
Event::GroupEnd { .. } => break,
|
||||
Event::GroupEnd(_) => break,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
43
src/error.rs
43
src/error.rs
|
|
@ -1,4 +1,4 @@
|
|||
use crate::Token;
|
||||
use crate::{Event, Token};
|
||||
use miette::{Diagnostic, SourceSpan};
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
|
@ -15,6 +15,10 @@ pub enum VdfError {
|
|||
#[diagnostic(transparent)]
|
||||
/// No valid token found
|
||||
NoValidToken(#[from] NoValidTokenError),
|
||||
#[error(transparent)]
|
||||
#[diagnostic(transparent)]
|
||||
/// Wrong event to for conversion
|
||||
WrongEntryType(#[from] WrongEventTypeError),
|
||||
}
|
||||
|
||||
struct ExpectedTokens<'a>(&'a [Token]);
|
||||
|
|
@ -85,8 +89,9 @@ impl Display for UnexpectedTokenError {
|
|||
impl Error for UnexpectedTokenError {}
|
||||
|
||||
/// A token that wasn't expected was found while parsing
|
||||
#[derive(Debug, Clone, Diagnostic)]
|
||||
#[derive(Debug, Clone, Diagnostic, Error)]
|
||||
#[diagnostic(code(vmt_reader::no_valid_token))]
|
||||
#[error("No valid token found, expected one of {}", ExpectedTokens(self.expected))]
|
||||
pub struct NoValidTokenError {
|
||||
#[label("Expected {}", ExpectedTokens(self.expected))]
|
||||
err_span: SourceSpan,
|
||||
|
|
@ -105,14 +110,32 @@ impl NoValidTokenError {
|
|||
}
|
||||
}
|
||||
|
||||
impl Display for NoValidTokenError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"No valid token found, expected one of {}",
|
||||
ExpectedTokens(self.expected)
|
||||
)
|
||||
/// Wrong event to for conversion
|
||||
#[derive(Debug, Clone, Diagnostic, Error)]
|
||||
#[diagnostic(code(vmt_reader::wrong_value_type))]
|
||||
#[error("Wrong event to for conversion, expected a {expected} but found a {got}")]
|
||||
pub struct WrongEventTypeError {
|
||||
pub expected: &'static str,
|
||||
pub got: &'static str,
|
||||
pub event: Event<'static>,
|
||||
#[label("Expected a {}", self.expected)]
|
||||
err_span: SourceSpan,
|
||||
#[source_code]
|
||||
src: String,
|
||||
}
|
||||
|
||||
impl WrongEventTypeError {
|
||||
pub fn new(event: Event, expected: &'static str, got: &'static str) -> Self {
|
||||
WrongEventTypeError {
|
||||
err_span: event.span().into(),
|
||||
event: event.into_owned(),
|
||||
expected,
|
||||
got,
|
||||
src: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for NoValidTokenError {}
|
||||
pub fn with_source(self, src: String) -> Self {
|
||||
WrongEventTypeError { src, ..self }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
155
src/event.rs
Normal file
155
src/event.rs
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
use crate::error::WrongEventTypeError;
|
||||
use crate::VdfError;
|
||||
use logos::Span;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Kinds of item.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Item<'a> {
|
||||
/// A statement, the ones starting with #.
|
||||
Statement { content: Cow<'a, str>, span: Span },
|
||||
|
||||
/// A value.
|
||||
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 {
|
||||
Item::Statement { span, .. } => span.clone(),
|
||||
Item::Item { span, .. } => span.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_content(self) -> Cow<'a, str> {
|
||||
match self {
|
||||
Item::Statement { content, .. } => content,
|
||||
Item::Item { content, .. } => content,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reader event.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Event<'a> {
|
||||
/// A group with the given name is starting.
|
||||
GroupStart(GroupStartEvent<'a>),
|
||||
|
||||
/// A group has ended.
|
||||
GroupEnd(GroupEndEvent),
|
||||
|
||||
/// An entry.
|
||||
Entry(EntryEvent<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct GroupStartEvent<'a> {
|
||||
pub name: Cow<'a, str>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl GroupStartEvent<'_> {
|
||||
pub fn into_owned(self) -> GroupStartEvent<'static> {
|
||||
GroupStartEvent {
|
||||
name: self.name.into_owned().into(),
|
||||
span: self.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<Event<'a>> for GroupStartEvent<'a> {
|
||||
type Error = VdfError;
|
||||
|
||||
fn try_from(event: Event<'a>) -> Result<Self, Self::Error> {
|
||||
match event {
|
||||
Event::GroupStart(event) => Ok(event),
|
||||
Event::GroupEnd(_) => {
|
||||
Err(WrongEventTypeError::new(event, "group start", "group end").into())
|
||||
}
|
||||
Event::Entry(_) => Err(WrongEventTypeError::new(event, "group start", "entry").into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct GroupEndEvent {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<Event<'a>> for GroupEndEvent {
|
||||
type Error = VdfError;
|
||||
|
||||
fn try_from(event: Event<'a>) -> Result<Self, Self::Error> {
|
||||
match event {
|
||||
Event::GroupEnd(event) => Ok(event),
|
||||
Event::GroupStart(_) => {
|
||||
Err(WrongEventTypeError::new(event, "group end", "group start").into())
|
||||
}
|
||||
Event::Entry(_) => Err(WrongEventTypeError::new(event, "group start", "entry").into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct EntryEvent<'a> {
|
||||
pub key: Item<'a>,
|
||||
pub value: Item<'a>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl EntryEvent<'_> {
|
||||
pub fn into_owned(self) -> EntryEvent<'static> {
|
||||
EntryEvent {
|
||||
key: self.key.into_owned(),
|
||||
value: self.value.into_owned(),
|
||||
span: self.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<Event<'a>> for EntryEvent<'a> {
|
||||
type Error = VdfError;
|
||||
|
||||
fn try_from(event: Event<'a>) -> Result<Self, Self::Error> {
|
||||
match event {
|
||||
Event::Entry(event) => Ok(event),
|
||||
Event::GroupEnd(_) => Err(WrongEventTypeError::new(event, "entry", "group end").into()),
|
||||
Event::GroupStart(_) => {
|
||||
Err(WrongEventTypeError::new(event, "entry", "group start").into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Event<'_> {
|
||||
#[allow(dead_code)]
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
Event::GroupStart(GroupStartEvent { span, .. }) => span.clone(),
|
||||
Event::GroupEnd(GroupEndEvent { span, .. }) => span.clone(),
|
||||
Event::Entry(EntryEvent { span, .. }) => span.clone(),
|
||||
}
|
||||
}
|
||||
pub fn into_owned(self) -> Event<'static> {
|
||||
match self {
|
||||
Event::GroupStart(event) => Event::GroupStart(event.into_owned()),
|
||||
Event::GroupEnd(event) => Event::GroupEnd(event),
|
||||
Event::Entry(event) => Event::Entry(event.into_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
pub mod entry;
|
||||
mod error;
|
||||
mod event;
|
||||
mod parser;
|
||||
mod reader;
|
||||
|
||||
pub use error::VdfError;
|
||||
|
||||
pub type Result<T, E = VdfError> = std::result::Result<T, E>;
|
||||
pub use event::{EntryEvent, Event, GroupEndEvent, GroupStartEvent, Item};
|
||||
pub use parser::Token;
|
||||
pub use reader::{Event, Item, Reader};
|
||||
pub use reader::Reader;
|
||||
|
|
|
|||
|
|
@ -1,65 +1,12 @@
|
|||
use super::{Result, Token};
|
||||
use crate::error::{NoValidTokenError, UnexpectedTokenError};
|
||||
use crate::event::{EntryEvent, Event, GroupEndEvent, GroupStartEvent, Item};
|
||||
use logos::{Lexer, Logos, Span, SpannedIter};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Kinds of item.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Item<'a> {
|
||||
/// A statement, the ones starting with #.
|
||||
Statement { content: Cow<'a, str>, span: Span },
|
||||
|
||||
/// A value.
|
||||
Item { content: Cow<'a, str>, span: Span },
|
||||
}
|
||||
|
||||
impl<'a> Item<'a> {
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
Item::Statement { span, .. } => span.clone(),
|
||||
Item::Item { span, .. } => span.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_content(self) -> Cow<'a, str> {
|
||||
match self {
|
||||
Item::Statement { content, .. } => content,
|
||||
Item::Item { content, .. } => content,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reader event.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Event<'a> {
|
||||
/// A group with the given name is starting.
|
||||
GroupStart { name: Cow<'a, str>, span: Span },
|
||||
|
||||
/// A group has ended.
|
||||
GroupEnd { span: Span },
|
||||
|
||||
/// An entry.
|
||||
Entry {
|
||||
key: Item<'a>,
|
||||
value: Item<'a>,
|
||||
span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
impl Event<'_> {
|
||||
#[allow(dead_code)]
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
Event::GroupStart { span, .. } => span.clone(),
|
||||
Event::GroupEnd { span, .. } => span.clone(),
|
||||
Event::Entry { span, .. } => span.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A VDF token reader.
|
||||
pub struct Reader<'a> {
|
||||
pub(crate) content: &'a str,
|
||||
pub source: &'a str,
|
||||
lexer: SpannedIter<'a, Token>,
|
||||
peeked: Option<(Result<Token, <Token as Logos<'a>>::Error>, Span)>,
|
||||
}
|
||||
|
|
@ -67,7 +14,7 @@ pub struct Reader<'a> {
|
|||
impl<'a> From<&'a str> for Reader<'a> {
|
||||
fn from(content: &'a str) -> Self {
|
||||
Reader {
|
||||
content,
|
||||
source: content,
|
||||
lexer: Lexer::new(content).spanned(),
|
||||
peeked: None,
|
||||
}
|
||||
|
|
@ -90,6 +37,10 @@ impl<'a> Reader<'a> {
|
|||
self.peeked.clone()
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
self.lexer.span()
|
||||
}
|
||||
|
||||
fn token_eat_newlines(&mut self) -> Option<(Result<Token, <Token as Logos>::Error>, Span)> {
|
||||
loop {
|
||||
let (token, span) = self.token()?;
|
||||
|
|
@ -122,11 +73,13 @@ impl<'a> Reader<'a> {
|
|||
return Some(Err(NoValidTokenError::new(
|
||||
VALID_KEY,
|
||||
span.into(),
|
||||
self.content.into(),
|
||||
self.source.into(),
|
||||
)
|
||||
.into()));
|
||||
}
|
||||
Some((Ok(Token::GroupEnd), span)) => return Some(Ok(Event::GroupEnd { span })),
|
||||
Some((Ok(Token::GroupEnd), span)) => {
|
||||
return Some(Ok(Event::GroupEnd(GroupEndEvent { span })))
|
||||
}
|
||||
|
||||
Some((Ok(Token::Item), span)) => Item::Item {
|
||||
content: string(self.lexer.slice()),
|
||||
|
|
@ -153,7 +106,7 @@ impl<'a> Reader<'a> {
|
|||
VALID_KEY,
|
||||
Some(token),
|
||||
span.into(),
|
||||
self.content.into(),
|
||||
self.source.into(),
|
||||
)
|
||||
.into()))
|
||||
}
|
||||
|
|
@ -175,7 +128,7 @@ impl<'a> Reader<'a> {
|
|||
&[Token::GroupStart],
|
||||
Some(token),
|
||||
span.into(),
|
||||
self.content.into(),
|
||||
self.source.into(),
|
||||
)
|
||||
.into()))
|
||||
}
|
||||
|
|
@ -183,7 +136,7 @@ impl<'a> Reader<'a> {
|
|||
return Some(Err(NoValidTokenError::new(
|
||||
VALID_VALUE,
|
||||
span.into(),
|
||||
self.content.into(),
|
||||
self.source.into(),
|
||||
)
|
||||
.into()));
|
||||
}
|
||||
|
|
@ -192,7 +145,7 @@ impl<'a> Reader<'a> {
|
|||
VALID_VALUE,
|
||||
None,
|
||||
span.into(),
|
||||
self.content.into(),
|
||||
self.source.into(),
|
||||
)
|
||||
.into()))
|
||||
}
|
||||
|
|
@ -206,7 +159,7 @@ impl<'a> Reader<'a> {
|
|||
VALID_VALUE,
|
||||
None,
|
||||
self.lexer.span().into(),
|
||||
self.content.into(),
|
||||
self.source.into(),
|
||||
)
|
||||
.into()));
|
||||
}
|
||||
|
|
@ -215,16 +168,16 @@ impl<'a> Reader<'a> {
|
|||
return Some(Err(NoValidTokenError::new(
|
||||
VALID_VALUE,
|
||||
span.into(),
|
||||
self.content.into(),
|
||||
self.source.into(),
|
||||
)
|
||||
.into()));
|
||||
}
|
||||
|
||||
Some((Ok(Token::GroupStart), span)) => {
|
||||
return Some(Ok(Event::GroupStart {
|
||||
return Some(Ok(Event::GroupStart(GroupStartEvent {
|
||||
name: key.into_content(),
|
||||
span,
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
Some((Ok(Token::QuotedItem), span)) => Item::Item {
|
||||
|
|
@ -252,14 +205,14 @@ impl<'a> Reader<'a> {
|
|||
VALID_VALUE,
|
||||
Some(token),
|
||||
span.into(),
|
||||
self.content.into(),
|
||||
self.source.into(),
|
||||
)
|
||||
.into()))
|
||||
}
|
||||
};
|
||||
|
||||
let span = key.span().start..value.span().end;
|
||||
Some(Ok(Event::Entry { key, value, span }))
|
||||
Some(Ok(Event::Entry(EntryEvent { key, value, span })))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue