vdf_reader/
reader.rs

1use super::{Result, Token};
2use crate::error::{NoValidTokenError, UnexpectedTokenError};
3use crate::event::{
4    EntryEvent, Event, EventType, GroupEndEvent, GroupStartEvent, Item, ValueContinuationEvent,
5};
6use logos::{Lexer, Logos, Span, SpannedIter};
7use std::borrow::Cow;
8
9/// A VDF token reader.
10pub struct Reader<'a> {
11    pub source: &'a str,
12    pub last_event: Option<EventType>,
13    lexer: SpannedIter<'a, Token>,
14}
15
16impl<'a> From<&'a str> for Reader<'a> {
17    fn from(content: &'a str) -> Self {
18        Reader {
19            source: content,
20            last_event: None,
21            lexer: Lexer::new(content).spanned(),
22        }
23    }
24}
25
26impl<'a> Reader<'a> {
27    fn token(&mut self) -> Option<(Result<Token, <Token as Logos>::Error>, Span)> {
28        self.lexer.next()
29    }
30
31    pub fn span(&self) -> Span {
32        self.lexer.span()
33    }
34
35    /// Get the next event, this does copies.
36    pub fn event(&mut self) -> Option<Result<Event<'a>>> {
37        let result = self.event_inner();
38        if let Some(Ok(event)) = &result {
39            self.last_event = Some(event.ty());
40        }
41        result
42    }
43
44    #[allow(dead_code)]
45    fn event_inner(&mut self) -> Option<Result<Event<'a>>> {
46        const VALID_KEY: &[Token] = &[
47            Token::Item,
48            Token::QuotedItem,
49            Token::GroupEnd,
50            Token::Statement,
51            Token::QuotedStatement,
52        ];
53
54        let whitespace_start = self.span().end;
55
56        let key = match self.token() {
57            None => {
58                return None;
59            }
60            Some((Err(_), span)) => {
61                return Some(Err(NoValidTokenError::new(
62                    VALID_KEY,
63                    span.into(),
64                    self.source.into(),
65                )
66                .into()));
67            }
68            Some((Ok(Token::GroupEnd), span)) => {
69                return Some(Ok(Event::GroupEnd(GroupEndEvent { span })))
70            }
71
72            Some((Ok(Token::Item), span)) => Item::Item {
73                content: string(self.lexer.slice()),
74                span,
75            },
76
77            Some((Ok(Token::QuotedItem), span)) => Item::Item {
78                content: quoted_string(self.lexer.slice()),
79                span,
80            },
81
82            Some((Ok(Token::Statement), span)) => Item::Statement {
83                content: string(self.lexer.slice()),
84                span,
85            },
86
87            Some((Ok(Token::QuotedStatement), span)) => Item::Statement {
88                content: quoted_string(self.lexer.slice()),
89                span,
90            },
91
92            Some((Ok(token), span)) => {
93                return Some(Err(UnexpectedTokenError::new(
94                    VALID_KEY,
95                    Some(token),
96                    span.into(),
97                    self.source.into(),
98                )
99                .into()))
100            }
101        };
102
103        let whitespace_end = self.span().start;
104        let skipped_newline = self.source[whitespace_start..whitespace_end].contains('\n');
105        let last_event_has_value = matches!(
106            self.last_event,
107            Some(EventType::Entry | EventType::ValueContinuation)
108        );
109
110        // multiple values on the same line create an array
111        if last_event_has_value && !skipped_newline {
112            return Some(Ok(Event::ValueContinuation(ValueContinuationEvent {
113                value: key,
114                span: self.span(),
115            })));
116        }
117
118        const VALID_VALUE: &[Token] = &[
119            Token::Item,
120            Token::QuotedItem,
121            Token::GroupStart,
122            Token::Statement,
123            Token::QuotedStatement,
124        ];
125
126        let value = match self.token() {
127            None => {
128                return Some(Err(UnexpectedTokenError::new(
129                    VALID_VALUE,
130                    None,
131                    self.lexer.span().into(),
132                    self.source.into(),
133                )
134                .into()));
135            }
136
137            Some((Err(_), span)) => {
138                return Some(Err(NoValidTokenError::new(
139                    VALID_VALUE,
140                    span.into(),
141                    self.source.into(),
142                )
143                .into()));
144            }
145
146            Some((Ok(Token::GroupStart), span)) => {
147                return Some(Ok(Event::GroupStart(GroupStartEvent {
148                    name: key.into_content(),
149                    span,
150                })))
151            }
152
153            Some((Ok(Token::QuotedItem), span)) => Item::Item {
154                content: quoted_string(self.lexer.slice()),
155                span,
156            },
157
158            Some((Ok(Token::Item), span)) => Item::Item {
159                content: string(self.lexer.slice()),
160                span,
161            },
162
163            Some((Ok(Token::QuotedStatement), span)) => Item::Statement {
164                content: quoted_string(self.lexer.slice()),
165                span,
166            },
167
168            Some((Ok(Token::Statement), span)) => Item::Statement {
169                content: string(self.lexer.slice()),
170                span,
171            },
172
173            Some((Ok(token), span)) => {
174                return Some(Err(UnexpectedTokenError::new(
175                    VALID_VALUE,
176                    Some(token),
177                    span.into(),
178                    self.source.into(),
179                )
180                .into()))
181            }
182        };
183
184        let span = key.span().start..value.span().end;
185        Some(Ok(Event::Entry(EntryEvent { key, value, span })))
186    }
187}
188
189impl<'a> Iterator for Reader<'a> {
190    type Item = Result<Event<'a>>;
191
192    fn next(&mut self) -> Option<Self::Item> {
193        self.event()
194    }
195}
196
197pub(crate) fn quoted_string(source: &str) -> Cow<str> {
198    let source = &source[1..source.len() - 1];
199
200    if source.contains(r#"\""#) || source.contains(r#"\\"#) {
201        let mut buffer = source.bytes();
202        let mut string = Vec::with_capacity(buffer.len());
203
204        while let Some(byte) = buffer.next() {
205            if byte == b'\\' {
206                match buffer.next() {
207                    Some(b'\\') => string.push(b'\\'),
208                    Some(b'"') => string.push(b'"'),
209                    Some(byte) => string.extend_from_slice(&[b'\\', byte]),
210                    None => break,
211                }
212            } else {
213                string.push(byte);
214            }
215        }
216
217        String::from_utf8(string).unwrap().into()
218    } else {
219        source.into()
220    }
221}
222
223fn string(source: &str) -> Cow<str> {
224    source.into()
225}