extern crate alloc;
use alloc::{
borrow::Cow,
collections::VecDeque,
string::{String, ToString},
vec::Vec,
};
use facet_format::{
ContainerKind, DeserializeErrorKind, FieldKey, FieldLocationHint, FormatParser, ParseError,
ParseEvent, ParseEventKind, SavePoint, ScalarValue,
};
use toml_parser::{
ErrorSink, Raw, Source,
decoder::ScalarKind,
parser::{Event, EventKind, RecursionGuard, parse_document},
};
struct TomlParseErrorCollector {
error: Option<(String, facet_reflect::Span)>,
}
impl TomlParseErrorCollector {
const fn new() -> Self {
Self { error: None }
}
fn take_error(&mut self) -> Option<(String, facet_reflect::Span)> {
self.error.take()
}
}
impl ErrorSink for TomlParseErrorCollector {
fn report_error(&mut self, error: toml_parser::ParseError) {
if self.error.is_none() {
let toml_span = error
.context()
.or(error.unexpected())
.expect("toml_parser::ParseError must have either context or unexpected span set");
let span =
facet_reflect::Span::new(toml_span.start(), toml_span.end() - toml_span.start());
self.error = Some((error.description().to_string(), span));
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SegmentKind {
Table,
Array,
ArrayElement,
}
#[derive(Debug, Clone)]
struct PathSegment<'de> {
name: Cow<'de, str>,
kind: SegmentKind,
}
pub struct TomlParser<'de> {
input: &'de str,
events: Vec<Event>,
pos: usize,
current_path: Vec<PathSegment<'de>>,
pending_events: VecDeque<ParseEvent<'de>>,
event_peek: Option<ParseEvent<'de>>,
root_started: bool,
root_ended: bool,
inline_stack: Vec<(bool, usize)>,
last_span: facet_reflect::Span,
save_counter: u64,
saved_states: Vec<(u64, SavedState<'de>)>,
}
#[derive(Clone)]
struct SavedState<'de> {
pos: usize,
current_path: Vec<PathSegment<'de>>,
pending_events: VecDeque<ParseEvent<'de>>,
event_peek: Option<ParseEvent<'de>>,
root_started: bool,
root_ended: bool,
inline_stack: Vec<(bool, usize)>,
}
impl<'de> TomlParser<'de> {
pub fn new(input: &'de str) -> Result<Self, ParseError> {
let source = Source::new(input);
let tokens: Vec<_> = source.lex().collect();
let mut events: Vec<Event> = Vec::new();
let mut guarded = RecursionGuard::new(&mut events, 128);
let mut error_collector = TomlParseErrorCollector::new();
parse_document(&tokens, &mut guarded, &mut error_collector);
if let Some((err_msg, span)) = error_collector.take_error() {
return Err(ParseError::new(
span,
DeserializeErrorKind::InvalidValue {
message: err_msg.into(),
},
));
}
Ok(Self {
input,
events,
pos: 0,
current_path: Vec::new(),
pending_events: VecDeque::new(),
event_peek: None,
root_started: false,
root_ended: false,
inline_stack: Vec::new(),
last_span: facet_reflect::Span::new(0, 0),
save_counter: 0,
saved_states: Vec::new(),
})
}
pub const fn input(&self) -> &'de str {
self.input
}
fn eof_span(&self) -> facet_reflect::Span {
facet_reflect::Span::new(self.input.len(), 0)
}
#[inline]
fn should_skip(event: &Event) -> bool {
matches!(
event.kind(),
EventKind::Whitespace | EventKind::Comment | EventKind::Newline
)
}
fn peek_raw(&self) -> Option<&Event> {
let mut pos = self.pos;
while pos < self.events.len() {
let event = &self.events[pos];
if !Self::should_skip(event) {
return Some(event);
}
pos += 1;
}
None
}
fn next_raw(&mut self) -> Option<&Event> {
while self.pos < self.events.len() {
let event = &self.events[self.pos];
self.pos += 1;
if !Self::should_skip(event) {
return Some(event);
}
}
None
}
fn get_span_str(&self, event: &Event) -> &'de str {
let span = event.span();
&self.input[span.start()..span.end()]
}
fn raw_from_event(&self, event: &Event) -> Raw<'de> {
let span = event.span();
Raw::new_unchecked(
&self.input[span.start()..span.end()],
event.encoding(),
span,
)
}
fn decode_scalar(&self, event: &Event) -> Result<ScalarValue<'de>, ParseError> {
let raw = self.raw_from_event(event);
let mut output: Cow<'de, str> = Cow::Borrowed("");
let kind = raw.decode_scalar(&mut output, &mut ());
let span = event.span();
let facet_span = facet_reflect::Span::new(span.start(), span.end() - span.start());
match kind {
ScalarKind::String => {
Ok(ScalarValue::Str(output))
}
ScalarKind::Boolean(b) => Ok(ScalarValue::Bool(b)),
ScalarKind::Integer(radix) => {
let clean: String = output.chars().filter(|c| *c != '_').collect();
let n: i64 = i64::from_str_radix(&clean, radix.value()).map_err(|e| {
ParseError::new(
facet_span,
DeserializeErrorKind::InvalidValue {
message: e.to_string().into(),
},
)
})?;
Ok(ScalarValue::I64(n))
}
ScalarKind::Float => {
let clean: String = output.chars().filter(|c| *c != '_').collect();
let f: f64 = match clean.as_str() {
"inf" | "+inf" => f64::INFINITY,
"-inf" => f64::NEG_INFINITY,
"nan" | "+nan" | "-nan" => f64::NAN,
_ => clean.parse().map_err(|e: core::num::ParseFloatError| {
ParseError::new(
facet_span,
DeserializeErrorKind::InvalidValue {
message: e.to_string().into(),
},
)
})?,
};
Ok(ScalarValue::F64(f))
}
ScalarKind::DateTime => {
Ok(ScalarValue::Str(output))
}
}
}
fn parse_dotted_key(&mut self) -> Vec<Cow<'de, str>> {
let mut parts = Vec::new();
loop {
let Some(event) = self.peek_raw() else {
break;
};
match event.kind() {
EventKind::SimpleKey => {
let key = self.decode_key(event);
self.next_raw(); parts.push(key);
}
EventKind::KeySep => {
self.next_raw();
}
_ => break,
}
}
parts
}
fn decode_key(&self, event: &Event) -> Cow<'de, str> {
let raw = self.raw_from_event(event);
let mut output: Cow<'de, str> = Cow::Borrowed("");
raw.decode_key(&mut output, &mut ());
output
}
fn end_event_for_segment(&self, segment: &PathSegment<'_>) -> ParseEvent<'de> {
match segment.kind {
SegmentKind::Table => self.event(ParseEventKind::StructEnd),
SegmentKind::Array => self.event(ParseEventKind::SequenceEnd),
SegmentKind::ArrayElement => self.event(ParseEventKind::StructEnd),
}
}
fn compute_navigation_to_table(
&self,
target_names: &[Cow<'de, str>],
) -> (Vec<ParseEvent<'de>>, Vec<PathSegment<'de>>) {
let mut events = Vec::new();
let mut current_idx = 0;
let mut target_idx = 0;
while current_idx < self.current_path.len() && target_idx < target_names.len() {
let seg = &self.current_path[current_idx];
let target_name = &target_names[target_idx];
if seg.name != *target_name {
break;
}
current_idx += 1;
if matches!(seg.kind, SegmentKind::Array) && current_idx < self.current_path.len() {
let next_seg = &self.current_path[current_idx];
if matches!(next_seg.kind, SegmentKind::ArrayElement) && next_seg.name == seg.name {
current_idx += 1;
}
}
target_idx += 1;
}
for segment in self.current_path[current_idx..].iter().rev() {
events.push(self.end_event_for_segment(segment));
}
let mut new_path: Vec<PathSegment<'de>> = self.current_path[..current_idx].to_vec();
for name in &target_names[target_idx..] {
events.push(self.event(ParseEventKind::FieldKey(FieldKey::new(
name.clone(),
FieldLocationHint::KeyValue,
))));
events.push(self.event(ParseEventKind::StructStart(ContainerKind::Object)));
new_path.push(PathSegment {
name: name.clone(),
kind: SegmentKind::Table,
});
}
(events, new_path)
}
fn compute_navigation_to_array_table(
&self,
target_names: &[Cow<'de, str>],
) -> (Vec<ParseEvent<'de>>, Vec<PathSegment<'de>>) {
let mut events = Vec::new();
let mut current_idx = 0;
let mut target_idx = 0;
while current_idx < self.current_path.len() && target_idx < target_names.len() {
let seg = &self.current_path[current_idx];
let target_name = &target_names[target_idx];
if seg.name != *target_name {
break;
}
if matches!(seg.kind, SegmentKind::Array) {
let more_targets_after = target_idx + 1 < target_names.len();
if more_targets_after {
current_idx += 1;
if current_idx < self.current_path.len() {
let next_seg = &self.current_path[current_idx];
if matches!(next_seg.kind, SegmentKind::ArrayElement)
&& next_seg.name == seg.name
{
current_idx += 1;
}
}
target_idx += 1;
} else {
break;
}
} else if matches!(seg.kind, SegmentKind::ArrayElement) {
break;
} else {
current_idx += 1;
target_idx += 1;
}
}
for segment in self.current_path[current_idx..].iter().rev() {
events.push(self.end_event_for_segment(segment));
}
let mut new_path: Vec<PathSegment<'de>> = self.current_path[..current_idx].to_vec();
if target_names.len() > target_idx {
for name in &target_names[target_idx..target_names.len() - 1] {
events.push(self.event(ParseEventKind::FieldKey(FieldKey::new(
name.clone(),
FieldLocationHint::KeyValue,
))));
events.push(self.event(ParseEventKind::StructStart(ContainerKind::Object)));
new_path.push(PathSegment {
name: name.clone(),
kind: SegmentKind::Table,
});
}
let array_name = target_names.last().unwrap();
events.push(self.event(ParseEventKind::FieldKey(FieldKey::new(
array_name.clone(),
FieldLocationHint::KeyValue,
))));
events.push(self.event(ParseEventKind::SequenceStart(ContainerKind::Array)));
events.push(self.event(ParseEventKind::StructStart(ContainerKind::Object)));
new_path.push(PathSegment {
name: array_name.clone(),
kind: SegmentKind::Array,
});
new_path.push(PathSegment {
name: array_name.clone(),
kind: SegmentKind::ArrayElement,
});
}
(events, new_path)
}
fn produce_event(&mut self) -> Result<Option<ParseEvent<'de>>, ParseError> {
if let Some(event) = self.pending_events.pop_front() {
return Ok(Some(event));
}
if !self.inline_stack.is_empty() {
return self.produce_inline_event();
}
if !self.root_started {
self.root_started = true;
return Ok(Some(
self.event(ParseEventKind::StructStart(ContainerKind::Object)),
));
}
let Some(event) = self.peek_raw() else {
if self.root_ended {
return Ok(None);
}
for segment in self.current_path.iter().rev() {
self.pending_events
.push_back(self.end_event_for_segment(segment));
}
self.current_path.clear();
self.pending_events
.push_back(self.event(ParseEventKind::StructEnd));
self.root_ended = true;
return Ok(self.pending_events.pop_front());
};
match event.kind() {
EventKind::StdTableOpen => {
self.next_raw(); let path = self.parse_dotted_key();
if let Some(close) = self.peek_raw()
&& matches!(close.kind(), EventKind::StdTableClose)
{
self.next_raw();
}
let (nav_events, new_path) = self.compute_navigation_to_table(&path);
for e in nav_events {
self.pending_events.push_back(e);
}
self.current_path = new_path;
if self.pending_events.is_empty() {
return self.produce_event();
}
Ok(self.pending_events.pop_front())
}
EventKind::ArrayTableOpen => {
self.next_raw(); let path = self.parse_dotted_key();
if let Some(close) = self.peek_raw()
&& matches!(close.kind(), EventKind::ArrayTableClose)
{
self.next_raw();
}
let (nav_events, new_path) = self.compute_navigation_to_array_table(&path);
for e in nav_events {
self.pending_events.push_back(e);
}
self.current_path = new_path;
Ok(self.pending_events.pop_front())
}
EventKind::SimpleKey => {
let key_parts = self.parse_dotted_key();
if let Some(sep) = self.peek_raw()
&& matches!(sep.kind(), EventKind::KeyValSep)
{
self.next_raw();
}
if key_parts.len() > 1 {
for name in &key_parts[..key_parts.len() - 1] {
self.pending_events
.push_back(self.event(ParseEventKind::FieldKey(FieldKey::new(
name.clone(),
FieldLocationHint::KeyValue,
))));
self.pending_events.push_back(
self.event(ParseEventKind::StructStart(ContainerKind::Object)),
);
}
let final_key = key_parts.last().unwrap();
self.pending_events
.push_back(self.event(ParseEventKind::FieldKey(FieldKey::new(
final_key.clone(),
FieldLocationHint::KeyValue,
))));
let inline_depth_before = self.inline_stack.len();
self.parse_value_into_pending()?;
let entered_inline_container = self.inline_stack.len() > inline_depth_before;
if entered_inline_container {
let num_deferred = key_parts.len() - 1;
if let Some((_, deferred_closes)) = self.inline_stack.last_mut() {
*deferred_closes += num_deferred;
}
} else {
for _ in 0..key_parts.len() - 1 {
self.pending_events
.push_back(self.event(ParseEventKind::StructEnd));
}
}
Ok(self.pending_events.pop_front())
} else {
let key = key_parts.into_iter().next().unwrap();
self.pending_events
.push_back(self.event(ParseEventKind::FieldKey(FieldKey::new(
key,
FieldLocationHint::KeyValue,
))));
self.parse_value_into_pending()?;
Ok(self.pending_events.pop_front())
}
}
EventKind::Error => {
let span_str = self.get_span_str(event);
let span = event.span();
Err(ParseError::new(
facet_reflect::Span::new(span.start(), span.end() - span.start()),
DeserializeErrorKind::InvalidValue {
message: span_str.to_string().into(),
},
))
}
_ => {
self.next_raw();
self.produce_event()
}
}
}
fn parse_value_into_pending(&mut self) -> Result<(), ParseError> {
let Some(event) = self.peek_raw() else {
return Err(ParseError::new(
self.eof_span(),
DeserializeErrorKind::UnexpectedEof { expected: "value" },
));
};
match event.kind() {
EventKind::Scalar => {
let scalar = self.decode_scalar(event)?;
self.update_span(event.span());
self.next_raw();
self.pending_events
.push_back(self.event(ParseEventKind::Scalar(scalar)));
}
EventKind::InlineTableOpen => {
self.next_raw();
self.pending_events
.push_back(self.event(ParseEventKind::StructStart(ContainerKind::Object)));
self.inline_stack.push((true, 0)); }
EventKind::ArrayOpen => {
self.next_raw();
self.pending_events
.push_back(self.event(ParseEventKind::SequenceStart(ContainerKind::Array)));
self.inline_stack.push((false, 0)); }
_ => {
let span = event.span();
return Err(ParseError::new(
facet_reflect::Span::new(span.start(), span.end() - span.start()),
DeserializeErrorKind::UnexpectedToken {
expected: "value",
got: "unexpected token".into(),
},
));
}
}
Ok(())
}
fn produce_inline_event(&mut self) -> Result<Option<ParseEvent<'de>>, ParseError> {
if let Some(event) = self.pending_events.pop_front() {
return Ok(Some(event));
}
let (is_inline_table, _deferred_closes) = *self.inline_stack.last().unwrap();
let Some(event) = self.peek_raw() else {
return Err(ParseError::new(
self.eof_span(),
DeserializeErrorKind::UnexpectedEof {
expected: if is_inline_table { "}" } else { "]" },
},
));
};
match event.kind() {
EventKind::InlineTableClose if is_inline_table => {
self.next_raw();
let (_, deferred_closes) = self.inline_stack.pop().unwrap();
self.pending_events
.push_back(self.event(ParseEventKind::StructEnd));
for _ in 0..deferred_closes {
self.pending_events
.push_back(self.event(ParseEventKind::StructEnd));
}
Ok(self.pending_events.pop_front())
}
EventKind::ArrayClose if !is_inline_table => {
self.next_raw();
let (_, deferred_closes) = self.inline_stack.pop().unwrap();
self.pending_events
.push_back(self.event(ParseEventKind::SequenceEnd));
for _ in 0..deferred_closes {
self.pending_events
.push_back(self.event(ParseEventKind::StructEnd));
}
Ok(self.pending_events.pop_front())
}
EventKind::ValueSep => {
self.next_raw();
self.produce_inline_event()
}
EventKind::SimpleKey if is_inline_table => {
let key_parts = self.parse_dotted_key();
if let Some(sep) = self.peek_raw()
&& matches!(sep.kind(), EventKind::KeyValSep)
{
self.next_raw();
}
if key_parts.len() > 1 {
for name in &key_parts[..key_parts.len() - 1] {
self.pending_events
.push_back(self.event(ParseEventKind::FieldKey(FieldKey::new(
name.clone(),
FieldLocationHint::KeyValue,
))));
self.pending_events.push_back(
self.event(ParseEventKind::StructStart(ContainerKind::Object)),
);
}
let final_key = key_parts.last().unwrap();
self.pending_events
.push_back(self.event(ParseEventKind::FieldKey(FieldKey::new(
final_key.clone(),
FieldLocationHint::KeyValue,
))));
let inline_depth_before = self.inline_stack.len();
self.parse_value_into_pending()?;
let entered_inline_container = self.inline_stack.len() > inline_depth_before;
if entered_inline_container {
let num_deferred = key_parts.len() - 1;
if let Some((_, deferred_closes)) = self.inline_stack.last_mut() {
*deferred_closes += num_deferred;
}
} else {
for _ in 0..key_parts.len() - 1 {
self.pending_events
.push_back(self.event(ParseEventKind::StructEnd));
}
}
Ok(self.pending_events.pop_front())
} else {
let key = key_parts.into_iter().next().unwrap();
self.pending_events
.push_back(self.event(ParseEventKind::FieldKey(FieldKey::new(
key,
FieldLocationHint::KeyValue,
))));
self.parse_value_into_pending()?;
Ok(self.pending_events.pop_front())
}
}
EventKind::Scalar if !is_inline_table => {
let scalar = self.decode_scalar(event)?;
self.update_span(event.span());
self.next_raw();
Ok(Some(self.event(ParseEventKind::Scalar(scalar))))
}
EventKind::InlineTableOpen if !is_inline_table => {
self.next_raw();
self.inline_stack.push((true, 0));
Ok(Some(
self.event(ParseEventKind::StructStart(ContainerKind::Object)),
))
}
EventKind::ArrayOpen if !is_inline_table => {
self.next_raw();
self.inline_stack.push((false, 0));
Ok(Some(self.event(ParseEventKind::SequenceStart(
ContainerKind::Array,
))))
}
_ => {
self.next_raw();
self.produce_inline_event()
}
}
}
fn skip_current_value(&mut self) -> Result<(), ParseError> {
let Some(event) = self.next_event()? else {
return Ok(());
};
match event.kind {
ParseEventKind::Scalar(_) => {
Ok(())
}
ParseEventKind::StructStart(_) => {
let mut depth = 1;
while depth > 0 {
let Some(event) = self.next_event()? else {
return Err(ParseError::new(
self.eof_span(),
DeserializeErrorKind::UnexpectedEof {
expected: "struct end",
},
));
};
match event.kind {
ParseEventKind::StructStart(_) => depth += 1,
ParseEventKind::StructEnd => depth -= 1,
_ => {}
}
}
Ok(())
}
ParseEventKind::SequenceStart(_) => {
let mut depth = 1;
while depth > 0 {
let Some(event) = self.next_event()? else {
return Err(ParseError::new(
self.eof_span(),
DeserializeErrorKind::UnexpectedEof {
expected: "sequence end",
},
));
};
match event.kind {
ParseEventKind::SequenceStart(_) => depth += 1,
ParseEventKind::SequenceEnd => depth -= 1,
_ => {}
}
}
Ok(())
}
_ => {
Ok(())
}
}
}
}
impl<'de> FormatParser<'de> for TomlParser<'de> {
fn next_event(&mut self) -> Result<Option<ParseEvent<'de>>, ParseError> {
if let Some(event) = self.event_peek.take() {
return Ok(Some(event));
}
self.produce_event()
}
fn peek_event(&mut self) -> Result<Option<ParseEvent<'de>>, ParseError> {
if let Some(event) = self.event_peek.clone() {
return Ok(Some(event));
}
let event = self.produce_event()?;
if let Some(ref e) = event {
self.event_peek = Some(e.clone());
}
Ok(event)
}
fn skip_value(&mut self) -> Result<(), ParseError> {
debug_assert!(
self.event_peek.is_none(),
"skip_value called while an event is buffered"
);
self.skip_current_value()
}
fn save(&mut self) -> SavePoint {
self.save_counter += 1;
let state = SavedState {
pos: self.pos,
current_path: self.current_path.clone(),
pending_events: self.pending_events.clone(),
event_peek: self.event_peek.clone(),
root_started: self.root_started,
root_ended: self.root_ended,
inline_stack: self.inline_stack.clone(),
};
self.saved_states.push((self.save_counter, state));
SavePoint(self.save_counter)
}
fn restore(&mut self, save_point: SavePoint) {
if let Some(idx) = self
.saved_states
.iter()
.position(|(id, _)| *id == save_point.0)
{
let (_, state) = self.saved_states.remove(idx);
self.pos = state.pos;
self.current_path = state.current_path;
self.pending_events = state.pending_events;
self.event_peek = state.event_peek;
self.root_started = state.root_started;
self.root_ended = state.root_ended;
self.inline_stack = state.inline_stack;
}
}
fn capture_raw(&mut self) -> Result<Option<&'de str>, ParseError> {
self.skip_value()?;
Ok(None)
}
fn current_span(&self) -> Option<facet_reflect::Span> {
Some(self.last_span)
}
}
impl<'de> TomlParser<'de> {
#[inline]
fn event(&self, kind: ParseEventKind<'de>) -> ParseEvent<'de> {
ParseEvent::new(kind, self.last_span)
}
#[inline]
fn update_span(&mut self, span: toml_parser::Span) {
self.last_span = facet_reflect::Span::new(span.start(), span.end() - span.start());
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::from_str;
fn collect_events<'de>(parser: &mut TomlParser<'de>) -> Vec<ParseEvent<'de>> {
let mut events = Vec::new();
while let Ok(Some(event)) = parser.next_event() {
events.push(event);
}
events
}
fn format_events(events: &[ParseEvent<'_>]) -> String {
events
.iter()
.map(|e| format!("{:?}", e))
.collect::<Vec<_>>()
.join("\n")
}
#[test]
fn test_simple_key_value() {
let input = r#"
name = "test"
value = 42
"#;
let mut parser = TomlParser::new(input).unwrap();
assert!(matches!(
parser.next_event().unwrap(),
Some(ParseEvent {
kind: ParseEventKind::StructStart(ContainerKind::Object),
..
})
));
assert!(matches!(
parser.next_event().unwrap(),
Some(ParseEvent { kind: ParseEventKind::FieldKey(key), .. }) if key.name().map(|c| c.as_ref()) == Some("name")
));
assert!(matches!(
parser.next_event().unwrap(),
Some(ParseEvent { kind: ParseEventKind::Scalar(ScalarValue::Str(s)), .. }) if s == "test"
));
assert!(matches!(
parser.next_event().unwrap(),
Some(ParseEvent { kind: ParseEventKind::FieldKey(key), .. }) if key.name().map(|c| c.as_ref()) == Some("value")
));
assert!(matches!(
parser.next_event().unwrap(),
Some(ParseEvent {
kind: ParseEventKind::Scalar(ScalarValue::I64(42)),
..
})
));
assert!(matches!(
parser.next_event().unwrap(),
Some(ParseEvent {
kind: ParseEventKind::StructEnd,
..
})
));
assert!(parser.next_event().unwrap().is_none());
}
#[test]
fn test_table_header() {
let input = r#"
[server]
host = "localhost"
port = 8080
"#;
let mut parser = TomlParser::new(input).unwrap();
let events = collect_events(&mut parser);
assert!(matches!(
&events[0],
ParseEvent {
kind: ParseEventKind::StructStart(_),
..
}
));
assert!(
matches!(&events[1], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("server"))
);
assert!(matches!(
&events[2],
ParseEvent {
kind: ParseEventKind::StructStart(_),
..
}
));
assert!(
matches!(&events[3], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("host"))
);
assert!(
matches!(&events[4], ParseEvent { kind: ParseEventKind::Scalar(ScalarValue::Str(s)), .. } if s == "localhost")
);
assert!(
matches!(&events[5], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("port"))
);
assert!(matches!(
&events[6],
ParseEvent {
kind: ParseEventKind::Scalar(ScalarValue::I64(8080)),
..
}
));
assert!(matches!(
&events[7],
ParseEvent {
kind: ParseEventKind::StructEnd,
..
}
)); assert!(matches!(
&events[8],
ParseEvent {
kind: ParseEventKind::StructEnd,
..
}
)); }
#[test]
fn test_array_table() {
let input = r#"
[[servers]]
name = "alpha"
[[servers]]
name = "beta"
"#;
let mut parser = TomlParser::new(input).unwrap();
let events = collect_events(&mut parser);
let event_str = format_events(&events);
eprintln!("Events:\n{}", event_str);
assert!(matches!(
&events[0],
ParseEvent {
kind: ParseEventKind::StructStart(_),
..
}
)); assert!(
matches!(&events[1], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("servers"))
);
assert!(matches!(
&events[2],
ParseEvent {
kind: ParseEventKind::SequenceStart(_),
..
}
));
assert!(matches!(
&events[3],
ParseEvent {
kind: ParseEventKind::StructStart(_),
..
}
)); assert!(
matches!(&events[4], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("name"))
);
assert!(
matches!(&events[5], ParseEvent { kind: ParseEventKind::Scalar(ScalarValue::Str(s)), .. } if s == "alpha")
);
assert!(matches!(
&events[6],
ParseEvent {
kind: ParseEventKind::StructEnd,
..
}
)); assert!(matches!(
&events[7],
ParseEvent {
kind: ParseEventKind::SequenceEnd,
..
}
));
assert!(
matches!(&events[8], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("servers"))
);
assert!(matches!(
&events[9],
ParseEvent {
kind: ParseEventKind::SequenceStart(_),
..
}
));
assert!(matches!(
&events[10],
ParseEvent {
kind: ParseEventKind::StructStart(_),
..
}
)); assert!(
matches!(&events[11], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("name"))
);
assert!(
matches!(&events[12], ParseEvent { kind: ParseEventKind::Scalar(ScalarValue::Str(s)), .. } if s == "beta")
);
}
#[test]
fn test_interleaved_array_table() {
let input = r#"
[[servers]]
name = "alpha"
[database]
host = "localhost"
[[servers]]
name = "beta"
"#;
let mut parser = TomlParser::new(input).unwrap();
let events = collect_events(&mut parser);
let event_str = format_events(&events);
eprintln!("Interleaved events:\n{}", event_str);
let mut saw_servers_first = false;
let mut saw_database = false;
let mut saw_servers_second = false;
let mut servers_count = 0;
for event in events.iter() {
if let ParseEvent {
kind: ParseEventKind::FieldKey(k),
..
} = event
{
if k.name().map(|c| c.as_ref()) == Some("servers") {
servers_count += 1;
if !saw_database {
saw_servers_first = true;
} else {
saw_servers_second = true;
}
} else if k.name().map(|c| c.as_ref()) == Some("database") {
saw_database = true;
}
}
}
assert!(saw_servers_first, "Should see servers before database");
assert!(saw_database, "Should see database");
assert!(
saw_servers_second,
"Should see servers reopened after database"
);
assert_eq!(servers_count, 2, "Should have two FieldKey(servers) events");
}
#[test]
fn test_table_reopening() {
let input = r#"
[foo.bar]
x = 1
[foo.baz]
z = 3
[foo.bar]
y = 2
"#;
let mut parser = TomlParser::new(input).unwrap();
let events = collect_events(&mut parser);
let event_str = format_events(&events);
eprintln!("Table reopen events:\n{}", event_str);
let bar_count = events
.iter()
.filter(|e| matches!(e, ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("bar")))
.count();
assert_eq!(bar_count, 2, "Should see bar twice (reopened)");
}
#[test]
fn test_dotted_key() {
let input = r#"
foo.bar.baz = 1
"#;
let mut parser = TomlParser::new(input).unwrap();
let events = collect_events(&mut parser);
let event_str = format_events(&events);
eprintln!("Dotted key events:\n{}", event_str);
assert!(matches!(
&events[0],
ParseEvent {
kind: ParseEventKind::StructStart(_),
..
}
)); assert!(
matches!(&events[1], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("foo"))
);
assert!(matches!(
&events[2],
ParseEvent {
kind: ParseEventKind::StructStart(_),
..
}
));
assert!(
matches!(&events[3], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("bar"))
);
assert!(matches!(
&events[4],
ParseEvent {
kind: ParseEventKind::StructStart(_),
..
}
));
assert!(
matches!(&events[5], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("baz"))
);
assert!(matches!(
&events[6],
ParseEvent {
kind: ParseEventKind::Scalar(ScalarValue::I64(1)),
..
}
));
assert!(matches!(
&events[7],
ParseEvent {
kind: ParseEventKind::StructEnd,
..
}
));
assert!(matches!(
&events[8],
ParseEvent {
kind: ParseEventKind::StructEnd,
..
}
));
assert!(matches!(
&events[9],
ParseEvent {
kind: ParseEventKind::StructEnd,
..
}
));
}
#[test]
fn test_inline_table() {
let input = r#"
server = { host = "localhost", port = 8080 }
"#;
let mut parser = TomlParser::new(input).unwrap();
let events = collect_events(&mut parser);
let event_str = format_events(&events);
eprintln!("Inline table events:\n{}", event_str);
assert!(matches!(
&events[0],
ParseEvent {
kind: ParseEventKind::StructStart(_),
..
}
)); assert!(
matches!(&events[1], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("server"))
);
assert!(matches!(
&events[2],
ParseEvent {
kind: ParseEventKind::StructStart(_),
..
}
)); assert!(
matches!(&events[3], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("host"))
);
assert!(
matches!(&events[4], ParseEvent { kind: ParseEventKind::Scalar(ScalarValue::Str(s)), .. } if s == "localhost")
);
assert!(
matches!(&events[5], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("port"))
);
assert!(matches!(
&events[6],
ParseEvent {
kind: ParseEventKind::Scalar(ScalarValue::I64(8080)),
..
}
));
assert!(matches!(
&events[7],
ParseEvent {
kind: ParseEventKind::StructEnd,
..
}
)); assert!(matches!(
&events[8],
ParseEvent {
kind: ParseEventKind::StructEnd,
..
}
)); }
#[test]
fn test_inline_array() {
let input = r#"
numbers = [1, 2, 3]
"#;
let mut parser = TomlParser::new(input).unwrap();
let events = collect_events(&mut parser);
let event_str = format_events(&events);
eprintln!("Inline array events:\n{}", event_str);
assert!(matches!(
&events[0],
ParseEvent {
kind: ParseEventKind::StructStart(_),
..
}
)); assert!(
matches!(&events[1], ParseEvent { kind: ParseEventKind::FieldKey(k), .. } if k.name().map(|c| c.as_ref()) == Some("numbers"))
);
assert!(matches!(
&events[2],
ParseEvent {
kind: ParseEventKind::SequenceStart(_),
..
}
));
assert!(matches!(
&events[3],
ParseEvent {
kind: ParseEventKind::Scalar(ScalarValue::I64(1)),
..
}
));
assert!(matches!(
&events[4],
ParseEvent {
kind: ParseEventKind::Scalar(ScalarValue::I64(2)),
..
}
));
assert!(matches!(
&events[5],
ParseEvent {
kind: ParseEventKind::Scalar(ScalarValue::I64(3)),
..
}
));
assert!(matches!(
&events[6],
ParseEvent {
kind: ParseEventKind::SequenceEnd,
..
}
));
assert!(matches!(
&events[7],
ParseEvent {
kind: ParseEventKind::StructEnd,
..
}
)); }
#[test]
fn test_deserialize_simple_struct() {
#[derive(Debug, PartialEq, facet::Facet)]
struct Config {
name: String,
port: i64,
enabled: bool,
}
let input = r#"
name = "myapp"
port = 8080
enabled = true
"#;
let config: Config = from_str(input).unwrap();
assert_eq!(config.name, "myapp");
assert_eq!(config.port, 8080);
assert!(config.enabled);
}
#[test]
fn test_deserialize_nested_table() {
#[derive(Debug, PartialEq, facet::Facet)]
struct Config {
server: Server,
}
#[derive(Debug, PartialEq, facet::Facet)]
struct Server {
host: String,
port: i64,
}
let input = r#"
[server]
host = "localhost"
port = 3000
"#;
let config: Config = from_str(input).unwrap();
assert_eq!(config.server.host, "localhost");
assert_eq!(config.server.port, 3000);
}
#[test]
fn test_deserialize_array_table() {
#[derive(Debug, PartialEq, facet::Facet)]
struct Config {
servers: Vec<Server>,
}
#[derive(Debug, PartialEq, facet::Facet)]
struct Server {
name: String,
}
let input = r#"
[[servers]]
name = "alpha"
[[servers]]
name = "beta"
[[servers]]
name = "gamma"
"#;
let config: Config = from_str(input).unwrap();
assert_eq!(config.servers.len(), 3);
assert_eq!(config.servers[0].name, "alpha");
assert_eq!(config.servers[1].name, "beta");
assert_eq!(config.servers[2].name, "gamma");
}
#[test]
fn test_deserialize_interleaved_array_table() {
#[derive(Debug, PartialEq, facet::Facet)]
struct Config {
servers: Vec<Server>,
database: Database,
}
#[derive(Debug, PartialEq, facet::Facet)]
struct Server {
name: String,
}
#[derive(Debug, PartialEq, facet::Facet)]
struct Database {
host: String,
}
let input = r#"
[[servers]]
name = "alpha"
[database]
host = "localhost"
[[servers]]
name = "beta"
"#;
let config: Config = from_str(input).unwrap();
assert_eq!(config.servers.len(), 2);
assert_eq!(config.servers[0].name, "alpha");
assert_eq!(config.servers[1].name, "beta");
assert_eq!(config.database.host, "localhost");
}
#[test]
fn test_issue_1399_array_of_tables_only_parses_last_entry() {
#[derive(Debug, PartialEq, facet::Facet)]
struct Lockfile {
version: Option<u32>,
package: Option<Vec<Package>>,
}
#[derive(Debug, PartialEq, facet::Facet)]
struct Package {
name: String,
version: String,
}
let input = r#"
version = 4
[[package]]
name = "myapp"
version = "0.1.0"
[[package]]
name = "aho-corasick"
version = "1.1.2"
"#;
let lockfile: Lockfile = from_str(input).unwrap();
assert_eq!(lockfile.version, Some(4));
let packages = lockfile.package.expect("package field should be Some");
assert_eq!(
packages.len(),
2,
"Should parse both package entries, not just the last one"
);
assert_eq!(packages[0].name, "myapp");
assert_eq!(packages[0].version, "0.1.0");
assert_eq!(packages[1].name, "aho-corasick");
assert_eq!(packages[1].version, "1.1.2");
}
#[test]
fn test_deserialize_inline_table() {
#[derive(Debug, PartialEq, facet::Facet)]
struct Config {
point: Point,
}
#[derive(Debug, PartialEq, facet::Facet)]
struct Point {
x: i64,
y: i64,
}
let input = r#"point = { x = 10, y = 20 }"#;
let config: Config = from_str(input).unwrap();
assert_eq!(config.point.x, 10);
assert_eq!(config.point.y, 20);
}
#[test]
fn test_deserialize_inline_array() {
#[derive(Debug, PartialEq, facet::Facet)]
struct Config {
values: Vec<i64>,
}
let input = r#"values = [1, 2, 3, 4, 5]"#;
let config: Config = from_str(input).unwrap();
assert_eq!(config.values, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_deserialize_dotted_key() {
#[derive(Debug, PartialEq, facet::Facet)]
struct Config {
foo: Foo,
}
#[derive(Debug, PartialEq, facet::Facet)]
struct Foo {
bar: Bar,
}
#[derive(Debug, PartialEq, facet::Facet)]
struct Bar {
baz: i64,
}
let input = r#"foo.bar.baz = 42"#;
let config: Config = from_str(input).unwrap();
assert_eq!(config.foo.bar.baz, 42);
}
#[test]
fn test_deserialize_table_reopening() {
#[derive(Debug, PartialEq, facet::Facet)]
struct Config {
foo: Foo,
}
#[derive(Debug, PartialEq, facet::Facet)]
struct Foo {
bar: Bar,
baz: Baz,
}
#[derive(Debug, PartialEq, facet::Facet)]
struct Bar {
x: i64,
y: i64,
}
#[derive(Debug, PartialEq, facet::Facet)]
struct Baz {
z: i64,
}
let input = r#"
[foo.bar]
x = 1
[foo.baz]
z = 3
[foo.bar]
y = 2
"#;
let config: Config = from_str(input).unwrap();
assert_eq!(config.foo.bar.x, 1);
assert_eq!(config.foo.bar.y, 2);
assert_eq!(config.foo.baz.z, 3);
}
}