feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+442
@@ -0,0 +1,442 @@
|
||||
use std::iter::FromIterator;
|
||||
use std::mem;
|
||||
|
||||
use crate::repr::Decor;
|
||||
use crate::value::{DEFAULT_LEADING_VALUE_DECOR, DEFAULT_VALUE_DECOR};
|
||||
use crate::{Item, RawString, Value};
|
||||
|
||||
/// A TOML [`Value`] that contains a sequence of [`Value`]s
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Array {
|
||||
// `trailing` represents whitespaces, newlines
|
||||
// and comments in an empty array or after the trailing comma
|
||||
trailing: RawString,
|
||||
trailing_comma: bool,
|
||||
// prefix before `[` and suffix after `]`
|
||||
decor: Decor,
|
||||
pub(crate) span: Option<std::ops::Range<usize>>,
|
||||
// always Vec<Item::Value>
|
||||
pub(crate) values: Vec<Item>,
|
||||
}
|
||||
|
||||
/// An owned iterator type over [`Array`]'s [`Value`]s
|
||||
pub type ArrayIntoIter = Box<dyn Iterator<Item = Value>>;
|
||||
/// An iterator type over [`Array`]'s [`Value`]s
|
||||
pub type ArrayIter<'a> = Box<dyn Iterator<Item = &'a Value> + 'a>;
|
||||
/// An iterator type over [`Array`]'s [`Value`]s
|
||||
pub type ArrayIterMut<'a> = Box<dyn Iterator<Item = &'a mut Value> + 'a>;
|
||||
|
||||
/// Constructors
|
||||
///
|
||||
/// See also `FromIterator`
|
||||
impl Array {
|
||||
/// Create an empty `Array`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut arr = toml_edit::Array::new();
|
||||
/// ```
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub(crate) fn with_vec(values: Vec<Item>) -> Self {
|
||||
Self {
|
||||
values,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Formatting
|
||||
impl Array {
|
||||
/// Auto formats the array.
|
||||
pub fn fmt(&mut self) {
|
||||
decorate_array(self);
|
||||
}
|
||||
|
||||
/// Set whether the array will use a trailing comma
|
||||
pub fn set_trailing_comma(&mut self, yes: bool) {
|
||||
self.trailing_comma = yes;
|
||||
}
|
||||
|
||||
/// Whether the array will use a trailing comma
|
||||
pub fn trailing_comma(&self) -> bool {
|
||||
self.trailing_comma
|
||||
}
|
||||
|
||||
/// Set whitespace after last element
|
||||
pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
|
||||
self.trailing = trailing.into();
|
||||
}
|
||||
|
||||
/// Whitespace after last element
|
||||
pub fn trailing(&self) -> &RawString {
|
||||
&self.trailing
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace
|
||||
pub fn decor_mut(&mut self) -> &mut Decor {
|
||||
&mut self.decor
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace
|
||||
pub fn decor(&self) -> &Decor {
|
||||
&self.decor
|
||||
}
|
||||
|
||||
/// The location within the original document
|
||||
///
|
||||
/// This generally requires a [`Document`][crate::Document].
|
||||
pub fn span(&self) -> Option<std::ops::Range<usize>> {
|
||||
self.span.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn despan(&mut self, input: &str) {
|
||||
self.span = None;
|
||||
self.decor.despan(input);
|
||||
self.trailing.despan(input);
|
||||
for value in &mut self.values {
|
||||
value.despan(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Array {
|
||||
/// Returns an iterator over all values.
|
||||
pub fn iter(&self) -> ArrayIter<'_> {
|
||||
Box::new(self.values.iter().filter_map(Item::as_value))
|
||||
}
|
||||
|
||||
/// Returns an iterator over all values.
|
||||
pub fn iter_mut(&mut self) -> ArrayIterMut<'_> {
|
||||
Box::new(self.values.iter_mut().filter_map(Item::as_value_mut))
|
||||
}
|
||||
|
||||
/// Returns the length of the underlying Vec.
|
||||
///
|
||||
/// In some rare cases, placeholder elements will exist. For a more accurate count, call
|
||||
/// `a.iter().count()`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut arr = toml_edit::Array::new();
|
||||
/// arr.push(1);
|
||||
/// arr.push("foo");
|
||||
/// assert_eq!(arr.len(), 2);
|
||||
/// ```
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
/// Return true if `self.len() == 0`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut arr = toml_edit::Array::new();
|
||||
/// assert!(arr.is_empty());
|
||||
///
|
||||
/// arr.push(1);
|
||||
/// arr.push("foo");
|
||||
/// assert!(! arr.is_empty());
|
||||
/// ```
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Clears the array, removing all values. Keeps the allocated memory for reuse.
|
||||
pub fn clear(&mut self) {
|
||||
self.values.clear();
|
||||
}
|
||||
|
||||
/// Returns a reference to the value at the given index, or `None` if the index is out of
|
||||
/// bounds.
|
||||
pub fn get(&self, index: usize) -> Option<&Value> {
|
||||
self.values.get(index).and_then(Item::as_value)
|
||||
}
|
||||
|
||||
/// Returns a reference to the value at the given index, or `None` if the index is out of
|
||||
/// bounds.
|
||||
pub fn get_mut(&mut self, index: usize) -> Option<&mut Value> {
|
||||
self.values.get_mut(index).and_then(Item::as_value_mut)
|
||||
}
|
||||
|
||||
/// Appends a new value to the end of the array, applying default formatting to it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut arr = toml_edit::Array::new();
|
||||
/// arr.push(1);
|
||||
/// arr.push("foo");
|
||||
/// ```
|
||||
pub fn push<V: Into<Value>>(&mut self, v: V) {
|
||||
self.values.push(Item::Value(v.into()));
|
||||
}
|
||||
|
||||
/// Appends a new, already formatted value to the end of the array.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "parse")] {
|
||||
/// let formatted_value = "'literal'".parse::<toml_edit::Value>().unwrap();
|
||||
/// let mut arr = toml_edit::Array::new();
|
||||
/// arr.push_formatted(formatted_value);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn push_formatted(&mut self, v: Value) {
|
||||
self.values.push(Item::Value(v));
|
||||
}
|
||||
|
||||
/// Inserts an element at the given position within the array, applying default formatting to
|
||||
/// it and shifting all values after it to the right.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `index > len`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut arr = toml_edit::Array::new();
|
||||
/// arr.push(1);
|
||||
/// arr.push("foo");
|
||||
///
|
||||
/// arr.insert(0, "start");
|
||||
/// ```
|
||||
pub fn insert<V: Into<Value>>(&mut self, index: usize, v: V) {
|
||||
self.values.insert(index, Item::Value(v.into()));
|
||||
}
|
||||
|
||||
/// Inserts an already formatted value at the given position within the array, shifting all
|
||||
/// values after it to the right.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `index > len`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "parse")] {
|
||||
/// let mut arr = toml_edit::Array::new();
|
||||
/// arr.push(1);
|
||||
/// arr.push("foo");
|
||||
///
|
||||
/// let formatted_value = "'start'".parse::<toml_edit::Value>().unwrap();
|
||||
/// arr.insert_formatted(0, formatted_value);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn insert_formatted(&mut self, index: usize, v: Value) {
|
||||
self.values.insert(index, Item::Value(v));
|
||||
}
|
||||
|
||||
/// Replaces the element at the given position within the array, preserving existing formatting.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `index >= len`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut arr = toml_edit::Array::new();
|
||||
/// arr.push(1);
|
||||
/// arr.push("foo");
|
||||
///
|
||||
/// arr.replace(0, "start");
|
||||
/// ```
|
||||
pub fn replace<V: Into<Value>>(&mut self, index: usize, v: V) -> Value {
|
||||
// Read the existing value's decor and preserve it.
|
||||
let existing_decor = self
|
||||
.get(index)
|
||||
.unwrap_or_else(|| panic!("index {} out of bounds (len = {})", index, self.len()))
|
||||
.decor();
|
||||
let mut value = v.into();
|
||||
*value.decor_mut() = existing_decor.clone();
|
||||
self.replace_formatted(index, value)
|
||||
}
|
||||
|
||||
/// Replaces the element at the given position within the array with an already formatted value.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `index >= len`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "parse")] {
|
||||
/// let mut arr = toml_edit::Array::new();
|
||||
/// arr.push(1);
|
||||
/// arr.push("foo");
|
||||
///
|
||||
/// let formatted_value = "'start'".parse::<toml_edit::Value>().unwrap();
|
||||
/// arr.replace_formatted(0, formatted_value);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn replace_formatted(&mut self, index: usize, v: Value) -> Value {
|
||||
match mem::replace(&mut self.values[index], Item::Value(v)) {
|
||||
Item::Value(old_value) => old_value,
|
||||
x => panic!("non-value item {x:?} in an array"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the value at the given index.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut arr = toml_edit::Array::new();
|
||||
/// arr.push(1);
|
||||
/// arr.push("foo");
|
||||
///
|
||||
/// arr.remove(0);
|
||||
/// assert_eq!(arr.len(), 1);
|
||||
/// ```
|
||||
pub fn remove(&mut self, index: usize) -> Value {
|
||||
let removed = self.values.remove(index);
|
||||
match removed {
|
||||
Item::Value(v) => v,
|
||||
x => panic!("non-value item {x:?} in an array"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retains only the values specified by the `keep` predicate.
|
||||
///
|
||||
/// In other words, remove all values for which `keep(&value)` returns `false`.
|
||||
///
|
||||
/// This method operates in place, visiting each element exactly once in the
|
||||
/// original order, and preserves the order of the retained elements.
|
||||
pub fn retain<F>(&mut self, mut keep: F)
|
||||
where
|
||||
F: FnMut(&Value) -> bool,
|
||||
{
|
||||
self.values
|
||||
.retain(|item| item.as_value().map(&mut keep).unwrap_or(false));
|
||||
}
|
||||
|
||||
/// Sorts the slice with a comparator function.
|
||||
///
|
||||
/// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) worst-case.
|
||||
///
|
||||
/// The comparator function must define a total ordering for the elements in the slice. If
|
||||
/// the ordering is not total, the order of the elements is unspecified. An order is a
|
||||
/// total order if it is (for all `a`, `b` and `c`):
|
||||
///
|
||||
/// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and
|
||||
/// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
|
||||
///
|
||||
/// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use
|
||||
/// `partial_cmp` as our sort function when we know the slice doesn't contain a `NaN`.
|
||||
#[inline]
|
||||
pub fn sort_by<F>(&mut self, mut compare: F)
|
||||
where
|
||||
F: FnMut(&Value, &Value) -> std::cmp::Ordering,
|
||||
{
|
||||
self.values.sort_by(move |lhs, rhs| {
|
||||
let lhs = lhs.as_value();
|
||||
let rhs = rhs.as_value();
|
||||
match (lhs, rhs) {
|
||||
(None, None) => std::cmp::Ordering::Equal,
|
||||
(Some(_), None) => std::cmp::Ordering::Greater,
|
||||
(None, Some(_)) => std::cmp::Ordering::Less,
|
||||
(Some(lhs), Some(rhs)) => compare(lhs, rhs),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Sorts the array with a key extraction function.
|
||||
///
|
||||
/// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* \* log(*n*))
|
||||
/// worst-case, where the key function is *O*(*m*).
|
||||
#[inline]
|
||||
pub fn sort_by_key<K, F>(&mut self, mut f: F)
|
||||
where
|
||||
F: FnMut(&Value) -> K,
|
||||
K: Ord,
|
||||
{
|
||||
#[allow(clippy::manual_map)] // needed for lifetimes
|
||||
self.values.sort_by_key(move |item| {
|
||||
if let Some(value) = item.as_value() {
|
||||
Some(f(value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
impl std::fmt::Display for Array {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
crate::encode::encode_array(self, f, None, ("", ""))
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Into<Value>> Extend<V> for Array {
|
||||
fn extend<T: IntoIterator<Item = V>>(&mut self, iter: T) {
|
||||
for value in iter {
|
||||
self.push_formatted(value.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Into<Value>> FromIterator<V> for Array {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = V>,
|
||||
{
|
||||
let v = iter.into_iter().map(|a| Item::Value(a.into()));
|
||||
Self {
|
||||
values: v.collect(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Array {
|
||||
type Item = Value;
|
||||
type IntoIter = ArrayIntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Box::new(
|
||||
self.values
|
||||
.into_iter()
|
||||
.filter(|v| v.is_value())
|
||||
.map(|v| v.into_value().unwrap()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> IntoIterator for &'s Array {
|
||||
type Item = &'s Value;
|
||||
type IntoIter = ArrayIter<'s>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
fn decorate_array(array: &mut Array) {
|
||||
for (i, value) in array
|
||||
.values
|
||||
.iter_mut()
|
||||
.filter_map(Item::as_value_mut)
|
||||
.enumerate()
|
||||
{
|
||||
// [value1, value2, value3]
|
||||
if i == 0 {
|
||||
value.decorate(DEFAULT_LEADING_VALUE_DECOR.0, DEFAULT_LEADING_VALUE_DECOR.1);
|
||||
} else {
|
||||
value.decorate(DEFAULT_VALUE_DECOR.0, DEFAULT_VALUE_DECOR.1);
|
||||
}
|
||||
}
|
||||
// Since everything is now on the same line, remove trailing commas and whitespace.
|
||||
array.set_trailing_comma(false);
|
||||
array.set_trailing("");
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use crate::{Array, Item, Table};
|
||||
|
||||
/// A top-level sequence of [`Table`]s, each under their own header
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ArrayOfTables {
|
||||
// Always Vec<Item::Table>, just `Item` to make `Index` work
|
||||
pub(crate) span: Option<std::ops::Range<usize>>,
|
||||
pub(crate) values: Vec<Item>,
|
||||
}
|
||||
|
||||
/// Constructors
|
||||
///
|
||||
/// See also `FromIterator`
|
||||
impl ArrayOfTables {
|
||||
/// Creates an empty array of tables.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Formatting
|
||||
impl ArrayOfTables {
|
||||
/// Convert to an inline array
|
||||
pub fn into_array(mut self) -> Array {
|
||||
for value in self.values.iter_mut() {
|
||||
value.make_value();
|
||||
}
|
||||
let mut a = Array::with_vec(self.values);
|
||||
a.fmt();
|
||||
a
|
||||
}
|
||||
|
||||
/// The location within the original document
|
||||
///
|
||||
/// This generally requires a [`Document`][crate::Document].
|
||||
pub fn span(&self) -> Option<std::ops::Range<usize>> {
|
||||
self.span.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn despan(&mut self, input: &str) {
|
||||
self.span = None;
|
||||
for value in &mut self.values {
|
||||
value.despan(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArrayOfTables {
|
||||
/// Returns an iterator over tables.
|
||||
pub fn iter(&self) -> ArrayOfTablesIter<'_> {
|
||||
Box::new(self.values.iter().filter_map(Item::as_table))
|
||||
}
|
||||
|
||||
/// Returns an iterator over tables.
|
||||
pub fn iter_mut(&mut self) -> ArrayOfTablesIterMut<'_> {
|
||||
Box::new(self.values.iter_mut().filter_map(Item::as_table_mut))
|
||||
}
|
||||
|
||||
/// Returns the length of the underlying Vec.
|
||||
/// To get the actual number of items use `a.iter().count()`.
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
/// Returns true if `self.len() == 0`.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Removes all the tables.
|
||||
pub fn clear(&mut self) {
|
||||
self.values.clear();
|
||||
}
|
||||
|
||||
/// Returns an optional reference to the table.
|
||||
pub fn get(&self, index: usize) -> Option<&Table> {
|
||||
self.values.get(index).and_then(Item::as_table)
|
||||
}
|
||||
|
||||
/// Returns an optional mutable reference to the table.
|
||||
pub fn get_mut(&mut self, index: usize) -> Option<&mut Table> {
|
||||
self.values.get_mut(index).and_then(Item::as_table_mut)
|
||||
}
|
||||
|
||||
/// Appends a table to the array.
|
||||
pub fn push(&mut self, table: Table) {
|
||||
self.values.push(Item::Table(table));
|
||||
}
|
||||
|
||||
/// Inserts a table at the given index within the array, shifting all
|
||||
/// tables after it to the right.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `index > len`.
|
||||
pub fn insert(&mut self, index: usize, table: Table) {
|
||||
self.values.insert(index, Item::Table(table));
|
||||
}
|
||||
|
||||
/// Removes a table with the given index.
|
||||
pub fn remove(&mut self, index: usize) -> Table {
|
||||
self.values
|
||||
.remove(index)
|
||||
.into_table()
|
||||
.expect("cannot have any other item in an array-of-tables")
|
||||
}
|
||||
|
||||
/// Retains only the elements specified by the `keep` predicate.
|
||||
///
|
||||
/// In other words, remove all tables for which `keep(&table)` returns `false`.
|
||||
///
|
||||
/// This method operates in place, visiting each element exactly once in the
|
||||
/// original order, and preserves the order of the retained elements.
|
||||
pub fn retain<F>(&mut self, mut keep: F)
|
||||
where
|
||||
F: FnMut(&Table) -> bool,
|
||||
{
|
||||
self.values
|
||||
.retain(|item| item.as_table().map(&mut keep).unwrap_or(false));
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator type over [`ArrayOfTables`]'s [`Table`]s
|
||||
pub type ArrayOfTablesIter<'a> = Box<dyn Iterator<Item = &'a Table> + 'a>;
|
||||
/// An iterator type over [`ArrayOfTables`]'s [`Table`]s
|
||||
pub type ArrayOfTablesIterMut<'a> = Box<dyn Iterator<Item = &'a mut Table> + 'a>;
|
||||
/// An iterator type over [`ArrayOfTables`]'s [`Table`]s
|
||||
pub type ArrayOfTablesIntoIter = Box<dyn Iterator<Item = Table>>;
|
||||
|
||||
impl Extend<Table> for ArrayOfTables {
|
||||
fn extend<T: IntoIterator<Item = Table>>(&mut self, iter: T) {
|
||||
for value in iter {
|
||||
self.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Table> for ArrayOfTables {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = Table>,
|
||||
{
|
||||
let v = iter.into_iter().map(Item::Table);
|
||||
Self {
|
||||
values: v.collect(),
|
||||
span: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for ArrayOfTables {
|
||||
type Item = Table;
|
||||
type IntoIter = ArrayOfTablesIntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Box::new(
|
||||
self.values
|
||||
.into_iter()
|
||||
.filter(|v| v.is_table())
|
||||
.map(|v| v.into_table().unwrap()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> IntoIterator for &'s ArrayOfTables {
|
||||
type Item = &'s Table;
|
||||
type IntoIter = ArrayOfTablesIter<'s>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
impl std::fmt::Display for ArrayOfTables {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// HACK: Without the header, we don't really have a proper way of printing this
|
||||
self.clone().into_array().fmt(f)
|
||||
}
|
||||
}
|
||||
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
use crate::de::Error;
|
||||
|
||||
pub(crate) struct ArrayDeserializer {
|
||||
input: Vec<crate::Item>,
|
||||
span: Option<std::ops::Range<usize>>,
|
||||
}
|
||||
|
||||
impl ArrayDeserializer {
|
||||
pub(crate) fn new(input: Vec<crate::Item>, span: Option<std::ops::Range<usize>>) -> Self {
|
||||
Self { input, span }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde_core::Deserializer<'de> for ArrayDeserializer {
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_seq(ArraySeqAccess::new(self.input))
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
_fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
if serde_spanned::de::is_spanned(name) {
|
||||
if let Some(span) = self.span.clone() {
|
||||
return visitor.visit_map(
|
||||
serde_spanned::de::SpannedDeserializer::<Self, Error>::new(self, span),
|
||||
);
|
||||
} else {
|
||||
return Err(Error::custom("value is missing a span", None));
|
||||
}
|
||||
}
|
||||
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
serde_core::forward_to_deserialize_any! {
|
||||
bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
|
||||
bytes byte_buf map option unit newtype_struct
|
||||
ignored_any unit_struct tuple_struct tuple enum identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::de::IntoDeserializer<'_, Error> for ArrayDeserializer {
|
||||
type Deserializer = Self;
|
||||
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ArraySeqAccess {
|
||||
iter: std::vec::IntoIter<crate::Item>,
|
||||
}
|
||||
|
||||
impl ArraySeqAccess {
|
||||
pub(crate) fn new(input: Vec<crate::Item>) -> Self {
|
||||
Self {
|
||||
iter: input.into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde_core::de::SeqAccess<'de> for ArraySeqAccess {
|
||||
type Error = Error;
|
||||
|
||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
|
||||
where
|
||||
T: serde_core::de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.iter.next() {
|
||||
Some(v) => seed
|
||||
.deserialize(crate::de::ValueDeserializer::new(v))
|
||||
.map(Some),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+75
@@ -0,0 +1,75 @@
|
||||
/// Errors that can occur when deserializing a type.
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Error {
|
||||
inner: crate::TomlError,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub(crate) fn custom<T>(msg: T, span: Option<std::ops::Range<usize>>) -> Self
|
||||
where
|
||||
T: std::fmt::Display,
|
||||
{
|
||||
Self {
|
||||
inner: crate::TomlError::custom(msg.to_string(), span),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add key while unwinding
|
||||
pub fn add_key(&mut self, key: String) {
|
||||
self.inner.add_key(key);
|
||||
}
|
||||
|
||||
/// What went wrong
|
||||
pub fn message(&self) -> &str {
|
||||
self.inner.message()
|
||||
}
|
||||
|
||||
/// The start/end index into the original document where the error occurred
|
||||
pub fn span(&self) -> Option<std::ops::Range<usize>> {
|
||||
self.inner.span()
|
||||
}
|
||||
|
||||
pub(crate) fn set_span(&mut self, span: Option<std::ops::Range<usize>>) {
|
||||
self.inner.set_span(span);
|
||||
}
|
||||
|
||||
/// Provide the encoded TOML the error applies to
|
||||
pub fn set_input(&mut self, input: Option<&str>) {
|
||||
self.inner.set_input(input);
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::de::Error for Error {
|
||||
fn custom<T>(msg: T) -> Self
|
||||
where
|
||||
T: std::fmt::Display,
|
||||
{
|
||||
Self::custom(msg, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::TomlError> for Error {
|
||||
fn from(e: crate::TomlError) -> Self {
|
||||
Self { inner: e }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for crate::TomlError {
|
||||
fn from(e: Error) -> Self {
|
||||
e.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
Vendored
+290
@@ -0,0 +1,290 @@
|
||||
use serde_core::de::IntoDeserializer;
|
||||
|
||||
use super::Error;
|
||||
|
||||
pub(crate) struct KeyDeserializer {
|
||||
span: Option<std::ops::Range<usize>>,
|
||||
key: crate::Key,
|
||||
}
|
||||
|
||||
impl KeyDeserializer {
|
||||
pub(crate) fn new(key: crate::Key, span: Option<std::ops::Range<usize>>) -> Self {
|
||||
Self { span, key }
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDeserializer<'_, Error> for KeyDeserializer {
|
||||
type Deserializer = Self;
|
||||
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde_core::de::Deserializer<'de> for KeyDeserializer {
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
self.key.into_deserializer().deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let key: bool = self
|
||||
.key
|
||||
.parse()
|
||||
.map_err(<Error as serde_core::de::Error>::custom)?;
|
||||
key.into_deserializer().deserialize_bool(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let key: i8 = self
|
||||
.key
|
||||
.parse()
|
||||
.map_err(<Error as serde_core::de::Error>::custom)?;
|
||||
key.into_deserializer().deserialize_i8(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let key: i16 = self
|
||||
.key
|
||||
.parse()
|
||||
.map_err(<Error as serde_core::de::Error>::custom)?;
|
||||
key.into_deserializer().deserialize_i16(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let key: i32 = self
|
||||
.key
|
||||
.parse()
|
||||
.map_err(<Error as serde_core::de::Error>::custom)?;
|
||||
key.into_deserializer().deserialize_i32(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let key: i64 = self
|
||||
.key
|
||||
.parse()
|
||||
.map_err(<Error as serde_core::de::Error>::custom)?;
|
||||
key.into_deserializer().deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i128<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let key: i128 = self
|
||||
.key
|
||||
.parse()
|
||||
.map_err(<Error as serde_core::de::Error>::custom)?;
|
||||
key.into_deserializer().deserialize_i128(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let key: u8 = self
|
||||
.key
|
||||
.parse()
|
||||
.map_err(<Error as serde_core::de::Error>::custom)?;
|
||||
key.into_deserializer().deserialize_u8(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let key: u16 = self
|
||||
.key
|
||||
.parse()
|
||||
.map_err(<Error as serde_core::de::Error>::custom)?;
|
||||
key.into_deserializer().deserialize_u16(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let key: u32 = self
|
||||
.key
|
||||
.parse()
|
||||
.map_err(<Error as serde_core::de::Error>::custom)?;
|
||||
key.into_deserializer().deserialize_u32(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let key: u64 = self
|
||||
.key
|
||||
.parse()
|
||||
.map_err(<Error as serde_core::de::Error>::custom)?;
|
||||
key.into_deserializer().deserialize_u64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u128<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let key: u128 = self
|
||||
.key
|
||||
.parse()
|
||||
.map_err(<Error as serde_core::de::Error>::custom)?;
|
||||
key.into_deserializer().deserialize_u128(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let key: char = self
|
||||
.key
|
||||
.parse()
|
||||
.map_err(<Error as serde_core::de::Error>::custom)?;
|
||||
key.into_deserializer().deserialize_char(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
name: &str,
|
||||
variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let _ = name;
|
||||
let _ = variants;
|
||||
visitor.visit_enum(self)
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
_fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
if serde_spanned::de::is_spanned(name) {
|
||||
if let Some(span) = self.span.clone() {
|
||||
return visitor.visit_map(
|
||||
serde_spanned::de::SpannedDeserializer::<&str, Error>::new(
|
||||
self.key.get(),
|
||||
span,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Err(Error::custom("value is missing a span", None));
|
||||
}
|
||||
}
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_newtype_struct(self)
|
||||
}
|
||||
|
||||
serde_core::forward_to_deserialize_any! {
|
||||
f32 f64 str string seq
|
||||
bytes byte_buf map option unit
|
||||
ignored_any unit_struct tuple_struct tuple identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde_core::de::EnumAccess<'de> for KeyDeserializer {
|
||||
type Error = Error;
|
||||
type Variant = UnitOnly<Self::Error>;
|
||||
|
||||
fn variant_seed<T>(self, seed: T) -> Result<(T::Value, Self::Variant), Self::Error>
|
||||
where
|
||||
T: serde_core::de::DeserializeSeed<'de>,
|
||||
{
|
||||
seed.deserialize(self).map(unit_only)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UnitOnly<E> {
|
||||
marker: std::marker::PhantomData<E>,
|
||||
}
|
||||
|
||||
fn unit_only<T, E>(t: T) -> (T, UnitOnly<E>) {
|
||||
(
|
||||
t,
|
||||
UnitOnly {
|
||||
marker: std::marker::PhantomData,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
impl<'de, E> serde_core::de::VariantAccess<'de> for UnitOnly<E>
|
||||
where
|
||||
E: serde_core::de::Error,
|
||||
{
|
||||
type Error = E;
|
||||
|
||||
fn unit_variant(self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value, Self::Error>
|
||||
where
|
||||
T: serde_core::de::DeserializeSeed<'de>,
|
||||
{
|
||||
Err(serde_core::de::Error::invalid_type(
|
||||
serde_core::de::Unexpected::UnitVariant,
|
||||
&"newtype variant",
|
||||
))
|
||||
}
|
||||
|
||||
fn tuple_variant<V>(self, _len: usize, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde_core::de::Error::invalid_type(
|
||||
serde_core::de::Unexpected::UnitVariant,
|
||||
&"tuple variant",
|
||||
))
|
||||
}
|
||||
|
||||
fn struct_variant<V>(
|
||||
self,
|
||||
_fields: &'static [&'static str],
|
||||
_visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde_core::de::Error::invalid_type(
|
||||
serde_core::de::Unexpected::UnitVariant,
|
||||
&"struct variant",
|
||||
))
|
||||
}
|
||||
}
|
||||
Vendored
+250
@@ -0,0 +1,250 @@
|
||||
//! Deserializing TOML into Rust structures.
|
||||
//!
|
||||
//! This module contains all the Serde support for deserializing TOML documents into Rust structures.
|
||||
|
||||
use serde_core::de::DeserializeOwned;
|
||||
|
||||
mod array;
|
||||
mod error;
|
||||
mod key;
|
||||
mod table;
|
||||
mod table_enum;
|
||||
mod value;
|
||||
|
||||
use array::ArrayDeserializer;
|
||||
use key::KeyDeserializer;
|
||||
use table::TableDeserializer;
|
||||
use table_enum::TableEnumDeserializer;
|
||||
use toml_datetime::de::DatetimeDeserializer;
|
||||
|
||||
pub use error::Error;
|
||||
pub use value::ValueDeserializer;
|
||||
|
||||
/// Deserializes a string into a type.
|
||||
///
|
||||
/// This function will attempt to interpret `s` as a TOML document and
|
||||
/// deserialize `T` from the document.
|
||||
///
|
||||
/// To deserializes TOML values, instead of documents, see [`ValueDeserializer`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use serde::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Config {
|
||||
/// title: String,
|
||||
/// owner: Owner,
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Owner {
|
||||
/// name: String,
|
||||
/// }
|
||||
///
|
||||
/// let config: Config = toml_edit::de::from_str(r#"
|
||||
/// title = 'TOML Example'
|
||||
///
|
||||
/// [owner]
|
||||
/// name = 'Lisa'
|
||||
/// "#).unwrap();
|
||||
///
|
||||
/// assert_eq!(config.title, "TOML Example");
|
||||
/// assert_eq!(config.owner.name, "Lisa");
|
||||
/// ```
|
||||
#[cfg(feature = "parse")]
|
||||
pub fn from_str<T>(s: &'_ str) -> Result<T, Error>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let de = Deserializer::parse(s)?;
|
||||
T::deserialize(de)
|
||||
}
|
||||
|
||||
/// Deserializes bytes into a type.
|
||||
///
|
||||
/// This function will attempt to interpret `s` as a TOML document and
|
||||
/// deserialize `T` from the document.
|
||||
///
|
||||
/// To deserializes TOML values, instead of documents, see [`ValueDeserializer`].
|
||||
#[cfg(feature = "parse")]
|
||||
pub fn from_slice<T>(s: &'_ [u8]) -> Result<T, Error>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let s = std::str::from_utf8(s).map_err(|e| Error::custom(e, None))?;
|
||||
from_str(s)
|
||||
}
|
||||
|
||||
/// Convert a [`DocumentMut`][crate::DocumentMut] into `T`.
|
||||
pub fn from_document<T>(d: impl Into<Deserializer>) -> Result<T, Error>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let deserializer = d.into();
|
||||
T::deserialize(deserializer)
|
||||
}
|
||||
|
||||
/// Deserialization for TOML [documents][crate::DocumentMut].
|
||||
pub struct Deserializer<S = String> {
|
||||
root: crate::Item,
|
||||
raw: Option<S>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
impl<S: AsRef<str>> Deserializer<S> {
|
||||
/// Parse a TOML document
|
||||
pub fn parse(raw: S) -> Result<Self, Error> {
|
||||
crate::Document::parse(raw)
|
||||
.map(Self::from)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::DocumentMut> for Deserializer {
|
||||
fn from(doc: crate::DocumentMut) -> Self {
|
||||
let crate::DocumentMut { root, .. } = doc;
|
||||
Self { root, raw: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<crate::Document<S>> for Deserializer<S> {
|
||||
fn from(doc: crate::Document<S>) -> Self {
|
||||
let crate::Document { root, raw, .. } = doc;
|
||||
let raw = Some(raw);
|
||||
Self { root, raw }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
impl std::str::FromStr for Deserializer {
|
||||
type Err = Error;
|
||||
|
||||
/// Parses a document from a &str
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let doc: crate::Document<_> = s.parse().map_err(Error::from)?;
|
||||
Ok(Self::from(doc))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, S: AsRef<str>> serde_core::Deserializer<'de> for Deserializer<S> {
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let raw = self.raw;
|
||||
ValueDeserializer::new(self.root)
|
||||
.deserialize_any(visitor)
|
||||
.map_err(|mut e: Self::Error| {
|
||||
let raw = raw.as_ref().map(|r| r.as_ref());
|
||||
e.set_input(raw);
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
// `None` is interpreted as a missing field so be sure to implement `Some`
|
||||
// as a present field.
|
||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let raw = self.raw;
|
||||
ValueDeserializer::new(self.root)
|
||||
.deserialize_option(visitor)
|
||||
.map_err(|mut e: Self::Error| {
|
||||
let raw = raw.as_ref().map(|r| r.as_ref());
|
||||
e.set_input(raw);
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let raw = self.raw;
|
||||
ValueDeserializer::new(self.root)
|
||||
.deserialize_newtype_struct(name, visitor)
|
||||
.map_err(|mut e: Self::Error| {
|
||||
let raw = raw.as_ref().map(|r| r.as_ref());
|
||||
e.set_input(raw);
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let raw = self.raw;
|
||||
ValueDeserializer::new(self.root)
|
||||
.deserialize_struct(name, fields, visitor)
|
||||
.map_err(|mut e: Self::Error| {
|
||||
let raw = raw.as_ref().map(|r| r.as_ref());
|
||||
e.set_input(raw);
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
// Called when the type to deserialize is an enum, as opposed to a field in the type.
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let raw = self.raw;
|
||||
ValueDeserializer::new(self.root)
|
||||
.deserialize_enum(name, variants, visitor)
|
||||
.map_err(|mut e: Self::Error| {
|
||||
let raw = raw.as_ref().map(|r| r.as_ref());
|
||||
e.set_input(raw);
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
serde_core::forward_to_deserialize_any! {
|
||||
bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
|
||||
bytes byte_buf map unit
|
||||
ignored_any unit_struct tuple_struct tuple identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::de::IntoDeserializer<'_, Error> for Deserializer {
|
||||
type Deserializer = Self;
|
||||
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::de::IntoDeserializer<'_, Error> for crate::DocumentMut {
|
||||
type Deserializer = Deserializer;
|
||||
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
Deserializer::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::de::IntoDeserializer<'_, Error> for crate::Document<String> {
|
||||
type Deserializer = Deserializer;
|
||||
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
Deserializer::from(self)
|
||||
}
|
||||
}
|
||||
Vendored
+207
@@ -0,0 +1,207 @@
|
||||
use serde_core::de::IntoDeserializer;
|
||||
|
||||
use crate::de::Error;
|
||||
|
||||
pub(crate) struct TableDeserializer {
|
||||
span: Option<std::ops::Range<usize>>,
|
||||
items: crate::table::KeyValuePairs,
|
||||
}
|
||||
|
||||
impl TableDeserializer {
|
||||
pub(crate) fn new(
|
||||
items: crate::table::KeyValuePairs,
|
||||
span: Option<std::ops::Range<usize>>,
|
||||
) -> Self {
|
||||
Self { span, items }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde_core::Deserializer<'de> for TableDeserializer {
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_map(TableMapAccess::new(self))
|
||||
}
|
||||
|
||||
// `None` is interpreted as a missing field so be sure to implement `Some`
|
||||
// as a present field.
|
||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_some(self)
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_newtype_struct(self)
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
_fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
if serde_spanned::de::is_spanned(name) {
|
||||
if let Some(span) = self.span.clone() {
|
||||
return visitor.visit_map(
|
||||
serde_spanned::de::SpannedDeserializer::<Self, Error>::new(self, span),
|
||||
);
|
||||
} else {
|
||||
return Err(Error::custom("value is missing a span", None));
|
||||
}
|
||||
}
|
||||
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
// Called when the type to deserialize is an enum, as opposed to a field in the type.
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
if self.items.is_empty() {
|
||||
Err(Error::custom(
|
||||
"wanted exactly 1 element, found 0 elements",
|
||||
self.span,
|
||||
))
|
||||
} else if self.items.len() != 1 {
|
||||
Err(Error::custom(
|
||||
"wanted exactly 1 element, more than 1 element",
|
||||
self.span,
|
||||
))
|
||||
} else {
|
||||
visitor.visit_enum(TableMapAccess::new(self))
|
||||
}
|
||||
}
|
||||
|
||||
serde_core::forward_to_deserialize_any! {
|
||||
bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
|
||||
bytes byte_buf map unit
|
||||
ignored_any unit_struct tuple_struct tuple identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDeserializer<'_, Error> for TableDeserializer {
|
||||
type Deserializer = Self;
|
||||
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TableMapAccess {
|
||||
iter: indexmap::map::IntoIter<crate::Key, crate::Item>,
|
||||
span: Option<std::ops::Range<usize>>,
|
||||
value: Option<(crate::Key, crate::Item)>,
|
||||
}
|
||||
|
||||
impl TableMapAccess {
|
||||
pub(crate) fn new(input: TableDeserializer) -> Self {
|
||||
Self {
|
||||
iter: input.items.into_iter(),
|
||||
span: input.span,
|
||||
value: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde_core::de::MapAccess<'de> for TableMapAccess {
|
||||
type Error = Error;
|
||||
|
||||
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
|
||||
where
|
||||
K: serde_core::de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.iter.next() {
|
||||
Some((k, v)) => {
|
||||
let key_span = k.span();
|
||||
let ret = seed
|
||||
.deserialize(super::KeyDeserializer::new(k.clone(), key_span.clone()))
|
||||
.map(Some)
|
||||
.map_err(|mut e: Self::Error| {
|
||||
if e.span().is_none() {
|
||||
e.set_span(key_span);
|
||||
}
|
||||
e
|
||||
});
|
||||
self.value = Some((k, v));
|
||||
ret
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.value.take() {
|
||||
Some((k, v)) => {
|
||||
let span = v.span().or_else(|| k.span());
|
||||
seed.deserialize(crate::de::ValueDeserializer::new(v))
|
||||
.map_err(|mut e: Self::Error| {
|
||||
if e.span().is_none() {
|
||||
e.set_span(span);
|
||||
}
|
||||
e.add_key(k.get().to_owned());
|
||||
e
|
||||
})
|
||||
}
|
||||
None => {
|
||||
panic!("no more values in next_value_seed, internal error in ValueDeserializer")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde_core::de::EnumAccess<'de> for TableMapAccess {
|
||||
type Error = Error;
|
||||
type Variant = super::TableEnumDeserializer;
|
||||
|
||||
fn variant_seed<V>(mut self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
|
||||
where
|
||||
V: serde_core::de::DeserializeSeed<'de>,
|
||||
{
|
||||
let (key, value) = match self.iter.next() {
|
||||
Some(pair) => pair,
|
||||
None => {
|
||||
return Err(Error::custom(
|
||||
"expected table with exactly 1 entry, found empty table",
|
||||
self.span,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let val = seed
|
||||
.deserialize(key.into_deserializer())
|
||||
.map_err(|mut e: Self::Error| {
|
||||
if e.span().is_none() {
|
||||
e.set_span(key.span());
|
||||
}
|
||||
e
|
||||
})?;
|
||||
|
||||
let variant = super::TableEnumDeserializer::new(value);
|
||||
|
||||
Ok((val, variant))
|
||||
}
|
||||
}
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
use crate::de::Error;
|
||||
|
||||
/// Deserializes table values into enum variants.
|
||||
pub(crate) struct TableEnumDeserializer {
|
||||
value: crate::Item,
|
||||
}
|
||||
|
||||
impl TableEnumDeserializer {
|
||||
pub(crate) fn new(value: crate::Item) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde_core::de::VariantAccess<'de> for TableEnumDeserializer {
|
||||
type Error = Error;
|
||||
|
||||
fn unit_variant(self) -> Result<(), Self::Error> {
|
||||
match self.value {
|
||||
crate::Item::ArrayOfTables(values) => {
|
||||
if values.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::custom("expected empty array", values.span()))
|
||||
}
|
||||
}
|
||||
crate::Item::Value(crate::Value::Array(values)) => {
|
||||
if values.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::custom("expected empty table", values.span()))
|
||||
}
|
||||
}
|
||||
crate::Item::Table(values) => {
|
||||
if values.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::custom("expected empty table", values.span()))
|
||||
}
|
||||
}
|
||||
crate::Item::Value(crate::Value::InlineTable(values)) => {
|
||||
if values.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::custom("expected empty table", values.span()))
|
||||
}
|
||||
}
|
||||
e => Err(Error::custom(
|
||||
format!("expected table, found {}", e.type_name()),
|
||||
e.span(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
|
||||
where
|
||||
T: serde_core::de::DeserializeSeed<'de>,
|
||||
{
|
||||
seed.deserialize(super::ValueDeserializer::new(self.value))
|
||||
}
|
||||
|
||||
fn tuple_variant<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
crate::Item::ArrayOfTables(values) => {
|
||||
let values_span = values.span();
|
||||
let tuple_values = values.values.into_iter().collect::<Vec<_>>();
|
||||
|
||||
if tuple_values.len() == len {
|
||||
serde_core::de::Deserializer::deserialize_seq(
|
||||
super::ArrayDeserializer::new(tuple_values, values_span),
|
||||
visitor,
|
||||
)
|
||||
} else {
|
||||
Err(Error::custom(
|
||||
format!("expected tuple with length {len}"),
|
||||
values_span,
|
||||
))
|
||||
}
|
||||
}
|
||||
crate::Item::Value(crate::Value::Array(values)) => {
|
||||
let values_span = values.span();
|
||||
let tuple_values = values.values.into_iter().collect::<Vec<_>>();
|
||||
|
||||
if tuple_values.len() == len {
|
||||
serde_core::de::Deserializer::deserialize_seq(
|
||||
super::ArrayDeserializer::new(tuple_values, values_span),
|
||||
visitor,
|
||||
)
|
||||
} else {
|
||||
Err(Error::custom(
|
||||
format!("expected tuple with length {len}"),
|
||||
values_span,
|
||||
))
|
||||
}
|
||||
}
|
||||
crate::Item::Table(values) => {
|
||||
let values_span = values.span();
|
||||
let tuple_values: Result<Vec<_>, _> = values
|
||||
.items
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, (key, value))| match key.get().parse::<usize>() {
|
||||
Ok(key_index) if key_index == index => Ok(value),
|
||||
Ok(_) | Err(_) => Err(Error::custom(
|
||||
format!("expected table key `{}`, but was `{}`", index, key.get()),
|
||||
key.span(),
|
||||
)),
|
||||
})
|
||||
.collect();
|
||||
let tuple_values = tuple_values?;
|
||||
|
||||
if tuple_values.len() == len {
|
||||
serde_core::de::Deserializer::deserialize_seq(
|
||||
super::ArrayDeserializer::new(tuple_values, values_span),
|
||||
visitor,
|
||||
)
|
||||
} else {
|
||||
Err(Error::custom(
|
||||
format!("expected tuple with length {len}"),
|
||||
values_span,
|
||||
))
|
||||
}
|
||||
}
|
||||
crate::Item::Value(crate::Value::InlineTable(values)) => {
|
||||
let values_span = values.span();
|
||||
let tuple_values: Result<Vec<_>, _> = values
|
||||
.items
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, (key, value))| match key.get().parse::<usize>() {
|
||||
Ok(key_index) if key_index == index => Ok(value),
|
||||
Ok(_) | Err(_) => Err(Error::custom(
|
||||
format!("expected table key `{}`, but was `{}`", index, key.get()),
|
||||
key.span(),
|
||||
)),
|
||||
})
|
||||
.collect();
|
||||
let tuple_values = tuple_values?;
|
||||
|
||||
if tuple_values.len() == len {
|
||||
serde_core::de::Deserializer::deserialize_seq(
|
||||
super::ArrayDeserializer::new(tuple_values, values_span),
|
||||
visitor,
|
||||
)
|
||||
} else {
|
||||
Err(Error::custom(
|
||||
format!("expected tuple with length {len}"),
|
||||
values_span,
|
||||
))
|
||||
}
|
||||
}
|
||||
e => Err(Error::custom(
|
||||
format!("expected table, found {}", e.type_name()),
|
||||
e.span(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn struct_variant<V>(
|
||||
self,
|
||||
fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
serde_core::de::Deserializer::deserialize_struct(
|
||||
super::ValueDeserializer::new(self.value).with_struct_key_validation(),
|
||||
"", // TODO: this should be the variant name
|
||||
fields,
|
||||
visitor,
|
||||
)
|
||||
}
|
||||
}
|
||||
Vendored
+293
@@ -0,0 +1,293 @@
|
||||
use serde_core::de::IntoDeserializer as _;
|
||||
|
||||
use crate::de::ArrayDeserializer;
|
||||
use crate::de::DatetimeDeserializer;
|
||||
use crate::de::Error;
|
||||
use crate::de::TableDeserializer;
|
||||
|
||||
/// Deserialization implementation for TOML [values][crate::Value].
|
||||
///
|
||||
/// Can be created either directly from TOML strings, using [`std::str::FromStr`],
|
||||
/// or from parsed [values][crate::Value] using
|
||||
/// [`IntoDeserializer::into_deserializer`][serde_core::de::IntoDeserializer::into_deserializer].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "parse")] {
|
||||
/// # #[cfg(feature = "display")] {
|
||||
/// use serde::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Config {
|
||||
/// title: String,
|
||||
/// owner: Owner,
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Owner {
|
||||
/// name: String,
|
||||
/// }
|
||||
///
|
||||
/// let value = r#"{ title = 'TOML Example', owner = { name = 'Lisa' } }"#;
|
||||
/// let deserializer = value.parse::<toml_edit::de::ValueDeserializer>().unwrap();
|
||||
/// let config = Config::deserialize(deserializer).unwrap();
|
||||
/// assert_eq!(config.title, "TOML Example");
|
||||
/// assert_eq!(config.owner.name, "Lisa");
|
||||
/// # }
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct ValueDeserializer {
|
||||
input: crate::Item,
|
||||
validate_struct_keys: bool,
|
||||
}
|
||||
|
||||
impl ValueDeserializer {
|
||||
pub(crate) fn new(input: crate::Item) -> Self {
|
||||
Self {
|
||||
input,
|
||||
validate_struct_keys: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_struct_key_validation(mut self) -> Self {
|
||||
self.validate_struct_keys = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde_core::Deserializer<'de> for ValueDeserializer {
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let span = self.input.span();
|
||||
match self.input {
|
||||
crate::Item::None => visitor.visit_none(),
|
||||
crate::Item::Value(crate::Value::String(v)) => visitor.visit_string(v.into_value()),
|
||||
crate::Item::Value(crate::Value::Integer(v)) => visitor.visit_i64(v.into_value()),
|
||||
crate::Item::Value(crate::Value::Float(v)) => visitor.visit_f64(v.into_value()),
|
||||
crate::Item::Value(crate::Value::Boolean(v)) => visitor.visit_bool(v.into_value()),
|
||||
crate::Item::Value(crate::Value::Datetime(v)) => {
|
||||
visitor.visit_map(DatetimeDeserializer::new(v.into_value()))
|
||||
}
|
||||
crate::Item::Value(crate::Value::Array(v)) => {
|
||||
ArrayDeserializer::new(v.values, v.span).deserialize_any(visitor)
|
||||
}
|
||||
crate::Item::Value(crate::Value::InlineTable(v)) => {
|
||||
TableDeserializer::new(v.items, v.span).deserialize_any(visitor)
|
||||
}
|
||||
crate::Item::Table(v) => {
|
||||
TableDeserializer::new(v.items, v.span).deserialize_any(visitor)
|
||||
}
|
||||
crate::Item::ArrayOfTables(v) => {
|
||||
ArrayDeserializer::new(v.values, v.span).deserialize_any(visitor)
|
||||
}
|
||||
}
|
||||
.map_err(|mut e: Self::Error| {
|
||||
if e.span().is_none() {
|
||||
e.set_span(span);
|
||||
}
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
// `None` is interpreted as a missing field so be sure to implement `Some`
|
||||
// as a present field.
|
||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let span = self.input.span();
|
||||
visitor.visit_some(self).map_err(|mut e: Self::Error| {
|
||||
if e.span().is_none() {
|
||||
e.set_span(span);
|
||||
}
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let span = self.input.span();
|
||||
visitor
|
||||
.visit_newtype_struct(self)
|
||||
.map_err(|mut e: Self::Error| {
|
||||
if e.span().is_none() {
|
||||
e.set_span(span);
|
||||
}
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
if serde_spanned::de::is_spanned(name) {
|
||||
if let Some(span) = self.input.span() {
|
||||
return visitor.visit_map(
|
||||
serde_spanned::de::SpannedDeserializer::<Self, Error>::new(self, span),
|
||||
);
|
||||
} else {
|
||||
return Err(Error::custom("value is missing a span", None));
|
||||
}
|
||||
}
|
||||
|
||||
if toml_datetime::de::is_datetime(name) {
|
||||
let span = self.input.span();
|
||||
if let crate::Item::Value(crate::Value::Datetime(d)) = self.input {
|
||||
return visitor
|
||||
.visit_map(DatetimeDeserializer::new(d.into_value()))
|
||||
.map_err(|mut e: Self::Error| {
|
||||
if e.span().is_none() {
|
||||
e.set_span(span);
|
||||
}
|
||||
e
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if self.validate_struct_keys {
|
||||
let span = self.input.span();
|
||||
match &self.input {
|
||||
crate::Item::Table(values) => validate_struct_keys(&values.items, fields),
|
||||
crate::Item::Value(crate::Value::InlineTable(values)) => {
|
||||
validate_struct_keys(&values.items, fields)
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
.map_err(|mut e: Self::Error| {
|
||||
if e.span().is_none() {
|
||||
e.set_span(span);
|
||||
}
|
||||
e
|
||||
})?;
|
||||
}
|
||||
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
// Called when the type to deserialize is an enum, as opposed to a field in the type.
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: serde_core::de::Visitor<'de>,
|
||||
{
|
||||
let span = self.input.span();
|
||||
match self.input {
|
||||
crate::Item::Value(crate::Value::String(v)) => {
|
||||
visitor.visit_enum(v.into_value().into_deserializer())
|
||||
}
|
||||
crate::Item::Value(crate::Value::InlineTable(v)) => {
|
||||
if v.is_empty() {
|
||||
Err(Error::custom(
|
||||
"wanted exactly 1 element, found 0 elements",
|
||||
v.span(),
|
||||
))
|
||||
} else if v.len() != 1 {
|
||||
Err(Error::custom(
|
||||
"wanted exactly 1 element, more than 1 element",
|
||||
v.span(),
|
||||
))
|
||||
} else {
|
||||
TableDeserializer::new(v.items, v.span)
|
||||
.deserialize_enum(name, variants, visitor)
|
||||
}
|
||||
}
|
||||
crate::Item::Table(v) => {
|
||||
TableDeserializer::new(v.items, v.span).deserialize_enum(name, variants, visitor)
|
||||
}
|
||||
e => Err(Error::custom("wanted string or table", e.span())),
|
||||
}
|
||||
.map_err(|mut e: Self::Error| {
|
||||
if e.span().is_none() {
|
||||
e.set_span(span);
|
||||
}
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
serde_core::forward_to_deserialize_any! {
|
||||
bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
|
||||
bytes byte_buf map unit
|
||||
ignored_any unit_struct tuple_struct tuple identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::de::IntoDeserializer<'_, Error> for ValueDeserializer {
|
||||
type Deserializer = Self;
|
||||
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::de::IntoDeserializer<'_, Error> for crate::Value {
|
||||
type Deserializer = ValueDeserializer;
|
||||
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
ValueDeserializer::new(crate::Item::Value(self))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
impl std::str::FromStr for ValueDeserializer {
|
||||
type Err = Error;
|
||||
|
||||
/// Parses a value from a &str
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let value = s.parse::<crate::Value>().map_err(Error::from)?;
|
||||
Ok(value.into_deserializer())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn validate_struct_keys(
|
||||
table: &crate::table::KeyValuePairs,
|
||||
fields: &'static [&'static str],
|
||||
) -> Result<(), Error> {
|
||||
let extra_fields = table
|
||||
.keys()
|
||||
.filter_map(|key| {
|
||||
if !fields.contains(&key.get()) {
|
||||
Some(key.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if extra_fields.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::custom(
|
||||
format!(
|
||||
"unexpected keys in table: {}, available keys: {}",
|
||||
extra_fields
|
||||
.iter()
|
||||
.map(|k| k.get())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
fields.join(", "),
|
||||
),
|
||||
extra_fields[0].span(),
|
||||
))
|
||||
}
|
||||
}
|
||||
Vendored
+246
@@ -0,0 +1,246 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::table::Iter;
|
||||
use crate::{Item, RawString, Table};
|
||||
|
||||
/// The root TOML [`Table`], containing [`Key`][crate::Key]/[`Value`][crate::Value] pairs and all other logic [`Table`]s
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Document<S> {
|
||||
pub(crate) root: Item,
|
||||
// Trailing comments and whitespaces
|
||||
pub(crate) trailing: RawString,
|
||||
pub(crate) raw: S,
|
||||
}
|
||||
|
||||
impl Document<&'static str> {
|
||||
/// Creates an empty document
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
impl<S: AsRef<str>> Document<S> {
|
||||
/// Parse a TOML document
|
||||
pub fn parse(raw: S) -> Result<Self, crate::TomlError> {
|
||||
let source = toml_parser::Source::new(raw.as_ref());
|
||||
let mut sink = crate::error::TomlSink::<Option<_>>::new(source);
|
||||
let doc = crate::parser::parse_document(source, &mut sink);
|
||||
if let Some(err) = sink.into_inner() {
|
||||
Err(err)
|
||||
} else {
|
||||
Ok(Self {
|
||||
root: doc.root,
|
||||
trailing: doc.trailing,
|
||||
raw,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> Document<S> {
|
||||
/// # Panics
|
||||
///
|
||||
/// If run on a [`DocumentMut`] not generated by the parser
|
||||
pub(crate) fn despan(&mut self) {
|
||||
self.root.despan(self.raw.as_ref());
|
||||
self.trailing.despan(self.raw.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Document<S> {
|
||||
/// Returns a reference to the root item.
|
||||
pub fn as_item(&self) -> &Item {
|
||||
&self.root
|
||||
}
|
||||
|
||||
/// Returns the root item.
|
||||
pub fn into_item(self) -> Item {
|
||||
self.root
|
||||
}
|
||||
|
||||
/// Returns a reference to the root table.
|
||||
pub fn as_table(&self) -> &Table {
|
||||
self.root.as_table().expect("root should always be a table")
|
||||
}
|
||||
|
||||
/// Returns the root table.
|
||||
pub fn into_table(self) -> Table {
|
||||
self.root
|
||||
.into_table()
|
||||
.expect("root should always be a table")
|
||||
}
|
||||
|
||||
/// Returns an iterator over the root table.
|
||||
pub fn iter(&self) -> Iter<'_> {
|
||||
self.as_table().iter()
|
||||
}
|
||||
|
||||
/// Whitespace after last element
|
||||
pub fn trailing(&self) -> &RawString {
|
||||
&self.trailing
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> Document<S> {
|
||||
/// Access the raw, unparsed document
|
||||
pub fn raw(&self) -> &str {
|
||||
self.raw.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> Document<S> {
|
||||
/// Allow editing of the [`DocumentMut`]
|
||||
pub fn into_mut(mut self) -> DocumentMut {
|
||||
self.despan();
|
||||
DocumentMut {
|
||||
root: self.root,
|
||||
trailing: self.trailing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Document<&'static str> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
root: Item::Table(Table::with_pos(Some(0))),
|
||||
trailing: Default::default(),
|
||||
raw: "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
impl FromStr for Document<String> {
|
||||
type Err = crate::TomlError;
|
||||
|
||||
/// Parses a document from a &str
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::parse(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> std::ops::Deref for Document<S> {
|
||||
type Target = Table;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_table()
|
||||
}
|
||||
}
|
||||
|
||||
/// The editable root TOML [`Table`], containing [`Key`][crate::Key]/[`Value`][crate::Value] pairs and all other logic [`Table`]s
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DocumentMut {
|
||||
pub(crate) root: Item,
|
||||
// Trailing comments and whitespaces
|
||||
pub(crate) trailing: RawString,
|
||||
}
|
||||
|
||||
impl DocumentMut {
|
||||
/// Creates an empty document
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Returns a reference to the root item.
|
||||
pub fn as_item(&self) -> &Item {
|
||||
&self.root
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the root item.
|
||||
pub fn as_item_mut(&mut self) -> &mut Item {
|
||||
&mut self.root
|
||||
}
|
||||
|
||||
/// Returns the root item.
|
||||
pub fn into_item(self) -> Item {
|
||||
self.root
|
||||
}
|
||||
|
||||
/// Returns a reference to the root table.
|
||||
pub fn as_table(&self) -> &Table {
|
||||
self.root.as_table().expect("root should always be a table")
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the root table.
|
||||
pub fn as_table_mut(&mut self) -> &mut Table {
|
||||
self.root
|
||||
.as_table_mut()
|
||||
.expect("root should always be a table")
|
||||
}
|
||||
|
||||
/// Returns the root table.
|
||||
pub fn into_table(self) -> Table {
|
||||
self.root
|
||||
.into_table()
|
||||
.expect("root should always be a table")
|
||||
}
|
||||
|
||||
/// Returns an iterator over the root table.
|
||||
pub fn iter(&self) -> Iter<'_> {
|
||||
self.as_table().iter()
|
||||
}
|
||||
|
||||
/// Set whitespace after last element
|
||||
pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
|
||||
self.trailing = trailing.into();
|
||||
}
|
||||
|
||||
/// Whitespace after last element
|
||||
pub fn trailing(&self) -> &RawString {
|
||||
&self.trailing
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DocumentMut {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
root: Item::Table(Table::with_pos(Some(0))),
|
||||
trailing: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
impl FromStr for DocumentMut {
|
||||
type Err = crate::TomlError;
|
||||
|
||||
/// Parses a document from a &str
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let im = Document::from_str(s)?;
|
||||
Ok(im.into_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for DocumentMut {
|
||||
type Target = Table;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_table()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for DocumentMut {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_table_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Table> for DocumentMut {
|
||||
fn from(root: Table) -> Self {
|
||||
Self {
|
||||
root: Item::Table(root),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "parse")]
|
||||
#[cfg(feature = "display")]
|
||||
fn default_roundtrip() {
|
||||
DocumentMut::default()
|
||||
.to_string()
|
||||
.parse::<DocumentMut>()
|
||||
.unwrap();
|
||||
}
|
||||
Vendored
+450
@@ -0,0 +1,450 @@
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{Display, Formatter, Result, Write};
|
||||
|
||||
use toml_datetime::Datetime;
|
||||
use toml_writer::ToTomlValue as _;
|
||||
use toml_writer::TomlWrite as _;
|
||||
|
||||
use crate::DocumentMut;
|
||||
use crate::inline_table::DEFAULT_INLINE_KEY_DECOR;
|
||||
use crate::key::Key;
|
||||
use crate::repr::{Decor, Formatted, Repr, ValueRepr};
|
||||
use crate::table::{
|
||||
DEFAULT_KEY_DECOR, DEFAULT_KEY_PATH_DECOR, DEFAULT_ROOT_DECOR, DEFAULT_TABLE_DECOR,
|
||||
};
|
||||
use crate::value::{
|
||||
DEFAULT_LEADING_VALUE_DECOR, DEFAULT_TRAILING_VALUE_DECOR, DEFAULT_VALUE_DECOR,
|
||||
};
|
||||
use crate::{Array, InlineTable, Item, Table, Value};
|
||||
|
||||
pub(crate) fn encode_key(this: &Key, buf: &mut dyn Write, input: Option<&str>) -> Result {
|
||||
if let Some(input) = input {
|
||||
let repr = this
|
||||
.as_repr()
|
||||
.map(Cow::Borrowed)
|
||||
.unwrap_or_else(|| Cow::Owned(this.default_repr()));
|
||||
repr.encode(buf, input)?;
|
||||
} else {
|
||||
let repr = this.display_repr();
|
||||
write!(buf, "{repr}")?;
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn encode_key_path(
|
||||
this: &[Key],
|
||||
mut buf: &mut dyn Write,
|
||||
input: Option<&str>,
|
||||
default_decor: (&str, &str),
|
||||
leaf_decor: &Decor,
|
||||
) -> Result {
|
||||
for (i, key) in this.iter().enumerate() {
|
||||
let dotted_decor = key.dotted_decor();
|
||||
|
||||
let first = i == 0;
|
||||
let last = i + 1 == this.len();
|
||||
|
||||
if first {
|
||||
leaf_decor.prefix_encode(buf, input, default_decor.0)?;
|
||||
} else {
|
||||
buf.key_sep()?;
|
||||
dotted_decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
|
||||
}
|
||||
|
||||
encode_key(key, buf, input)?;
|
||||
|
||||
if last {
|
||||
leaf_decor.suffix_encode(buf, input, default_decor.1)?;
|
||||
} else {
|
||||
dotted_decor.suffix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.1)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn encode_key_path_ref(
|
||||
this: &[&Key],
|
||||
mut buf: &mut dyn Write,
|
||||
input: Option<&str>,
|
||||
default_decor: (&str, &str),
|
||||
) -> Result {
|
||||
let leaf_decor = this.last().expect("always at least one key").leaf_decor();
|
||||
for (i, key) in this.iter().enumerate() {
|
||||
let dotted_decor = key.dotted_decor();
|
||||
|
||||
let first = i == 0;
|
||||
let last = i + 1 == this.len();
|
||||
|
||||
if first {
|
||||
leaf_decor.prefix_encode(buf, input, default_decor.0)?;
|
||||
} else {
|
||||
buf.key_sep()?;
|
||||
dotted_decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
|
||||
}
|
||||
|
||||
encode_key(key, buf, input)?;
|
||||
|
||||
if last {
|
||||
leaf_decor.suffix_encode(buf, input, default_decor.1)?;
|
||||
} else {
|
||||
dotted_decor.suffix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.1)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn encode_formatted<T: ValueRepr>(
|
||||
this: &Formatted<T>,
|
||||
buf: &mut dyn Write,
|
||||
input: Option<&str>,
|
||||
default_decor: (&str, &str),
|
||||
) -> Result {
|
||||
let decor = this.decor();
|
||||
decor.prefix_encode(buf, input, default_decor.0)?;
|
||||
|
||||
if let Some(input) = input {
|
||||
let repr = this
|
||||
.as_repr()
|
||||
.map(Cow::Borrowed)
|
||||
.unwrap_or_else(|| Cow::Owned(this.default_repr()));
|
||||
repr.encode(buf, input)?;
|
||||
} else {
|
||||
let repr = this.display_repr();
|
||||
write!(buf, "{repr}")?;
|
||||
};
|
||||
|
||||
decor.suffix_encode(buf, input, default_decor.1)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn encode_array(
|
||||
this: &Array,
|
||||
mut buf: &mut dyn Write,
|
||||
input: Option<&str>,
|
||||
default_decor: (&str, &str),
|
||||
) -> Result {
|
||||
let decor = this.decor();
|
||||
decor.prefix_encode(buf, input, default_decor.0)?;
|
||||
buf.open_array()?;
|
||||
|
||||
for (i, elem) in this.iter().enumerate() {
|
||||
let inner_decor;
|
||||
if i == 0 {
|
||||
inner_decor = DEFAULT_LEADING_VALUE_DECOR;
|
||||
} else {
|
||||
inner_decor = DEFAULT_VALUE_DECOR;
|
||||
buf.val_sep()?;
|
||||
}
|
||||
encode_value(elem, buf, input, inner_decor)?;
|
||||
}
|
||||
if this.trailing_comma() && !this.is_empty() {
|
||||
buf.val_sep()?;
|
||||
}
|
||||
|
||||
this.trailing().encode_with_default(buf, input, "")?;
|
||||
buf.close_array()?;
|
||||
decor.suffix_encode(buf, input, default_decor.1)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn encode_table(
|
||||
this: &InlineTable,
|
||||
mut buf: &mut dyn Write,
|
||||
input: Option<&str>,
|
||||
default_decor: (&str, &str),
|
||||
) -> Result {
|
||||
let decor = this.decor();
|
||||
decor.prefix_encode(buf, input, default_decor.0)?;
|
||||
buf.open_inline_table()?;
|
||||
|
||||
let children = this.get_values();
|
||||
let len = children.len();
|
||||
for (i, (key_path, value)) in children.into_iter().enumerate() {
|
||||
if i != 0 {
|
||||
buf.val_sep()?;
|
||||
}
|
||||
let inner_decor = if i == len - 1 {
|
||||
DEFAULT_TRAILING_VALUE_DECOR
|
||||
} else {
|
||||
DEFAULT_VALUE_DECOR
|
||||
};
|
||||
encode_key_path_ref(&key_path, buf, input, DEFAULT_INLINE_KEY_DECOR)?;
|
||||
buf.keyval_sep()?;
|
||||
encode_value(value, buf, input, inner_decor)?;
|
||||
}
|
||||
if this.trailing_comma() && !this.is_empty() {
|
||||
buf.val_sep()?;
|
||||
}
|
||||
|
||||
this.trailing().encode_with_default(buf, input, "")?;
|
||||
buf.close_inline_table()?;
|
||||
decor.suffix_encode(buf, input, default_decor.1)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn encode_value(
|
||||
this: &Value,
|
||||
buf: &mut dyn Write,
|
||||
input: Option<&str>,
|
||||
default_decor: (&str, &str),
|
||||
) -> Result {
|
||||
match this {
|
||||
Value::String(repr) => encode_formatted(repr, buf, input, default_decor),
|
||||
Value::Integer(repr) => encode_formatted(repr, buf, input, default_decor),
|
||||
Value::Float(repr) => encode_formatted(repr, buf, input, default_decor),
|
||||
Value::Boolean(repr) => encode_formatted(repr, buf, input, default_decor),
|
||||
Value::Datetime(repr) => encode_formatted(repr, buf, input, default_decor),
|
||||
Value::Array(array) => encode_array(array, buf, input, default_decor),
|
||||
Value::InlineTable(table) => encode_table(table, buf, input, default_decor),
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DocumentMut {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
let decor = self.decor();
|
||||
decor.prefix_encode(f, None, DEFAULT_ROOT_DECOR.0)?;
|
||||
|
||||
let mut path = Vec::new();
|
||||
let mut last_position = 0;
|
||||
let mut tables = Vec::new();
|
||||
visit_nested_tables(self.as_table(), &mut path, false, &mut |t, p, is_array| {
|
||||
if let Some(pos) = t.position() {
|
||||
last_position = pos;
|
||||
}
|
||||
tables.push((last_position, t, p.clone(), is_array));
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
tables.sort_by_key(|&(id, _, _, _)| id);
|
||||
let mut first_table = true;
|
||||
for (_, table, path, is_array) in tables {
|
||||
visit_table(f, None, table, &path, is_array, &mut first_table)?;
|
||||
}
|
||||
decor.suffix_encode(f, None, DEFAULT_ROOT_DECOR.1)?;
|
||||
self.trailing().encode_with_default(f, None, "")
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_nested_tables<'t, F>(
|
||||
table: &'t Table,
|
||||
path: &mut Vec<Key>,
|
||||
is_array_of_tables: bool,
|
||||
callback: &mut F,
|
||||
) -> Result
|
||||
where
|
||||
F: FnMut(&'t Table, &Vec<Key>, bool) -> Result,
|
||||
{
|
||||
if !table.is_dotted() {
|
||||
callback(table, path, is_array_of_tables)?;
|
||||
}
|
||||
|
||||
for (key, value) in table.items.iter() {
|
||||
match value {
|
||||
Item::Table(t) => {
|
||||
let key = key.clone();
|
||||
path.push(key);
|
||||
visit_nested_tables(t, path, false, callback)?;
|
||||
path.pop();
|
||||
}
|
||||
Item::ArrayOfTables(a) => {
|
||||
for t in a.iter() {
|
||||
let key = key.clone();
|
||||
path.push(key);
|
||||
visit_nested_tables(t, path, true, callback)?;
|
||||
path.pop();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Split the leaf decor for a table header into the part before `[` and the part inside it.
|
||||
///
|
||||
/// The TOML spec only allows spaces and tabs (ws) between brackets and keys, so any prefix
|
||||
/// containing newlines must be placed before `[`.
|
||||
///
|
||||
/// Returns `(before_bracket, inside_header)`:
|
||||
/// - `before_bracket`: `Some(&Decor)` whose prefix should be written before the opening bracket,
|
||||
/// when the leaf decor prefix contains newlines.
|
||||
/// - `inside_header`: `&Decor` to use around the key path inside the brackets.
|
||||
fn leaf_decor_before_bracket<'a>(
|
||||
path: &'a [Key],
|
||||
input: Option<&str>,
|
||||
) -> (Option<&'a Decor>, &'a Decor) {
|
||||
let Some(last_key) = path.last() else {
|
||||
return (None, &Decor::EMPTY);
|
||||
};
|
||||
let leaf_decor = last_key.leaf_decor();
|
||||
let needs_extraction = leaf_decor.prefix().is_some_and(|prefix| {
|
||||
prefix
|
||||
.to_str_with_default(input, DEFAULT_KEY_PATH_DECOR.0)
|
||||
.contains('\n')
|
||||
});
|
||||
if needs_extraction {
|
||||
(Some(leaf_decor), &Decor::EMPTY)
|
||||
} else {
|
||||
(None, leaf_decor)
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_table(
|
||||
mut buf: &mut dyn Write,
|
||||
input: Option<&str>,
|
||||
table: &Table,
|
||||
path: &[Key],
|
||||
is_array_of_tables: bool,
|
||||
first_table: &mut bool,
|
||||
) -> Result {
|
||||
let children = table.get_values();
|
||||
// We are intentionally hiding implicit tables without any tables nested under them (ie
|
||||
// `table.is_empty()` which is in contrast to `table.get_values().is_empty()`). We are
|
||||
// trusting the user that an empty implicit table is not semantically meaningful
|
||||
//
|
||||
// This allows a user to delete all tables under this implicit table and the implicit table
|
||||
// will disappear.
|
||||
//
|
||||
// However, this means that users need to take care in deciding what tables get marked as
|
||||
// implicit.
|
||||
let is_visible_std_table = !(table.implicit && children.is_empty());
|
||||
|
||||
if path.is_empty() {
|
||||
// don't print header for the root node
|
||||
if !children.is_empty() {
|
||||
*first_table = false;
|
||||
}
|
||||
} else if is_array_of_tables {
|
||||
let default_decor = if *first_table {
|
||||
*first_table = false;
|
||||
("", DEFAULT_TABLE_DECOR.1)
|
||||
} else {
|
||||
DEFAULT_TABLE_DECOR
|
||||
};
|
||||
let (before_bracket, key_decor) = leaf_decor_before_bracket(path, input);
|
||||
if let Some(decor) = before_bracket {
|
||||
decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
|
||||
}
|
||||
table.decor.prefix_encode(buf, input, default_decor.0)?;
|
||||
buf.open_array_of_tables_header()?;
|
||||
encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR, key_decor)?;
|
||||
buf.close_array_of_tables_header()?;
|
||||
table.decor.suffix_encode(buf, input, default_decor.1)?;
|
||||
writeln!(buf)?;
|
||||
} else if is_visible_std_table {
|
||||
let default_decor = if *first_table {
|
||||
*first_table = false;
|
||||
("", DEFAULT_TABLE_DECOR.1)
|
||||
} else {
|
||||
DEFAULT_TABLE_DECOR
|
||||
};
|
||||
let (before_bracket, key_decor) = leaf_decor_before_bracket(path, input);
|
||||
if let Some(decor) = before_bracket {
|
||||
decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
|
||||
}
|
||||
table.decor.prefix_encode(buf, input, default_decor.0)?;
|
||||
buf.open_table_header()?;
|
||||
encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR, key_decor)?;
|
||||
buf.close_table_header()?;
|
||||
table.decor.suffix_encode(buf, input, default_decor.1)?;
|
||||
writeln!(buf)?;
|
||||
}
|
||||
// print table body
|
||||
for (key_path, value) in children {
|
||||
encode_key_path_ref(&key_path, buf, input, DEFAULT_KEY_DECOR)?;
|
||||
buf.keyval_sep()?;
|
||||
encode_value(value, buf, input, DEFAULT_VALUE_DECOR)?;
|
||||
writeln!(buf)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl ValueRepr for String {
|
||||
fn to_repr(&self) -> Repr {
|
||||
let output = toml_writer::TomlStringBuilder::new(self.as_str())
|
||||
.as_default()
|
||||
.to_toml_value();
|
||||
Repr::new_unchecked(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueRepr for i64 {
|
||||
fn to_repr(&self) -> Repr {
|
||||
let repr = self.to_toml_value();
|
||||
Repr::new_unchecked(repr)
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueRepr for f64 {
|
||||
fn to_repr(&self) -> Repr {
|
||||
let repr = self.to_toml_value();
|
||||
Repr::new_unchecked(repr)
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueRepr for bool {
|
||||
fn to_repr(&self) -> Repr {
|
||||
let repr = self.to_toml_value();
|
||||
Repr::new_unchecked(repr)
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueRepr for Datetime {
|
||||
fn to_repr(&self) -> Repr {
|
||||
Repr::new_unchecked(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use proptest::prelude::*;
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
#[cfg(feature = "parse")]
|
||||
fn parseable_string(string in "\\PC*") {
|
||||
let value = Value::from(string.clone());
|
||||
let encoded = value.to_string();
|
||||
let _: Value = encoded.parse().unwrap_or_else(|err| {
|
||||
panic!("error: {err}
|
||||
|
||||
string:
|
||||
```
|
||||
{string}
|
||||
```
|
||||
value:
|
||||
```
|
||||
{value}
|
||||
```
|
||||
")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
#[cfg(feature = "parse")]
|
||||
fn parseable_key(string in "\\PC*") {
|
||||
let key = Key::new(string.clone());
|
||||
let encoded = key.to_string();
|
||||
let _: Key = encoded.parse().unwrap_or_else(|err| {
|
||||
panic!("error: {err}
|
||||
|
||||
string:
|
||||
```
|
||||
{string}
|
||||
```
|
||||
key:
|
||||
```
|
||||
{key}
|
||||
```
|
||||
")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+299
@@ -0,0 +1,299 @@
|
||||
/// A TOML parse error
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct TomlError {
|
||||
message: String,
|
||||
input: Option<std::sync::Arc<str>>,
|
||||
keys: Vec<String>,
|
||||
span: Option<std::ops::Range<usize>>,
|
||||
}
|
||||
|
||||
impl TomlError {
|
||||
#[cfg(feature = "parse")]
|
||||
pub(crate) fn new(input: std::sync::Arc<str>, error: toml_parser::ParseError) -> Self {
|
||||
let mut message = String::new();
|
||||
message.push_str(error.description());
|
||||
if let Some(expected) = error.expected() {
|
||||
message.push_str(", expected ");
|
||||
if expected.is_empty() {
|
||||
message.push_str("nothing");
|
||||
} else {
|
||||
for (i, expected) in expected.iter().enumerate() {
|
||||
if i != 0 {
|
||||
message.push_str(", ");
|
||||
}
|
||||
match expected {
|
||||
toml_parser::Expected::Literal(desc) => {
|
||||
message.push_str(&render_literal(desc));
|
||||
}
|
||||
toml_parser::Expected::Description(desc) => message.push_str(desc),
|
||||
_ => message.push_str("etc"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let span = error.unexpected().map(|span| span.start()..span.end());
|
||||
|
||||
Self {
|
||||
message,
|
||||
input: Some(input),
|
||||
keys: Vec::new(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
pub(crate) fn custom(message: String, span: Option<std::ops::Range<usize>>) -> Self {
|
||||
Self {
|
||||
message,
|
||||
input: None,
|
||||
keys: Vec::new(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
pub(crate) fn add_key(&mut self, key: String) {
|
||||
self.keys.insert(0, key);
|
||||
}
|
||||
|
||||
/// What went wrong
|
||||
pub fn message(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
|
||||
/// The start/end index into the original document where the error occurred
|
||||
pub fn span(&self) -> Option<std::ops::Range<usize>> {
|
||||
self.span.clone()
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
pub(crate) fn set_span(&mut self, span: Option<std::ops::Range<usize>>) {
|
||||
self.span = span;
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
pub(crate) fn set_input(&mut self, input: Option<&str>) {
|
||||
self.input = input.map(|s| s.into());
|
||||
}
|
||||
}
|
||||
|
||||
fn render_literal(literal: &str) -> String {
|
||||
match literal {
|
||||
"\n" => "newline".to_owned(),
|
||||
"`" => "'`'".to_owned(),
|
||||
s if s.chars().all(|c| c.is_ascii_control()) => {
|
||||
format!("`{}`", s.escape_debug())
|
||||
}
|
||||
s => format!("`{s}`"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Displays a TOML parse error
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// TOML parse error at line 1, column 10
|
||||
/// |
|
||||
/// 1 | 00:32:00.a999999
|
||||
/// | ^
|
||||
/// Unexpected `a`
|
||||
/// Expected `digit`
|
||||
/// While parsing a Time
|
||||
/// While parsing a Date-Time
|
||||
impl std::fmt::Display for TomlError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut context = false;
|
||||
if let (Some(input), Some(span)) = (&self.input, self.span()) {
|
||||
context = true;
|
||||
|
||||
let (line, column) = translate_position(input.as_bytes(), span.start);
|
||||
let line_num = line + 1;
|
||||
let col_num = column + 1;
|
||||
let gutter = line_num.to_string().len();
|
||||
let content = input.split('\n').nth(line).expect("valid line number");
|
||||
let highlight_len = span.end - span.start;
|
||||
// Allow highlight to go one past the line
|
||||
let highlight_len = highlight_len.min(content.len().saturating_sub(column));
|
||||
|
||||
writeln!(f, "TOML parse error at line {line_num}, column {col_num}")?;
|
||||
// |
|
||||
for _ in 0..=gutter {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
writeln!(f, "|")?;
|
||||
|
||||
// 1 | 00:32:00.a999999
|
||||
write!(f, "{line_num} | ")?;
|
||||
writeln!(f, "{content}")?;
|
||||
|
||||
// | ^
|
||||
for _ in 0..=gutter {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
write!(f, "|")?;
|
||||
for _ in 0..=column {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
// The span will be empty at eof, so we need to make sure we always print at least
|
||||
// one `^`
|
||||
write!(f, "^")?;
|
||||
for _ in 1..highlight_len {
|
||||
write!(f, "^")?;
|
||||
}
|
||||
writeln!(f)?;
|
||||
}
|
||||
writeln!(f, "{}", self.message)?;
|
||||
if !context && !self.keys.is_empty() {
|
||||
writeln!(f, "in `{}`", self.keys.join("."))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for TomlError {}
|
||||
|
||||
fn translate_position(input: &[u8], index: usize) -> (usize, usize) {
|
||||
if input.is_empty() {
|
||||
return (0, index);
|
||||
}
|
||||
|
||||
let safe_index = index.min(input.len() - 1);
|
||||
let column_offset = index - safe_index;
|
||||
let index = safe_index;
|
||||
|
||||
let nl = input[0..index]
|
||||
.iter()
|
||||
.rev()
|
||||
.enumerate()
|
||||
.find(|(_, b)| **b == b'\n')
|
||||
.map(|(nl, _)| index - nl - 1);
|
||||
let line_start = match nl {
|
||||
Some(nl) => nl + 1,
|
||||
None => 0,
|
||||
};
|
||||
let line = input[0..line_start].iter().filter(|b| **b == b'\n').count();
|
||||
|
||||
let column = std::str::from_utf8(&input[line_start..=index])
|
||||
.map(|s| s.chars().count() - 1)
|
||||
.unwrap_or_else(|_| index - line_start);
|
||||
let column = column + column_offset;
|
||||
|
||||
(line, column)
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
pub(crate) struct TomlSink<'i, S> {
|
||||
source: toml_parser::Source<'i>,
|
||||
input: Option<std::sync::Arc<str>>,
|
||||
sink: S,
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
impl<'i, S: Default> TomlSink<'i, S> {
|
||||
pub(crate) fn new(source: toml_parser::Source<'i>) -> Self {
|
||||
Self {
|
||||
source,
|
||||
input: None,
|
||||
sink: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_inner(self) -> S {
|
||||
self.sink
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
impl<'i> toml_parser::ErrorSink for TomlSink<'i, Option<TomlError>> {
|
||||
fn report_error(&mut self, error: toml_parser::ParseError) {
|
||||
if self.sink.is_none() {
|
||||
let input = self
|
||||
.input
|
||||
.get_or_insert_with(|| std::sync::Arc::from(self.source.input()));
|
||||
let error = TomlError::new(input.clone(), error);
|
||||
self.sink = Some(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
impl<'i> toml_parser::ErrorSink for TomlSink<'i, Vec<TomlError>> {
|
||||
fn report_error(&mut self, error: toml_parser::ParseError) {
|
||||
let input = self
|
||||
.input
|
||||
.get_or_insert_with(|| std::sync::Arc::from(self.source.input()));
|
||||
let error = TomlError::new(input.clone(), error);
|
||||
self.sink.push(error);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_translate_position {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let input = b"";
|
||||
let index = 0;
|
||||
let position = translate_position(&input[..], index);
|
||||
assert_eq!(position, (0, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn start() {
|
||||
let input = b"Hello";
|
||||
let index = 0;
|
||||
let position = translate_position(&input[..], index);
|
||||
assert_eq!(position, (0, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn end() {
|
||||
let input = b"Hello";
|
||||
let index = input.len() - 1;
|
||||
let position = translate_position(&input[..], index);
|
||||
assert_eq!(position, (0, input.len() - 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn after() {
|
||||
let input = b"Hello";
|
||||
let index = input.len();
|
||||
let position = translate_position(&input[..], index);
|
||||
assert_eq!(position, (0, input.len()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_line() {
|
||||
let input = b"Hello\nWorld\n";
|
||||
let index = 2;
|
||||
let position = translate_position(&input[..], index);
|
||||
assert_eq!(position, (0, 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn end_of_line() {
|
||||
let input = b"Hello\nWorld\n";
|
||||
let index = 5;
|
||||
let position = translate_position(&input[..], index);
|
||||
assert_eq!(position, (0, 5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn start_of_second_line() {
|
||||
let input = b"Hello\nWorld\n";
|
||||
let index = 6;
|
||||
let position = translate_position(&input[..], index);
|
||||
assert_eq!(position, (1, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn second_line() {
|
||||
let input = b"Hello\nWorld\n";
|
||||
let index = 8;
|
||||
let position = translate_position(&input[..], index);
|
||||
assert_eq!(position, (1, 2));
|
||||
}
|
||||
}
|
||||
Vendored
+142
@@ -0,0 +1,142 @@
|
||||
use std::ops;
|
||||
|
||||
use crate::DocumentMut;
|
||||
use crate::key::Key;
|
||||
use crate::{InlineTable, Item, Table, Value, value};
|
||||
|
||||
// copied from
|
||||
// https://github.com/serde-rs/json/blob/master/src/value/index.rs
|
||||
|
||||
pub trait Index: crate::private::Sealed {
|
||||
#[doc(hidden)]
|
||||
fn index<'v>(&self, val: &'v Item) -> Option<&'v Item>;
|
||||
#[doc(hidden)]
|
||||
fn index_mut<'v>(&self, val: &'v mut Item) -> Option<&'v mut Item>;
|
||||
}
|
||||
|
||||
impl Index for usize {
|
||||
fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
|
||||
match *v {
|
||||
Item::ArrayOfTables(ref aot) => aot.values.get(*self),
|
||||
Item::Value(ref a) if a.is_array() => a.as_array().and_then(|a| a.values.get(*self)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
|
||||
match *v {
|
||||
Item::ArrayOfTables(ref mut vec) => vec.values.get_mut(*self),
|
||||
Item::Value(ref mut a) => a.as_array_mut().and_then(|a| a.values.get_mut(*self)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index for str {
|
||||
fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
|
||||
match *v {
|
||||
Item::Table(ref t) => t.get(self),
|
||||
Item::Value(ref v) => v
|
||||
.as_inline_table()
|
||||
.and_then(|t| t.items.get(self))
|
||||
.and_then(|value| if !value.is_none() { Some(value) } else { None }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
|
||||
if let Item::None = *v {
|
||||
let mut t = InlineTable::default();
|
||||
t.items.insert(Key::new(self), Item::None);
|
||||
*v = value(Value::InlineTable(t));
|
||||
}
|
||||
match *v {
|
||||
Item::Table(ref mut t) => Some(t.entry(self).or_insert(Item::None)),
|
||||
Item::Value(ref mut v) => v
|
||||
.as_inline_table_mut()
|
||||
.map(|t| t.items.entry(Key::new(self)).or_insert_with(|| Item::None)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index for String {
|
||||
fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
|
||||
self[..].index(v)
|
||||
}
|
||||
fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
|
||||
self[..].index_mut(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Index for &T
|
||||
where
|
||||
T: Index,
|
||||
{
|
||||
fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
|
||||
(**self).index(v)
|
||||
}
|
||||
fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
|
||||
(**self).index_mut(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> ops::Index<I> for Item
|
||||
where
|
||||
I: Index,
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
fn index(&self, index: I) -> &Self {
|
||||
index.index(self).expect("index not found")
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> ops::IndexMut<I> for Item
|
||||
where
|
||||
I: Index,
|
||||
{
|
||||
fn index_mut(&mut self, index: I) -> &mut Self {
|
||||
index.index_mut(self).expect("index not found")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> ops::Index<&'s str> for Table {
|
||||
type Output = Item;
|
||||
|
||||
fn index(&self, key: &'s str) -> &Item {
|
||||
self.get(key).expect("index not found")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> ops::IndexMut<&'s str> for Table {
|
||||
fn index_mut(&mut self, key: &'s str) -> &mut Item {
|
||||
self.entry(key).or_insert(Item::None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> ops::Index<&'s str> for InlineTable {
|
||||
type Output = Value;
|
||||
|
||||
fn index(&self, key: &'s str) -> &Value {
|
||||
self.get(key).expect("index not found")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> ops::IndexMut<&'s str> for InlineTable {
|
||||
fn index_mut(&mut self, key: &'s str) -> &mut Value {
|
||||
self.get_mut(key).expect("index not found")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> ops::Index<&'s str> for DocumentMut {
|
||||
type Output = Item;
|
||||
|
||||
fn index(&self, key: &'s str) -> &Item {
|
||||
self.root.index(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> ops::IndexMut<&'s str> for DocumentMut {
|
||||
fn index_mut(&mut self, key: &'s str) -> &mut Item {
|
||||
self.root.index_mut(key)
|
||||
}
|
||||
}
|
||||
Vendored
+746
@@ -0,0 +1,746 @@
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use crate::key::Key;
|
||||
use crate::repr::Decor;
|
||||
use crate::table::{Iter, IterMut, KeyValuePairs, TableLike};
|
||||
use crate::{Item, KeyMut, RawString, Table, Value};
|
||||
|
||||
/// A TOML [`Value`] that contains a collection of [`Key`]/[`Value`] pairs
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct InlineTable {
|
||||
// `trailing` represents whitespaces, newlines
|
||||
// and comments in an empty array or after the trailing comma
|
||||
trailing: RawString,
|
||||
trailing_comma: bool,
|
||||
// Whether to hide an empty table
|
||||
pub(crate) implicit: bool,
|
||||
// prefix before `{` and suffix after `}`
|
||||
decor: Decor,
|
||||
pub(crate) span: Option<std::ops::Range<usize>>,
|
||||
// whether this is a proxy for dotted keys
|
||||
dotted: bool,
|
||||
pub(crate) items: KeyValuePairs,
|
||||
}
|
||||
|
||||
/// Constructors
|
||||
///
|
||||
/// See also `FromIterator`
|
||||
impl InlineTable {
|
||||
/// Creates an empty table.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub(crate) fn with_pairs(items: KeyValuePairs) -> Self {
|
||||
Self {
|
||||
items,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert to a table
|
||||
pub fn into_table(self) -> Table {
|
||||
let mut t = Table::with_pairs(self.items);
|
||||
t.fmt();
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
/// Formatting
|
||||
impl InlineTable {
|
||||
/// Get key/values for values that are visually children of this table
|
||||
///
|
||||
/// For example, this will return dotted keys
|
||||
pub fn get_values(&self) -> Vec<(Vec<&Key>, &Value)> {
|
||||
let mut values = Vec::new();
|
||||
let root = Vec::new();
|
||||
self.append_values(&root, &mut values);
|
||||
values
|
||||
}
|
||||
|
||||
pub(crate) fn append_values<'s>(
|
||||
&'s self,
|
||||
parent: &[&'s Key],
|
||||
values: &mut Vec<(Vec<&'s Key>, &'s Value)>,
|
||||
) {
|
||||
for (key, value) in self.items.iter() {
|
||||
let mut path = parent.to_vec();
|
||||
path.push(key);
|
||||
match value {
|
||||
Item::Value(Value::InlineTable(table)) if table.is_dotted() => {
|
||||
table.append_values(&path, values);
|
||||
}
|
||||
Item::Value(value) => {
|
||||
values.push((path, value));
|
||||
}
|
||||
Item::Table(table) => {
|
||||
table.append_all_values(&path, values);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Auto formats the table.
|
||||
pub fn fmt(&mut self) {
|
||||
decorate_inline_table(self);
|
||||
}
|
||||
|
||||
/// Set whether the array will use a trailing comma
|
||||
pub fn set_trailing_comma(&mut self, yes: bool) {
|
||||
self.trailing_comma = yes;
|
||||
}
|
||||
|
||||
/// Whether the array will use a trailing comma
|
||||
pub fn trailing_comma(&self) -> bool {
|
||||
self.trailing_comma
|
||||
}
|
||||
|
||||
/// Set whitespace after last element
|
||||
pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
|
||||
self.trailing = trailing.into();
|
||||
}
|
||||
|
||||
/// Whitespace after last element
|
||||
pub fn trailing(&self) -> &RawString {
|
||||
&self.trailing
|
||||
}
|
||||
|
||||
/// Sorts [Key]/[Value]-pairs of the table
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// This is not recursive.
|
||||
///
|
||||
/// </div>
|
||||
pub fn sort_values(&mut self) {
|
||||
// Assuming standard tables have their position set and this won't negatively impact them
|
||||
self.items.sort_keys();
|
||||
for value in self.items.values_mut() {
|
||||
match value {
|
||||
Item::Value(Value::InlineTable(table)) if table.is_dotted() => {
|
||||
table.sort_values();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sort [Key]/[Value]-pairs of the table using the using the comparison function `compare`
|
||||
///
|
||||
/// The comparison function receives two key and value pairs to compare (you can sort by keys or
|
||||
/// values or their combination as needed).
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// This is not recursive.
|
||||
///
|
||||
/// </div>
|
||||
pub fn sort_values_by<F>(&mut self, mut compare: F)
|
||||
where
|
||||
F: FnMut(&Key, &Value, &Key, &Value) -> std::cmp::Ordering,
|
||||
{
|
||||
self.sort_values_by_internal(&mut compare);
|
||||
}
|
||||
|
||||
fn sort_values_by_internal<F>(&mut self, compare: &mut F)
|
||||
where
|
||||
F: FnMut(&Key, &Value, &Key, &Value) -> std::cmp::Ordering,
|
||||
{
|
||||
let modified_cmp =
|
||||
|key1: &Key, val1: &Item, key2: &Key, val2: &Item| -> std::cmp::Ordering {
|
||||
match (val1.as_value(), val2.as_value()) {
|
||||
(Some(v1), Some(v2)) => compare(key1, v1, key2, v2),
|
||||
(Some(_), None) => std::cmp::Ordering::Greater,
|
||||
(None, Some(_)) => std::cmp::Ordering::Less,
|
||||
(None, None) => std::cmp::Ordering::Equal,
|
||||
}
|
||||
};
|
||||
|
||||
self.items.sort_by(modified_cmp);
|
||||
for value in self.items.values_mut() {
|
||||
match value {
|
||||
Item::Value(Value::InlineTable(table)) if table.is_dotted() => {
|
||||
table.sort_values_by_internal(compare);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If a table has no key/value pairs and implicit, it will not be displayed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```notrust
|
||||
/// [target."x86_64/windows.json".dependencies]
|
||||
/// ```
|
||||
///
|
||||
/// In the document above, tables `target` and `target."x86_64/windows.json"` are implicit.
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "parse")] {
|
||||
/// # #[cfg(feature = "display")] {
|
||||
/// use toml_edit::DocumentMut;
|
||||
/// let mut doc = "[a]\n[a.b]\n".parse::<DocumentMut>().expect("invalid toml");
|
||||
///
|
||||
/// doc["a"].as_table_mut().unwrap().set_implicit(true);
|
||||
/// assert_eq!(doc.to_string(), "[a.b]\n");
|
||||
/// # }
|
||||
/// # }
|
||||
/// ```
|
||||
pub(crate) fn set_implicit(&mut self, implicit: bool) {
|
||||
self.implicit = implicit;
|
||||
}
|
||||
|
||||
/// If a table has no key/value pairs and implicit, it will not be displayed.
|
||||
pub(crate) fn is_implicit(&self) -> bool {
|
||||
self.implicit
|
||||
}
|
||||
|
||||
/// Change this table's dotted status
|
||||
pub fn set_dotted(&mut self, yes: bool) {
|
||||
self.dotted = yes;
|
||||
}
|
||||
|
||||
/// Check if this is a wrapper for dotted keys, rather than a standard table
|
||||
pub fn is_dotted(&self) -> bool {
|
||||
self.dotted
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace
|
||||
pub fn decor_mut(&mut self) -> &mut Decor {
|
||||
&mut self.decor
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace
|
||||
pub fn decor(&self) -> &Decor {
|
||||
&self.decor
|
||||
}
|
||||
|
||||
/// Returns an accessor to a key's formatting
|
||||
pub fn key(&self, key: &str) -> Option<&'_ Key> {
|
||||
self.items.get_full(key).map(|(_, key, _)| key)
|
||||
}
|
||||
|
||||
/// Returns an accessor to a key's formatting
|
||||
pub fn key_mut(&mut self, key: &str) -> Option<KeyMut<'_>> {
|
||||
use indexmap::map::MutableKeys;
|
||||
self.items
|
||||
.get_full_mut2(key)
|
||||
.map(|(_, key, _)| key.as_mut())
|
||||
}
|
||||
|
||||
/// The location within the original document
|
||||
///
|
||||
/// This generally requires a [`Document`][crate::Document].
|
||||
pub fn span(&self) -> Option<std::ops::Range<usize>> {
|
||||
self.span.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn despan(&mut self, input: &str) {
|
||||
use indexmap::map::MutableKeys;
|
||||
self.span = None;
|
||||
self.decor.despan(input);
|
||||
self.trailing.despan(input);
|
||||
for (key, value) in self.items.iter_mut2() {
|
||||
key.despan(input);
|
||||
value.despan(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InlineTable {
|
||||
/// Returns an iterator over key/value pairs.
|
||||
pub fn iter(&self) -> InlineTableIter<'_> {
|
||||
Box::new(
|
||||
self.items
|
||||
.iter()
|
||||
.filter(|(_, value)| !value.is_none())
|
||||
.map(|(key, value)| (key.get(), value.as_value().unwrap())),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns an iterator over key/value pairs.
|
||||
pub fn iter_mut(&mut self) -> InlineTableIterMut<'_> {
|
||||
use indexmap::map::MutableKeys;
|
||||
Box::new(
|
||||
self.items
|
||||
.iter_mut2()
|
||||
.filter(|(_, value)| value.is_value())
|
||||
.map(|(key, value)| (key.as_mut(), value.as_value_mut().unwrap())),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the number of key/value pairs.
|
||||
pub fn len(&self) -> usize {
|
||||
self.iter().count()
|
||||
}
|
||||
|
||||
/// Returns true if the table is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Clears the table, removing all key-value pairs. Keeps the allocated memory for reuse.
|
||||
pub fn clear(&mut self) {
|
||||
self.items.clear();
|
||||
}
|
||||
|
||||
/// Gets the given key's corresponding entry in the Table for in-place manipulation.
|
||||
pub fn entry(&'_ mut self, key: impl Into<String>) -> InlineEntry<'_> {
|
||||
match self.items.entry(key.into().into()) {
|
||||
indexmap::map::Entry::Occupied(mut entry) => {
|
||||
// Ensure it is a `Value` to simplify `InlineOccupiedEntry`'s code.
|
||||
let scratch = std::mem::take(entry.get_mut());
|
||||
let scratch = Item::Value(
|
||||
scratch
|
||||
.into_value()
|
||||
// HACK: `Item::None` is a corner case of a corner case, let's just pick a
|
||||
// "safe" value
|
||||
.unwrap_or_else(|_| Value::InlineTable(Default::default())),
|
||||
);
|
||||
*entry.get_mut() = scratch;
|
||||
|
||||
InlineEntry::Occupied(InlineOccupiedEntry { entry })
|
||||
}
|
||||
indexmap::map::Entry::Vacant(entry) => InlineEntry::Vacant(InlineVacantEntry { entry }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the given key's corresponding entry in the Table for in-place manipulation.
|
||||
pub fn entry_format<'a>(&'a mut self, key: &Key) -> InlineEntry<'a> {
|
||||
// Accept a `&Key` to be consistent with `entry`
|
||||
match self.items.entry(key.clone()) {
|
||||
indexmap::map::Entry::Occupied(mut entry) => {
|
||||
// Ensure it is a `Value` to simplify `InlineOccupiedEntry`'s code.
|
||||
let scratch = std::mem::take(entry.get_mut());
|
||||
let scratch = Item::Value(
|
||||
scratch
|
||||
.into_value()
|
||||
// HACK: `Item::None` is a corner case of a corner case, let's just pick a
|
||||
// "safe" value
|
||||
.unwrap_or_else(|_| Value::InlineTable(Default::default())),
|
||||
);
|
||||
*entry.get_mut() = scratch;
|
||||
|
||||
InlineEntry::Occupied(InlineOccupiedEntry { entry })
|
||||
}
|
||||
indexmap::map::Entry::Vacant(entry) => InlineEntry::Vacant(InlineVacantEntry { entry }),
|
||||
}
|
||||
}
|
||||
/// Return an optional reference to the value at the given the key.
|
||||
pub fn get(&self, key: &str) -> Option<&Value> {
|
||||
self.items.get(key).and_then(|value| value.as_value())
|
||||
}
|
||||
|
||||
/// Return an optional mutable reference to the value at the given the key.
|
||||
pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
|
||||
self.items
|
||||
.get_mut(key)
|
||||
.and_then(|value| value.as_value_mut())
|
||||
}
|
||||
|
||||
/// Return references to the key-value pair stored for key, if it is present, else None.
|
||||
pub fn get_key_value<'a>(&'a self, key: &str) -> Option<(&'a Key, &'a Item)> {
|
||||
self.items.get_full(key).and_then(|(_, key, value)| {
|
||||
if !value.is_none() {
|
||||
Some((key, value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Return mutable references to the key-value pair stored for key, if it is present, else None.
|
||||
pub fn get_key_value_mut<'a>(&'a mut self, key: &str) -> Option<(KeyMut<'a>, &'a mut Item)> {
|
||||
use indexmap::map::MutableKeys;
|
||||
self.items.get_full_mut2(key).and_then(|(_, key, value)| {
|
||||
if !value.is_none() {
|
||||
Some((key.as_mut(), value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns true if the table contains given key.
|
||||
pub fn contains_key(&self, key: &str) -> bool {
|
||||
if let Some(value) = self.items.get(key) {
|
||||
value.is_value()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts a key/value pair if the table does not contain the key.
|
||||
/// Returns a mutable reference to the corresponding value.
|
||||
pub fn get_or_insert<V: Into<Value>>(
|
||||
&mut self,
|
||||
key: impl Into<String>,
|
||||
value: V,
|
||||
) -> &mut Value {
|
||||
let key = key.into();
|
||||
self.items
|
||||
.entry(Key::new(key))
|
||||
.or_insert(Item::Value(value.into()))
|
||||
.as_value_mut()
|
||||
.expect("non-value type in inline table")
|
||||
}
|
||||
|
||||
/// Inserts a key-value pair into the map.
|
||||
pub fn insert(&mut self, key: impl Into<String>, value: Value) -> Option<Value> {
|
||||
use indexmap::map::MutableEntryKey;
|
||||
let key = Key::new(key);
|
||||
let value = Item::Value(value);
|
||||
match self.items.entry(key.clone()) {
|
||||
indexmap::map::Entry::Occupied(mut entry) => {
|
||||
entry.key_mut().fmt();
|
||||
let old = std::mem::replace(entry.get_mut(), value);
|
||||
old.into_value().ok()
|
||||
}
|
||||
indexmap::map::Entry::Vacant(entry) => {
|
||||
entry.insert(value);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts a key-value pair into the map.
|
||||
pub fn insert_formatted(&mut self, key: &Key, value: Value) -> Option<Value> {
|
||||
use indexmap::map::MutableEntryKey;
|
||||
let value = Item::Value(value);
|
||||
match self.items.entry(key.clone()) {
|
||||
indexmap::map::Entry::Occupied(mut entry) => {
|
||||
*entry.key_mut() = key.clone();
|
||||
let old = std::mem::replace(entry.get_mut(), value);
|
||||
old.into_value().ok()
|
||||
}
|
||||
indexmap::map::Entry::Vacant(entry) => {
|
||||
entry.insert(value);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes an item given the key.
|
||||
pub fn remove(&mut self, key: &str) -> Option<Value> {
|
||||
self.items
|
||||
.shift_remove(key)
|
||||
.and_then(|value| value.into_value().ok())
|
||||
}
|
||||
|
||||
/// Removes a key from the map, returning the stored key and value if the key was previously in the map.
|
||||
pub fn remove_entry(&mut self, key: &str) -> Option<(Key, Value)> {
|
||||
self.items
|
||||
.shift_remove_entry(key)
|
||||
.and_then(|(key, value)| Some((key, value.into_value().ok()?)))
|
||||
}
|
||||
|
||||
/// Retains only the elements specified by the `keep` predicate.
|
||||
///
|
||||
/// In other words, remove all pairs `(key, value)` for which
|
||||
/// `keep(&key, &mut value)` returns `false`.
|
||||
///
|
||||
/// The elements are visited in iteration order.
|
||||
pub fn retain<F>(&mut self, mut keep: F)
|
||||
where
|
||||
F: FnMut(&str, &mut Value) -> bool,
|
||||
{
|
||||
self.items.retain(|key, item| {
|
||||
item.as_value_mut()
|
||||
.map(|value| keep(key, value))
|
||||
.unwrap_or(false)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
impl std::fmt::Display for InlineTable {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
crate::encode::encode_table(self, f, None, ("", ""))
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Into<Key>, V: Into<Value>> Extend<(K, V)> for InlineTable {
|
||||
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
|
||||
for (key, value) in iter {
|
||||
let key = key.into();
|
||||
let value = Item::Value(value.into());
|
||||
self.items.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Into<Key>, V: Into<Value>> FromIterator<(K, V)> for InlineTable {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = (K, V)>,
|
||||
{
|
||||
let mut table = Self::new();
|
||||
table.extend(iter);
|
||||
table
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for InlineTable {
|
||||
type Item = (String, Value);
|
||||
type IntoIter = InlineTableIntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Box::new(
|
||||
self.items
|
||||
.into_iter()
|
||||
.filter(|(_, value)| value.is_value())
|
||||
.map(|(key, value)| (key.into(), value.into_value().unwrap())),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> IntoIterator for &'s InlineTable {
|
||||
type Item = (&'s str, &'s Value);
|
||||
type IntoIter = InlineTableIter<'s>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
fn decorate_inline_table(table: &mut InlineTable) {
|
||||
use indexmap::map::MutableKeys;
|
||||
for (mut key, value) in table
|
||||
.items
|
||||
.iter_mut2()
|
||||
.filter(|(_, value)| value.is_value())
|
||||
.map(|(key, value)| (key.as_mut(), value.as_value_mut().unwrap()))
|
||||
{
|
||||
key.leaf_decor_mut().clear();
|
||||
key.dotted_decor_mut().clear();
|
||||
value.decor_mut().clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// An owned iterator type over an [`InlineTable`]'s [`Key`]/[`Value`] pairs
|
||||
pub type InlineTableIntoIter = Box<dyn Iterator<Item = (String, Value)>>;
|
||||
/// An iterator type over [`InlineTable`]'s [`Key`]/[`Value`] pairs
|
||||
pub type InlineTableIter<'a> = Box<dyn Iterator<Item = (&'a str, &'a Value)> + 'a>;
|
||||
/// A mutable iterator type over [`InlineTable`]'s [`Key`]/[`Value`] pairs
|
||||
pub type InlineTableIterMut<'a> = Box<dyn Iterator<Item = (KeyMut<'a>, &'a mut Value)> + 'a>;
|
||||
|
||||
impl TableLike for InlineTable {
|
||||
fn iter(&self) -> Iter<'_> {
|
||||
Box::new(self.items.iter().map(|(key, value)| (key.get(), value)))
|
||||
}
|
||||
fn iter_mut(&mut self) -> IterMut<'_> {
|
||||
use indexmap::map::MutableKeys;
|
||||
Box::new(
|
||||
self.items
|
||||
.iter_mut2()
|
||||
.map(|(key, value)| (key.as_mut(), value)),
|
||||
)
|
||||
}
|
||||
fn clear(&mut self) {
|
||||
self.clear();
|
||||
}
|
||||
fn entry<'a>(&'a mut self, key: &str) -> crate::Entry<'a> {
|
||||
// Accept a `&str` rather than an owned type to keep `String`, well, internal
|
||||
match self.items.entry(key.into()) {
|
||||
indexmap::map::Entry::Occupied(entry) => {
|
||||
crate::Entry::Occupied(crate::OccupiedEntry { entry })
|
||||
}
|
||||
indexmap::map::Entry::Vacant(entry) => {
|
||||
crate::Entry::Vacant(crate::VacantEntry { entry })
|
||||
}
|
||||
}
|
||||
}
|
||||
fn entry_format<'a>(&'a mut self, key: &Key) -> crate::Entry<'a> {
|
||||
// Accept a `&Key` to be consistent with `entry`
|
||||
match self.items.entry(key.get().into()) {
|
||||
indexmap::map::Entry::Occupied(entry) => {
|
||||
crate::Entry::Occupied(crate::OccupiedEntry { entry })
|
||||
}
|
||||
indexmap::map::Entry::Vacant(entry) => {
|
||||
crate::Entry::Vacant(crate::VacantEntry { entry })
|
||||
}
|
||||
}
|
||||
}
|
||||
fn get<'s>(&'s self, key: &str) -> Option<&'s Item> {
|
||||
self.items.get(key)
|
||||
}
|
||||
fn get_mut<'s>(&'s mut self, key: &str) -> Option<&'s mut Item> {
|
||||
self.items.get_mut(key)
|
||||
}
|
||||
fn get_key_value<'a>(&'a self, key: &str) -> Option<(&'a Key, &'a Item)> {
|
||||
self.get_key_value(key)
|
||||
}
|
||||
fn get_key_value_mut<'a>(&'a mut self, key: &str) -> Option<(KeyMut<'a>, &'a mut Item)> {
|
||||
self.get_key_value_mut(key)
|
||||
}
|
||||
fn contains_key(&self, key: &str) -> bool {
|
||||
self.contains_key(key)
|
||||
}
|
||||
fn insert(&mut self, key: &str, value: Item) -> Option<Item> {
|
||||
self.insert(key, value.into_value().unwrap())
|
||||
.map(Item::Value)
|
||||
}
|
||||
fn remove(&mut self, key: &str) -> Option<Item> {
|
||||
self.remove(key).map(Item::Value)
|
||||
}
|
||||
|
||||
fn get_values(&self) -> Vec<(Vec<&Key>, &Value)> {
|
||||
self.get_values()
|
||||
}
|
||||
fn fmt(&mut self) {
|
||||
self.fmt();
|
||||
}
|
||||
fn sort_values(&mut self) {
|
||||
self.sort_values();
|
||||
}
|
||||
fn set_dotted(&mut self, yes: bool) {
|
||||
self.set_dotted(yes);
|
||||
}
|
||||
fn is_dotted(&self) -> bool {
|
||||
self.is_dotted()
|
||||
}
|
||||
|
||||
fn key(&self, key: &str) -> Option<&'_ Key> {
|
||||
self.key(key)
|
||||
}
|
||||
fn key_mut(&mut self, key: &str) -> Option<KeyMut<'_>> {
|
||||
self.key_mut(key)
|
||||
}
|
||||
}
|
||||
|
||||
// `{ key1 = value1, ... }`
|
||||
pub(crate) const DEFAULT_INLINE_KEY_DECOR: (&str, &str) = (" ", " ");
|
||||
|
||||
/// A view into a single location in an [`InlineTable`], which may be vacant or occupied.
|
||||
pub enum InlineEntry<'a> {
|
||||
/// An occupied Entry.
|
||||
Occupied(InlineOccupiedEntry<'a>),
|
||||
/// A vacant Entry.
|
||||
Vacant(InlineVacantEntry<'a>),
|
||||
}
|
||||
|
||||
impl<'a> InlineEntry<'a> {
|
||||
/// Returns the entry key
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use toml_edit::Table;
|
||||
///
|
||||
/// let mut map = Table::new();
|
||||
///
|
||||
/// assert_eq!("hello", map.entry("hello").key());
|
||||
/// ```
|
||||
pub fn key(&self) -> &str {
|
||||
match self {
|
||||
InlineEntry::Occupied(e) => e.key(),
|
||||
InlineEntry::Vacant(e) => e.key(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures a value is in the entry by inserting the default if empty, and returns
|
||||
/// a mutable reference to the value in the entry.
|
||||
pub fn or_insert(self, default: Value) -> &'a mut Value {
|
||||
match self {
|
||||
InlineEntry::Occupied(entry) => entry.into_mut(),
|
||||
InlineEntry::Vacant(entry) => entry.insert(default),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures a value is in the entry by inserting the result of the default function if empty,
|
||||
/// and returns a mutable reference to the value in the entry.
|
||||
pub fn or_insert_with<F: FnOnce() -> Value>(self, default: F) -> &'a mut Value {
|
||||
match self {
|
||||
InlineEntry::Occupied(entry) => entry.into_mut(),
|
||||
InlineEntry::Vacant(entry) => entry.insert(default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A view into a single occupied location in an [`InlineTable`].
|
||||
pub struct InlineOccupiedEntry<'a> {
|
||||
entry: indexmap::map::OccupiedEntry<'a, Key, Item>,
|
||||
}
|
||||
|
||||
impl<'a> InlineOccupiedEntry<'a> {
|
||||
/// Gets a reference to the entry key
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use toml_edit::Table;
|
||||
///
|
||||
/// let mut map = Table::new();
|
||||
///
|
||||
/// assert_eq!("foo", map.entry("foo").key());
|
||||
/// ```
|
||||
pub fn key(&self) -> &str {
|
||||
self.entry.key().get()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the entry key
|
||||
pub fn key_mut(&mut self) -> KeyMut<'_> {
|
||||
use indexmap::map::MutableEntryKey;
|
||||
self.entry.key_mut().as_mut()
|
||||
}
|
||||
|
||||
/// Gets a reference to the value in the entry.
|
||||
pub fn get(&self) -> &Value {
|
||||
self.entry.get().as_value().unwrap()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the value in the entry.
|
||||
pub fn get_mut(&mut self) -> &mut Value {
|
||||
self.entry.get_mut().as_value_mut().unwrap()
|
||||
}
|
||||
|
||||
/// Converts the `OccupiedEntry` into a mutable reference to the value in the entry
|
||||
/// with a lifetime bound to the map itself
|
||||
pub fn into_mut(self) -> &'a mut Value {
|
||||
self.entry.into_mut().as_value_mut().unwrap()
|
||||
}
|
||||
|
||||
/// Sets the value of the entry, and returns the entry's old value
|
||||
pub fn insert(&mut self, value: Value) -> Value {
|
||||
let value = Item::Value(value);
|
||||
self.entry.insert(value).into_value().unwrap()
|
||||
}
|
||||
|
||||
/// Takes the value out of the entry, and returns it
|
||||
pub fn remove(self) -> Value {
|
||||
self.entry.shift_remove().into_value().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// A view into a single empty location in an [`InlineTable`].
|
||||
pub struct InlineVacantEntry<'a> {
|
||||
entry: indexmap::map::VacantEntry<'a, Key, Item>,
|
||||
}
|
||||
|
||||
impl<'a> InlineVacantEntry<'a> {
|
||||
/// Gets a reference to the entry key
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use toml_edit::Table;
|
||||
///
|
||||
/// let mut map = Table::new();
|
||||
///
|
||||
/// assert_eq!("foo", map.entry("foo").key());
|
||||
/// ```
|
||||
pub fn key(&self) -> &str {
|
||||
self.entry.key().get()
|
||||
}
|
||||
|
||||
/// Sets the value of the entry with the `VacantEntry`'s key,
|
||||
/// and returns a mutable reference to it
|
||||
pub fn insert(self, value: Value) -> &'a mut Value {
|
||||
let entry = self.entry;
|
||||
let value = Item::Value(value);
|
||||
entry.insert(value).as_value_mut().unwrap()
|
||||
}
|
||||
}
|
||||
Vendored
+450
@@ -0,0 +1,450 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use toml_datetime::Datetime;
|
||||
|
||||
use crate::array_of_tables::ArrayOfTables;
|
||||
use crate::table::TableLike;
|
||||
use crate::{Array, InlineTable, Table, Value};
|
||||
|
||||
/// Type representing either a value, a table, an array of tables, or none.
|
||||
#[derive(Debug, Default)]
|
||||
pub enum Item {
|
||||
/// Type representing none.
|
||||
#[default]
|
||||
None,
|
||||
/// Type representing value.
|
||||
Value(Value),
|
||||
/// Type representing table.
|
||||
Table(Table),
|
||||
/// Type representing array of tables.
|
||||
ArrayOfTables(ArrayOfTables),
|
||||
}
|
||||
|
||||
impl Item {
|
||||
/// Sets `self` to the given item if `self` is none and
|
||||
/// returns a mutable reference to `self`.
|
||||
pub fn or_insert(&mut self, item: Self) -> &mut Self {
|
||||
if self.is_none() {
|
||||
*self = item;
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This should be generated by macro or derive
|
||||
/// Downcasting
|
||||
impl Item {
|
||||
/// Text description of value type
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::None => "none",
|
||||
Self::Value(v) => v.type_name(),
|
||||
Self::Table(..) => "table",
|
||||
Self::ArrayOfTables(..) => "array of tables",
|
||||
}
|
||||
}
|
||||
|
||||
/// Index into a TOML array or map. A string index can be used to access a
|
||||
/// value in a map, and a usize index can be used to access an element of an
|
||||
/// array.
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The type of `self` does not match the type of the
|
||||
/// index, for example if the index is a string and `self` is an array or a
|
||||
/// number.
|
||||
/// - The given key does not exist in the map
|
||||
/// or the given index is not within the bounds of the array.
|
||||
pub fn get<I: crate::index::Index>(&self, index: I) -> Option<&Self> {
|
||||
index.index(self)
|
||||
}
|
||||
|
||||
/// Mutably index into a TOML array or map. A string index can be used to
|
||||
/// access a value in a map, and a usize index can be used to access an
|
||||
/// element of an array.
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The type of `self` does not match the type of the
|
||||
/// index, for example if the index is a string and `self` is an array or a
|
||||
/// number.
|
||||
/// - The given key does not exist in the map
|
||||
/// or the given index is not within the bounds of the array.
|
||||
pub fn get_mut<I: crate::index::Index>(&mut self, index: I) -> Option<&mut Self> {
|
||||
index.index_mut(self)
|
||||
}
|
||||
|
||||
/// Casts `self` to [`Value`]
|
||||
pub fn as_value(&self) -> Option<&Value> {
|
||||
match *self {
|
||||
Self::Value(ref v) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Casts `self` to [`Table`]
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// To operate on both [`Table`]s and [`InlineTable`]s, see [`Item::as_table_like`]
|
||||
///
|
||||
/// </div>
|
||||
pub fn as_table(&self) -> Option<&Table> {
|
||||
match *self {
|
||||
Self::Table(ref t) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Casts `self` to [`ArrayOfTables`]
|
||||
pub fn as_array_of_tables(&self) -> Option<&ArrayOfTables> {
|
||||
match *self {
|
||||
Self::ArrayOfTables(ref a) => Some(a),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Casts `self` to mutable [`Value`].
|
||||
pub fn as_value_mut(&mut self) -> Option<&mut Value> {
|
||||
match *self {
|
||||
Self::Value(ref mut v) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Casts `self` to mutable [`Table`]
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// To operate on both [`Table`]s and [`InlineTable`]s, see [`Item::as_table_like_mut`]
|
||||
///
|
||||
/// </div>
|
||||
pub fn as_table_mut(&mut self) -> Option<&mut Table> {
|
||||
match *self {
|
||||
Self::Table(ref mut t) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Casts `self` to mutable [`ArrayOfTables`]
|
||||
pub fn as_array_of_tables_mut(&mut self) -> Option<&mut ArrayOfTables> {
|
||||
match *self {
|
||||
Self::ArrayOfTables(ref mut a) => Some(a),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Casts `self` to [`Value`]
|
||||
pub fn into_value(self) -> Result<Value, Self> {
|
||||
match self {
|
||||
Self::None => Err(self),
|
||||
Self::Value(v) => Ok(v),
|
||||
Self::Table(v) => {
|
||||
let v = v.into_inline_table();
|
||||
Ok(Value::InlineTable(v))
|
||||
}
|
||||
Self::ArrayOfTables(v) => {
|
||||
let v = v.into_array();
|
||||
Ok(Value::Array(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
/// In-place convert to a value
|
||||
pub fn make_value(&mut self) {
|
||||
let other = std::mem::take(self);
|
||||
let other = other.into_value().map(Item::Value).unwrap_or(Self::None);
|
||||
*self = other;
|
||||
}
|
||||
/// Casts `self` to [`Table`]
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// This does not include [`InlineTable`]s
|
||||
///
|
||||
/// </div>
|
||||
pub fn into_table(self) -> Result<Table, Self> {
|
||||
match self {
|
||||
Self::Table(t) => Ok(t),
|
||||
Self::Value(Value::InlineTable(t)) => Ok(t.into_table()),
|
||||
_ => Err(self),
|
||||
}
|
||||
}
|
||||
/// Casts `self` to [`ArrayOfTables`]
|
||||
pub fn into_array_of_tables(self) -> Result<ArrayOfTables, Self> {
|
||||
match self {
|
||||
Self::ArrayOfTables(a) => Ok(a),
|
||||
Self::Value(Value::Array(a)) => {
|
||||
if a.is_empty() {
|
||||
Err(Self::Value(Value::Array(a)))
|
||||
} else if a.iter().all(|v| v.is_inline_table()) {
|
||||
let mut aot = ArrayOfTables::new();
|
||||
aot.values = a.values;
|
||||
for value in aot.values.iter_mut() {
|
||||
value.make_item();
|
||||
}
|
||||
Ok(aot)
|
||||
} else {
|
||||
Err(Self::Value(Value::Array(a)))
|
||||
}
|
||||
}
|
||||
_ => Err(self),
|
||||
}
|
||||
}
|
||||
// Starting private because the name is unclear
|
||||
pub(crate) fn make_item(&mut self) {
|
||||
let other = std::mem::take(self);
|
||||
let other = match other.into_table().map(Item::Table) {
|
||||
Ok(i) => i,
|
||||
Err(i) => i,
|
||||
};
|
||||
let other = match other.into_array_of_tables().map(Item::ArrayOfTables) {
|
||||
Ok(i) => i,
|
||||
Err(i) => i,
|
||||
};
|
||||
*self = other;
|
||||
}
|
||||
/// Returns true if `self` is a [`Value`]
|
||||
pub fn is_value(&self) -> bool {
|
||||
self.as_value().is_some()
|
||||
}
|
||||
/// Returns true if `self` is a [`Table`]
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// To operate on both [`Table`]s and [`InlineTable`]s, see [`Item::is_table_like`]
|
||||
///
|
||||
/// </div>
|
||||
pub fn is_table(&self) -> bool {
|
||||
self.as_table().is_some()
|
||||
}
|
||||
/// Returns true if `self` is an [`ArrayOfTables`]
|
||||
pub fn is_array_of_tables(&self) -> bool {
|
||||
self.as_array_of_tables().is_some()
|
||||
}
|
||||
/// Returns true if `self` is `None`.
|
||||
pub fn is_none(&self) -> bool {
|
||||
matches!(*self, Self::None)
|
||||
}
|
||||
|
||||
// Duplicate Value downcasting API
|
||||
|
||||
/// Casts `self` to integer.
|
||||
pub fn as_integer(&self) -> Option<i64> {
|
||||
self.as_value().and_then(Value::as_integer)
|
||||
}
|
||||
|
||||
/// Returns true if `self` is an integer.
|
||||
pub fn is_integer(&self) -> bool {
|
||||
self.as_integer().is_some()
|
||||
}
|
||||
|
||||
/// Casts `self` to float.
|
||||
pub fn as_float(&self) -> Option<f64> {
|
||||
self.as_value().and_then(Value::as_float)
|
||||
}
|
||||
|
||||
/// Returns true if `self` is a float.
|
||||
pub fn is_float(&self) -> bool {
|
||||
self.as_float().is_some()
|
||||
}
|
||||
|
||||
/// Casts `self` to boolean.
|
||||
pub fn as_bool(&self) -> Option<bool> {
|
||||
self.as_value().and_then(Value::as_bool)
|
||||
}
|
||||
|
||||
/// Returns true if `self` is a boolean.
|
||||
pub fn is_bool(&self) -> bool {
|
||||
self.as_bool().is_some()
|
||||
}
|
||||
|
||||
/// Casts `self` to str.
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
self.as_value().and_then(Value::as_str)
|
||||
}
|
||||
|
||||
/// Returns true if `self` is a string.
|
||||
pub fn is_str(&self) -> bool {
|
||||
self.as_str().is_some()
|
||||
}
|
||||
|
||||
/// Casts `self` to date-time.
|
||||
pub fn as_datetime(&self) -> Option<&Datetime> {
|
||||
self.as_value().and_then(Value::as_datetime)
|
||||
}
|
||||
|
||||
/// Returns true if `self` is a date-time.
|
||||
pub fn is_datetime(&self) -> bool {
|
||||
self.as_datetime().is_some()
|
||||
}
|
||||
|
||||
/// Casts `self` to array.
|
||||
pub fn as_array(&self) -> Option<&Array> {
|
||||
self.as_value().and_then(Value::as_array)
|
||||
}
|
||||
|
||||
/// Casts `self` to mutable array.
|
||||
pub fn as_array_mut(&mut self) -> Option<&mut Array> {
|
||||
self.as_value_mut().and_then(Value::as_array_mut)
|
||||
}
|
||||
|
||||
/// Returns true if `self` is an array.
|
||||
pub fn is_array(&self) -> bool {
|
||||
self.as_array().is_some()
|
||||
}
|
||||
|
||||
/// Casts `self` to inline table.
|
||||
pub fn as_inline_table(&self) -> Option<&InlineTable> {
|
||||
self.as_value().and_then(Value::as_inline_table)
|
||||
}
|
||||
|
||||
/// Casts `self` to mutable inline table.
|
||||
pub fn as_inline_table_mut(&mut self) -> Option<&mut InlineTable> {
|
||||
self.as_value_mut().and_then(Value::as_inline_table_mut)
|
||||
}
|
||||
|
||||
/// Returns true if `self` is an inline table.
|
||||
pub fn is_inline_table(&self) -> bool {
|
||||
self.as_inline_table().is_some()
|
||||
}
|
||||
|
||||
/// Casts `self` to either a table or an inline table.
|
||||
pub fn as_table_like(&self) -> Option<&dyn TableLike> {
|
||||
self.as_table()
|
||||
.map(|t| t as &dyn TableLike)
|
||||
.or_else(|| self.as_inline_table().map(|t| t as &dyn TableLike))
|
||||
}
|
||||
|
||||
/// Casts `self` to either a table or an inline table.
|
||||
pub fn as_table_like_mut(&mut self) -> Option<&mut dyn TableLike> {
|
||||
match self {
|
||||
Self::Table(t) => Some(t as &mut dyn TableLike),
|
||||
Self::Value(Value::InlineTable(t)) => Some(t as &mut dyn TableLike),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `self` is either a table, or an inline table.
|
||||
pub fn is_table_like(&self) -> bool {
|
||||
self.as_table_like().is_some()
|
||||
}
|
||||
|
||||
/// The location within the original document
|
||||
///
|
||||
/// This generally requires a [`Document`][crate::Document].
|
||||
pub fn span(&self) -> Option<std::ops::Range<usize>> {
|
||||
match self {
|
||||
Self::None => None,
|
||||
Self::Value(v) => v.span(),
|
||||
Self::Table(v) => v.span(),
|
||||
Self::ArrayOfTables(v) => v.span(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn despan(&mut self, input: &str) {
|
||||
match self {
|
||||
Self::None => {}
|
||||
Self::Value(v) => v.despan(input),
|
||||
Self::Table(v) => v.despan(input),
|
||||
Self::ArrayOfTables(v) => v.despan(input),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Item {
|
||||
#[inline(never)]
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::None => Self::None,
|
||||
Self::Value(v) => Self::Value(v.clone()),
|
||||
Self::Table(v) => Self::Table(v.clone()),
|
||||
Self::ArrayOfTables(v) => Self::ArrayOfTables(v.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
impl FromStr for Item {
|
||||
type Err = crate::TomlError;
|
||||
|
||||
/// Parses a value from a &str
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let value = s.parse::<Value>()?;
|
||||
Ok(Self::Value(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> From<&'b Self> for Item {
|
||||
fn from(s: &'b Self) -> Self {
|
||||
s.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Table> for Item {
|
||||
fn from(s: Table) -> Self {
|
||||
Self::Table(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArrayOfTables> for Item {
|
||||
fn from(s: ArrayOfTables) -> Self {
|
||||
Self::ArrayOfTables(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Into<Value>> From<V> for Item {
|
||||
fn from(s: V) -> Self {
|
||||
Self::Value(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
impl std::fmt::Display for Item {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self {
|
||||
Self::None => Ok(()),
|
||||
Self::Value(v) => v.fmt(f),
|
||||
Self::Table(v) => v.fmt(f),
|
||||
Self::ArrayOfTables(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a formatted value.
|
||||
///
|
||||
/// Since formatting is part of a `Value`, the right hand side of the
|
||||
/// assignment needs to be decorated with a space before the value.
|
||||
/// The `value` function does just that.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "display")] {
|
||||
/// # #[cfg(feature = "parse")] {
|
||||
/// # use toml_edit::*;
|
||||
/// let mut table = Table::default();
|
||||
/// let mut array = Array::default();
|
||||
/// array.push("hello");
|
||||
/// array.push("\\, world"); // \ is only allowed in a literal string
|
||||
/// table["key1"] = value("value1");
|
||||
/// table["key2"] = value(42);
|
||||
/// table["key3"] = value(array);
|
||||
/// assert_eq!(table.to_string(),
|
||||
/// r#"key1 = "value1"
|
||||
/// key2 = 42
|
||||
/// key3 = ["hello", '\, world']
|
||||
/// "#);
|
||||
/// # }
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn value<V: Into<Value>>(v: V) -> Item {
|
||||
Item::Value(v.into())
|
||||
}
|
||||
|
||||
/// Returns an empty table.
|
||||
pub fn table() -> Item {
|
||||
Item::Table(Table::new())
|
||||
}
|
||||
|
||||
/// Returns an empty array of tables.
|
||||
pub fn array() -> Item {
|
||||
Item::ArrayOfTables(ArrayOfTables::new())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "parse")]
|
||||
#[cfg(feature = "display")]
|
||||
fn string_roundtrip() {
|
||||
value("hello").to_string().parse::<Item>().unwrap();
|
||||
}
|
||||
Vendored
+397
@@ -0,0 +1,397 @@
|
||||
use std::borrow::Cow;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
use toml_writer::ToTomlKey as _;
|
||||
|
||||
use crate::repr::{Decor, Repr};
|
||||
|
||||
/// For Key/[`Value`][crate::Value] pairs under a [`Table`][crate::Table] header or inside an
|
||||
/// [`InlineTable`][crate::InlineTable]
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```notrust
|
||||
/// [dependencies."nom"]
|
||||
/// version = "5.0"
|
||||
/// 'literal key' = "nonsense"
|
||||
/// "basic string key" = 42
|
||||
/// ```
|
||||
///
|
||||
/// There are 3 types of keys:
|
||||
///
|
||||
/// 1. Bare keys (`version` and `dependencies`)
|
||||
///
|
||||
/// 2. Basic quoted keys (`"basic string key"` and `"nom"`)
|
||||
///
|
||||
/// 3. Literal quoted keys (`'literal key'`)
|
||||
///
|
||||
/// For details see [toml spec](https://github.com/toml-lang/toml/#keyvalue-pair).
|
||||
///
|
||||
/// To parse a key use `FromStr` trait implementation: `"string".parse::<Key>()`.
|
||||
#[derive(Debug)]
|
||||
pub struct Key {
|
||||
key: String,
|
||||
pub(crate) repr: Option<Repr>,
|
||||
pub(crate) leaf_decor: Decor,
|
||||
pub(crate) dotted_decor: Decor,
|
||||
}
|
||||
|
||||
impl Key {
|
||||
/// Create a new table key
|
||||
pub fn new(key: impl Into<String>) -> Self {
|
||||
Self {
|
||||
key: key.into(),
|
||||
repr: None,
|
||||
leaf_decor: Default::default(),
|
||||
dotted_decor: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a TOML key expression
|
||||
///
|
||||
/// Unlike `"".parse<Key>()`, this supports dotted keys.
|
||||
#[cfg(feature = "parse")]
|
||||
pub fn parse(repr: &str) -> Result<Vec<Self>, crate::TomlError> {
|
||||
Self::try_parse_path(repr)
|
||||
}
|
||||
|
||||
pub(crate) fn with_repr_unchecked(mut self, repr: Repr) -> Self {
|
||||
self.repr = Some(repr);
|
||||
self
|
||||
}
|
||||
|
||||
/// While creating the `Key`, add `Decor` to it for the line entry
|
||||
pub fn with_leaf_decor(mut self, decor: Decor) -> Self {
|
||||
self.leaf_decor = decor;
|
||||
self
|
||||
}
|
||||
|
||||
/// While creating the `Key`, add `Decor` to it for between dots
|
||||
pub fn with_dotted_decor(mut self, decor: Decor) -> Self {
|
||||
self.dotted_decor = decor;
|
||||
self
|
||||
}
|
||||
|
||||
/// Access a mutable proxy for the `Key`.
|
||||
pub fn as_mut(&mut self) -> KeyMut<'_> {
|
||||
KeyMut { key: self }
|
||||
}
|
||||
|
||||
/// Returns the parsed key value.
|
||||
pub fn get(&self) -> &str {
|
||||
&self.key
|
||||
}
|
||||
|
||||
/// Returns key raw representation, if available.
|
||||
pub fn as_repr(&self) -> Option<&Repr> {
|
||||
self.repr.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the default raw representation.
|
||||
#[cfg(feature = "display")]
|
||||
pub fn default_repr(&self) -> Repr {
|
||||
let output = toml_writer::TomlKeyBuilder::new(&self.key)
|
||||
.as_default()
|
||||
.to_toml_key();
|
||||
Repr::new_unchecked(output)
|
||||
}
|
||||
|
||||
/// Returns a raw representation.
|
||||
#[cfg(feature = "display")]
|
||||
pub fn display_repr(&self) -> Cow<'_, str> {
|
||||
self.as_repr()
|
||||
.and_then(|r| r.as_raw().as_str())
|
||||
.map(Cow::Borrowed)
|
||||
.unwrap_or_else(|| {
|
||||
Cow::Owned(self.default_repr().as_raw().as_str().unwrap().to_owned())
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace for the line entry
|
||||
pub fn leaf_decor_mut(&mut self) -> &mut Decor {
|
||||
&mut self.leaf_decor
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace for between dots
|
||||
pub fn dotted_decor_mut(&mut self) -> &mut Decor {
|
||||
&mut self.dotted_decor
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace for the line entry
|
||||
pub fn leaf_decor(&self) -> &Decor {
|
||||
&self.leaf_decor
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace for between dots
|
||||
pub fn dotted_decor(&self) -> &Decor {
|
||||
&self.dotted_decor
|
||||
}
|
||||
|
||||
/// The location within the original document
|
||||
///
|
||||
/// This generally requires a [`Document`][crate::Document].
|
||||
pub fn span(&self) -> Option<std::ops::Range<usize>> {
|
||||
self.repr.as_ref().and_then(|r| r.span())
|
||||
}
|
||||
|
||||
pub(crate) fn despan(&mut self, input: &str) {
|
||||
self.leaf_decor.despan(input);
|
||||
self.dotted_decor.despan(input);
|
||||
if let Some(repr) = &mut self.repr {
|
||||
repr.despan(input);
|
||||
}
|
||||
}
|
||||
|
||||
/// Auto formats the key.
|
||||
pub fn fmt(&mut self) {
|
||||
self.repr = None;
|
||||
self.leaf_decor.clear();
|
||||
self.dotted_decor.clear();
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
fn try_parse_simple(s: &str) -> Result<Self, crate::TomlError> {
|
||||
let source = toml_parser::Source::new(s);
|
||||
let mut sink = crate::error::TomlSink::<Option<_>>::new(source);
|
||||
let mut key = crate::parser::parse_key(source, &mut sink);
|
||||
if let Some(err) = sink.into_inner() {
|
||||
Err(err)
|
||||
} else {
|
||||
key.despan(s);
|
||||
Ok(key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
fn try_parse_path(s: &str) -> Result<Vec<Self>, crate::TomlError> {
|
||||
let source = toml_parser::Source::new(s);
|
||||
let mut sink = crate::error::TomlSink::<Option<_>>::new(source);
|
||||
let mut keys = crate::parser::parse_key_path(source, &mut sink);
|
||||
if let Some(err) = sink.into_inner() {
|
||||
Err(err)
|
||||
} else {
|
||||
for key in &mut keys {
|
||||
key.despan(s);
|
||||
}
|
||||
Ok(keys)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Key {
|
||||
#[inline(never)]
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
key: self.key.clone(),
|
||||
repr: self.repr.clone(),
|
||||
leaf_decor: self.leaf_decor.clone(),
|
||||
dotted_decor: self.dotted_decor.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Key {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::borrow::Borrow<str> for Key {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &str {
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for Key {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.get().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Key {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.get().cmp(other.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Key {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Key {}
|
||||
|
||||
impl PartialEq for Key {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
PartialEq::eq(self.get(), other.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for Key {
|
||||
#[inline]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
PartialEq::eq(self.get(), other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for Key {
|
||||
#[inline]
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
PartialEq::eq(self.get(), *other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<String> for Key {
|
||||
#[inline]
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
PartialEq::eq(self.get(), other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
impl std::fmt::Display for Key {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
crate::encode::encode_key(self, f, None)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
impl FromStr for Key {
|
||||
type Err = crate::TomlError;
|
||||
|
||||
/// Tries to parse a key from a &str,
|
||||
/// if fails, tries as basic quoted key (surrounds with "")
|
||||
/// and then literal quoted key (surrounds with '')
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::try_parse_simple(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> From<&'b str> for Key {
|
||||
fn from(s: &'b str) -> Self {
|
||||
Self::new(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> From<&'b String> for Key {
|
||||
fn from(s: &'b String) -> Self {
|
||||
Self::new(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Key {
|
||||
fn from(s: String) -> Self {
|
||||
Self::new(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<Key> for String {
|
||||
fn from(key: Key) -> Self {
|
||||
key.key
|
||||
}
|
||||
}
|
||||
|
||||
/// A mutable reference to a [`Key`]'s formatting
|
||||
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||
pub struct KeyMut<'k> {
|
||||
key: &'k mut Key,
|
||||
}
|
||||
|
||||
impl KeyMut<'_> {
|
||||
/// Returns the parsed key value.
|
||||
pub fn get(&self) -> &str {
|
||||
self.key.get()
|
||||
}
|
||||
|
||||
/// Returns the raw representation, if available.
|
||||
pub fn as_repr(&self) -> Option<&Repr> {
|
||||
self.key.as_repr()
|
||||
}
|
||||
|
||||
/// Returns the default raw representation.
|
||||
#[cfg(feature = "display")]
|
||||
pub fn default_repr(&self) -> Repr {
|
||||
self.key.default_repr()
|
||||
}
|
||||
|
||||
/// Returns a raw representation.
|
||||
#[cfg(feature = "display")]
|
||||
pub fn display_repr(&self) -> Cow<'_, str> {
|
||||
self.key.display_repr()
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace for the line entry
|
||||
pub fn leaf_decor_mut(&mut self) -> &mut Decor {
|
||||
self.key.leaf_decor_mut()
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace for between dots
|
||||
pub fn dotted_decor_mut(&mut self) -> &mut Decor {
|
||||
self.key.dotted_decor_mut()
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace for the line entry
|
||||
pub fn leaf_decor(&self) -> &Decor {
|
||||
self.key.leaf_decor()
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace for between dots
|
||||
pub fn dotted_decor(&self) -> &Decor {
|
||||
self.key.dotted_decor()
|
||||
}
|
||||
|
||||
/// Auto formats the key.
|
||||
pub fn fmt(&mut self) {
|
||||
self.key.fmt();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for KeyMut<'_> {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for KeyMut<'_> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
PartialEq::eq(self.get(), other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> PartialEq<&'s str> for KeyMut<'s> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
PartialEq::eq(self.get(), *other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<String> for KeyMut<'_> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
PartialEq::eq(self.get(), other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
impl std::fmt::Display for KeyMut<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(&self.key, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "parse")]
|
||||
#[cfg(feature = "display")]
|
||||
fn string_roundtrip() {
|
||||
Key::new("hello").to_string().parse::<Key>().unwrap();
|
||||
}
|
||||
Vendored
+147
@@ -0,0 +1,147 @@
|
||||
//! # `toml_edit`
|
||||
//!
|
||||
//! This crate allows you to parse and modify toml
|
||||
//! documents, while preserving comments, spaces *and
|
||||
//! relative order* of items.
|
||||
//!
|
||||
//! If you also need the ease of a more traditional API, see the [`toml`] crate.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[cfg(feature = "parse")] {
|
||||
//! # #[cfg(feature = "display")] {
|
||||
//! use toml_edit::{DocumentMut, value};
|
||||
//!
|
||||
//! let toml = r#"
|
||||
//! "hello" = 'toml!' # comment
|
||||
//! ['a'.b]
|
||||
//! "#;
|
||||
//! let mut doc = toml.parse::<DocumentMut>().expect("invalid doc");
|
||||
//! assert_eq!(doc.to_string(), toml);
|
||||
//! // let's add a new key/value pair inside a.b: c = {d = "hello"}
|
||||
//! doc["a"]["b"]["c"]["d"] = value("hello");
|
||||
//! // autoformat inline table a.b.c: { d = "hello" }
|
||||
//! doc["a"]["b"]["c"].as_inline_table_mut().map(|t| t.fmt());
|
||||
//! let expected = r#"
|
||||
//! "hello" = 'toml!' # comment
|
||||
//! ['a'.b]
|
||||
//! c = { d = "hello" }
|
||||
//! "#;
|
||||
//! assert_eq!(doc.to_string(), expected);
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Controlling formatting
|
||||
//!
|
||||
//! By default, values are created with default formatting
|
||||
//! ```rust
|
||||
//! # #[cfg(feature = "display")] {
|
||||
//! # #[cfg(feature = "parse")] {
|
||||
//! let mut doc = toml_edit::DocumentMut::new();
|
||||
//! doc["foo"] = toml_edit::value("bar");
|
||||
//! let expected = r#"foo = "bar"
|
||||
//! "#;
|
||||
//! assert_eq!(doc.to_string(), expected);
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! You can choose a custom TOML representation by parsing the value.
|
||||
//! ```rust
|
||||
//! # #[cfg(feature = "display")] {
|
||||
//! # #[cfg(feature = "parse")] {
|
||||
//! let mut doc = toml_edit::DocumentMut::new();
|
||||
//! doc["foo"] = "'bar'".parse::<toml_edit::Item>().unwrap();
|
||||
//! let expected = r#"foo = 'bar'
|
||||
//! "#;
|
||||
//! assert_eq!(doc.to_string(), expected);
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Limitations
|
||||
//!
|
||||
//! Things it does not preserve:
|
||||
//!
|
||||
//! * Order of dotted keys, see [issue](https://github.com/toml-rs/toml/issues/163).
|
||||
//!
|
||||
//! [`toml`]: https://docs.rs/toml/latest/toml/
|
||||
|
||||
// https://github.com/Marwes/combine/issues/172
|
||||
#![recursion_limit = "256"]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![warn(missing_docs)]
|
||||
#![warn(clippy::print_stderr)]
|
||||
#![warn(clippy::print_stdout)]
|
||||
|
||||
mod array;
|
||||
mod array_of_tables;
|
||||
mod document;
|
||||
#[cfg(feature = "display")]
|
||||
mod encode;
|
||||
mod error;
|
||||
mod index;
|
||||
mod inline_table;
|
||||
mod item;
|
||||
mod key;
|
||||
#[cfg(feature = "parse")]
|
||||
mod parser;
|
||||
mod raw_string;
|
||||
mod repr;
|
||||
mod table;
|
||||
mod value;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
pub mod de;
|
||||
#[cfg(feature = "serde")]
|
||||
pub mod ser;
|
||||
|
||||
pub mod visit;
|
||||
pub mod visit_mut;
|
||||
|
||||
pub use crate::array::{Array, ArrayIntoIter, ArrayIter, ArrayIterMut};
|
||||
pub use crate::array_of_tables::{
|
||||
ArrayOfTables, ArrayOfTablesIntoIter, ArrayOfTablesIter, ArrayOfTablesIterMut,
|
||||
};
|
||||
pub use crate::document::DocumentMut;
|
||||
/// Type representing a parsed TOML document
|
||||
#[deprecated(since = "0.23.0", note = "Replaced with `Document`")]
|
||||
pub type ImDocument<S> = Document<S>;
|
||||
pub use crate::document::Document;
|
||||
pub use crate::error::TomlError;
|
||||
pub use crate::inline_table::{
|
||||
InlineEntry, InlineOccupiedEntry, InlineTable, InlineTableIntoIter, InlineTableIter,
|
||||
InlineTableIterMut, InlineVacantEntry,
|
||||
};
|
||||
pub use crate::item::{Item, array, table, value};
|
||||
pub use crate::key::{Key, KeyMut};
|
||||
pub use crate::raw_string::RawString;
|
||||
pub use crate::repr::{Decor, Formatted, Repr};
|
||||
pub use crate::table::{
|
||||
Entry, IntoIter, Iter, IterMut, OccupiedEntry, Table, TableLike, VacantEntry,
|
||||
};
|
||||
pub use crate::value::Value;
|
||||
pub use toml_datetime::*;
|
||||
|
||||
// Prevent users from some traits.
|
||||
pub(crate) mod private {
|
||||
pub trait Sealed {}
|
||||
impl Sealed for usize {}
|
||||
impl Sealed for str {}
|
||||
impl Sealed for String {}
|
||||
impl Sealed for i64 {}
|
||||
impl Sealed for f64 {}
|
||||
impl Sealed for bool {}
|
||||
impl Sealed for crate::Datetime {}
|
||||
impl<T: ?Sized> Sealed for &T where T: Sealed {}
|
||||
impl Sealed for crate::Table {}
|
||||
impl Sealed for crate::InlineTable {}
|
||||
}
|
||||
|
||||
#[doc = include_str!("../README.md")]
|
||||
#[cfg(doctest)]
|
||||
#[cfg(feature = "display")]
|
||||
#[cfg(feature = "parse")]
|
||||
pub struct ReadmeDoctests;
|
||||
Vendored
+156
@@ -0,0 +1,156 @@
|
||||
use crate::parser::inline_table::on_inline_table;
|
||||
use crate::parser::value::on_scalar;
|
||||
use crate::{Array, RawString, Value};
|
||||
|
||||
use crate::parser::prelude::*;
|
||||
|
||||
/// ```abnf
|
||||
/// ;; Array
|
||||
///
|
||||
/// array = array-open array-values array-close
|
||||
/// array-values = ws-comment-newline val ws-comment-newline array-sep array-values
|
||||
/// array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ]
|
||||
/// ```
|
||||
pub(crate) fn on_array(
|
||||
open_event: &toml_parser::parser::Event,
|
||||
input: &mut Input<'_>,
|
||||
source: toml_parser::Source<'_>,
|
||||
errors: &mut dyn ErrorSink,
|
||||
) -> Value {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("array::on_array");
|
||||
let mut result = Array::new();
|
||||
|
||||
let mut state = State::default();
|
||||
state.open(open_event);
|
||||
while let Some(event) = input.next_token() {
|
||||
match event.kind() {
|
||||
EventKind::StdTableOpen
|
||||
| EventKind::ArrayTableOpen
|
||||
| EventKind::InlineTableClose
|
||||
| EventKind::SimpleKey
|
||||
| EventKind::KeySep
|
||||
| EventKind::KeyValSep
|
||||
| EventKind::StdTableClose
|
||||
| EventKind::ArrayTableClose => {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("unexpected {event:?}"),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
break;
|
||||
}
|
||||
EventKind::Error => {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("unexpected {event:?}"),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
EventKind::InlineTableOpen => {
|
||||
let value = on_inline_table(event, input, source, errors);
|
||||
state.capture_value(event, value);
|
||||
}
|
||||
EventKind::ArrayOpen => {
|
||||
let value = on_array(event, input, source, errors);
|
||||
state.capture_value(event, value);
|
||||
}
|
||||
EventKind::Scalar => {
|
||||
let value = on_scalar(event, source, errors);
|
||||
state.capture_value(event, value);
|
||||
}
|
||||
EventKind::ValueSep => {
|
||||
state.finish_value(event, &mut result);
|
||||
state.sep_value(event);
|
||||
}
|
||||
EventKind::Whitespace | EventKind::Comment | EventKind::Newline => {
|
||||
state.whitespace(event);
|
||||
}
|
||||
EventKind::ArrayClose => {
|
||||
state.finish_value(event, &mut result);
|
||||
state.close(open_event, event, &mut result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value::Array(result)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct State {
|
||||
current_prefix: Option<toml_parser::Span>,
|
||||
current_value: Option<Value>,
|
||||
trailing_start: Option<usize>,
|
||||
current_suffix: Option<toml_parser::Span>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn open(&mut self, open_event: &toml_parser::parser::Event) {
|
||||
self.trailing_start = Some(open_event.span().end());
|
||||
}
|
||||
|
||||
fn whitespace(&mut self, event: &toml_parser::parser::Event) {
|
||||
let decor = if self.is_prefix() {
|
||||
self.current_prefix.get_or_insert(event.span())
|
||||
} else {
|
||||
self.current_suffix.get_or_insert(event.span())
|
||||
};
|
||||
*decor = decor.append(event.span());
|
||||
}
|
||||
|
||||
fn is_prefix(&self) -> bool {
|
||||
self.current_value.is_none()
|
||||
}
|
||||
|
||||
fn capture_value(&mut self, event: &toml_parser::parser::Event, value: Value) {
|
||||
self.trailing_start = None;
|
||||
self.current_prefix
|
||||
.get_or_insert_with(|| event.span().before());
|
||||
self.current_value = Some(value);
|
||||
}
|
||||
|
||||
fn finish_value(&mut self, event: &toml_parser::parser::Event, result: &mut Array) {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("array::finish_value");
|
||||
if let Some(mut value) = self.current_value.take() {
|
||||
let prefix = self
|
||||
.current_prefix
|
||||
.take()
|
||||
.expect("setting a value should set a prefix");
|
||||
let suffix = self
|
||||
.current_suffix
|
||||
.take()
|
||||
.unwrap_or_else(|| event.span().before());
|
||||
let decor = value.decor_mut();
|
||||
decor.set_prefix(RawString::with_span(prefix.start()..prefix.end()));
|
||||
decor.set_suffix(RawString::with_span(suffix.start()..suffix.end()));
|
||||
result.push_formatted(value);
|
||||
}
|
||||
}
|
||||
|
||||
fn sep_value(&mut self, event: &toml_parser::parser::Event) {
|
||||
self.trailing_start = Some(event.span().end());
|
||||
}
|
||||
|
||||
fn close(
|
||||
&mut self,
|
||||
open_event: &toml_parser::parser::Event,
|
||||
close_event: &toml_parser::parser::Event,
|
||||
result: &mut Array,
|
||||
) {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("array::close");
|
||||
let trailing_comma = self.trailing_start.is_some() && !result.is_empty();
|
||||
let span = open_event.span().append(close_event.span());
|
||||
let trailing_start = self
|
||||
.trailing_start
|
||||
.unwrap_or_else(|| close_event.span().start());
|
||||
let trailing_end = close_event.span().start();
|
||||
|
||||
result.set_trailing_comma(trailing_comma);
|
||||
result.set_trailing(RawString::with_span(trailing_start..trailing_end));
|
||||
result.span = Some(span.start()..span.end());
|
||||
}
|
||||
}
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
pub(crate) struct TraceScope {
|
||||
text: String,
|
||||
style: anstyle::Style,
|
||||
guard: DebugDepthGuard,
|
||||
}
|
||||
|
||||
impl TraceScope {
|
||||
pub(crate) fn new(text: impl core::fmt::Display) -> Self {
|
||||
let text = text.to_string();
|
||||
let style = anstyle::Style::new();
|
||||
trace(&format!("> {text}"), style);
|
||||
Self {
|
||||
text,
|
||||
style,
|
||||
guard: DEBUG_DEPTH.scoped(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TraceScope {
|
||||
fn drop(&mut self) {
|
||||
let text = &self.text;
|
||||
let style = self.style;
|
||||
drop(self.guard.take());
|
||||
trace(&format!("< {text}"), style);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn trace(text: &str, style: anstyle::Style) {
|
||||
#![allow(unexpected_cfgs)] // HACK: fixed in newer versions
|
||||
let depth = DEBUG_DEPTH.depth();
|
||||
anstream::eprintln!("{:depth$}{style}{text}{style:#}", "");
|
||||
}
|
||||
|
||||
pub(crate) struct DebugDepth(core::sync::atomic::AtomicUsize);
|
||||
|
||||
impl DebugDepth {
|
||||
pub(crate) fn scoped(&self) -> DebugDepthGuard {
|
||||
DebugDepthGuard::new()
|
||||
}
|
||||
|
||||
pub(crate) fn enter_unchecked(&self) -> usize {
|
||||
self.0.fetch_add(1, core::sync::atomic::Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub(crate) fn exit_unchecked(&self) {
|
||||
let _ = self.0.fetch_sub(1, core::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub(crate) fn depth(&self) -> usize {
|
||||
self.0.load(core::sync::atomic::Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
static DEBUG_DEPTH: DebugDepth = DebugDepth(core::sync::atomic::AtomicUsize::new(0));
|
||||
|
||||
pub(crate) struct DebugDepthGuard {
|
||||
depth: usize,
|
||||
inc: bool,
|
||||
}
|
||||
|
||||
impl DebugDepthGuard {
|
||||
pub(crate) fn new() -> Self {
|
||||
let depth = DEBUG_DEPTH.enter_unchecked();
|
||||
Self { depth, inc: true }
|
||||
}
|
||||
|
||||
fn take(&mut self) -> Self {
|
||||
let depth = self.depth;
|
||||
let inc = self.inc;
|
||||
self.inc = false;
|
||||
Self { depth, inc }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DebugDepthGuard {
|
||||
fn drop(&mut self) {
|
||||
if self.inc {
|
||||
DEBUG_DEPTH.exit_unchecked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<usize> for DebugDepthGuard {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &usize {
|
||||
&self.depth
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Deref for DebugDepthGuard {
|
||||
type Target = usize;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.depth
|
||||
}
|
||||
}
|
||||
+573
@@ -0,0 +1,573 @@
|
||||
use crate::Item;
|
||||
use crate::RawString;
|
||||
use crate::Value;
|
||||
use crate::key::Key;
|
||||
use crate::parser::key::on_key;
|
||||
use crate::parser::prelude::*;
|
||||
use crate::parser::value::value;
|
||||
use crate::repr::Decor;
|
||||
use crate::{ArrayOfTables, Document, Table};
|
||||
|
||||
/// ```abnf
|
||||
/// ;; TOML
|
||||
///
|
||||
/// toml = expression *( newline expression )
|
||||
///
|
||||
/// expression = ( ( ws comment ) /
|
||||
/// ( ws keyval ws [ comment ] ) /
|
||||
/// ( ws table ws [ comment ] ) /
|
||||
/// ws )
|
||||
/// ```
|
||||
pub(crate) fn document<'s>(
|
||||
input: &mut Input<'_>,
|
||||
source: toml_parser::Source<'s>,
|
||||
errors: &mut dyn ErrorSink,
|
||||
) -> Document<&'s str> {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("document::document");
|
||||
let mut state = State::default();
|
||||
while let Some(event) = input.next_token() {
|
||||
match event.kind() {
|
||||
EventKind::InlineTableOpen
|
||||
| EventKind::InlineTableClose
|
||||
| EventKind::ArrayOpen
|
||||
| EventKind::ArrayClose
|
||||
| EventKind::Scalar
|
||||
| EventKind::ValueSep
|
||||
| EventKind::Error
|
||||
| EventKind::KeySep
|
||||
| EventKind::KeyValSep
|
||||
| EventKind::StdTableClose
|
||||
| EventKind::ArrayTableClose => {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("unexpected {event:?}"),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
EventKind::StdTableOpen | EventKind::ArrayTableOpen => {
|
||||
state.finish_table(errors);
|
||||
|
||||
let prefix = state.take_trailing();
|
||||
let header = on_table(event, input, source, errors);
|
||||
let suffix = ws_comment_newline(input)
|
||||
.map(|s| RawString::with_span(s.start()..s.end()))
|
||||
.unwrap_or_default();
|
||||
let decor = Decor::new(prefix, suffix);
|
||||
|
||||
state.start_table(header, decor, errors);
|
||||
}
|
||||
EventKind::SimpleKey => {
|
||||
let key_prefix = state.take_trailing();
|
||||
let (path, key) = on_key(event, input, source, errors);
|
||||
let Some(mut key) = key else {
|
||||
break;
|
||||
};
|
||||
let Some(next_event) = input.next_token() else {
|
||||
break;
|
||||
};
|
||||
let keyval_event;
|
||||
let key_suffix;
|
||||
if next_event.kind() == EventKind::Whitespace {
|
||||
key_suffix = Some(next_event);
|
||||
let Some(next_event) = input.next_token() else {
|
||||
break;
|
||||
};
|
||||
keyval_event = next_event;
|
||||
} else {
|
||||
key_suffix = None;
|
||||
keyval_event = next_event;
|
||||
}
|
||||
if keyval_event.kind() != EventKind::KeyValSep {
|
||||
break;
|
||||
}
|
||||
let key_suffix = key_suffix
|
||||
.map(|e| RawString::with_span(e.span().start()..e.span().end()))
|
||||
.unwrap_or_default();
|
||||
key.leaf_decor.set_prefix(key_prefix);
|
||||
key.leaf_decor.set_suffix(key_suffix);
|
||||
|
||||
let value_prefix = if input
|
||||
.first()
|
||||
.map(|e| e.kind() == EventKind::Whitespace)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
input
|
||||
.next_token()
|
||||
.map(|e| RawString::with_span(e.span().start()..e.span().end()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.unwrap_or_default();
|
||||
let mut value = value(input, source, errors);
|
||||
let value_suffix = ws_comment_newline(input)
|
||||
.map(|s| RawString::with_span(s.start()..s.end()))
|
||||
.unwrap_or_default();
|
||||
let decor = value.decor_mut();
|
||||
decor.set_prefix(value_prefix);
|
||||
decor.set_suffix(value_suffix);
|
||||
|
||||
state.capture_key_value(path, key, value, errors);
|
||||
}
|
||||
EventKind::Whitespace | EventKind::Comment | EventKind::Newline => {
|
||||
state.capture_trailing(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.finish_table(errors);
|
||||
|
||||
let trailing = state.take_trailing();
|
||||
Document {
|
||||
root: Item::Table(state.root),
|
||||
trailing,
|
||||
raw: source.input(),
|
||||
}
|
||||
}
|
||||
|
||||
/// ```abnf
|
||||
/// ;; Standard Table
|
||||
///
|
||||
/// std-table = std-table-open key *( table-key-sep key) std-table-close
|
||||
///
|
||||
/// ;; Array Table
|
||||
///
|
||||
/// array-table = array-table-open key *( table-key-sep key) array-table-close
|
||||
/// ```
|
||||
fn on_table(
|
||||
open_event: &toml_parser::parser::Event,
|
||||
input: &mut Input<'_>,
|
||||
source: toml_parser::Source<'_>,
|
||||
errors: &mut dyn ErrorSink,
|
||||
) -> TableHeader {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("document::on_table");
|
||||
let is_array = open_event.kind() == EventKind::ArrayTableOpen;
|
||||
let mut current_path = None;
|
||||
let mut current_key = None;
|
||||
let mut current_span = open_event.span();
|
||||
let mut current_prefix = None;
|
||||
let mut current_suffix = None;
|
||||
|
||||
while let Some(event) = input.next_token() {
|
||||
match event.kind() {
|
||||
EventKind::InlineTableOpen
|
||||
| EventKind::InlineTableClose
|
||||
| EventKind::ArrayOpen
|
||||
| EventKind::ArrayClose
|
||||
| EventKind::Scalar
|
||||
| EventKind::ValueSep
|
||||
| EventKind::Error
|
||||
| EventKind::KeySep
|
||||
| EventKind::KeyValSep
|
||||
| EventKind::StdTableOpen
|
||||
| EventKind::ArrayTableOpen
|
||||
| EventKind::Comment
|
||||
| EventKind::Newline => {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("unexpected {event:?}"),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
EventKind::ArrayTableClose | EventKind::StdTableClose => {
|
||||
current_span = current_span.append(event.span());
|
||||
break;
|
||||
}
|
||||
EventKind::SimpleKey => {
|
||||
current_prefix.get_or_insert_with(|| event.span().before());
|
||||
let (path, key) = on_key(event, input, source, errors);
|
||||
current_path = Some(path);
|
||||
current_key = key;
|
||||
current_suffix.get_or_insert_with(|| event.span().after());
|
||||
}
|
||||
EventKind::Whitespace => {
|
||||
if current_key.is_some() {
|
||||
current_suffix = Some(event.span());
|
||||
} else {
|
||||
current_prefix = Some(event.span());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let prefix = current_prefix
|
||||
.take()
|
||||
.expect("setting a key should set a prefix");
|
||||
let suffix = current_suffix
|
||||
.take()
|
||||
.expect("setting a key should set a suffix");
|
||||
if let Some(last_key) = current_key.as_mut() {
|
||||
let prefix = RawString::with_span(prefix.start()..prefix.end());
|
||||
let suffix = RawString::with_span(suffix.start()..suffix.end());
|
||||
let leaf_decor = Decor::new(prefix, suffix);
|
||||
*last_key.leaf_decor_mut() = leaf_decor;
|
||||
}
|
||||
|
||||
TableHeader {
|
||||
path: current_path.unwrap_or_default(),
|
||||
key: current_key,
|
||||
span: current_span,
|
||||
is_array,
|
||||
}
|
||||
}
|
||||
|
||||
struct TableHeader {
|
||||
path: Vec<Key>,
|
||||
key: Option<Key>,
|
||||
span: toml_parser::Span,
|
||||
is_array: bool,
|
||||
}
|
||||
|
||||
fn ws_comment_newline(input: &mut Input<'_>) -> Option<toml_parser::Span> {
|
||||
let mut current_span = None;
|
||||
while let Some(event) = input.next_token() {
|
||||
match event.kind() {
|
||||
EventKind::InlineTableOpen
|
||||
| EventKind::InlineTableClose
|
||||
| EventKind::ArrayOpen
|
||||
| EventKind::ArrayClose
|
||||
| EventKind::Scalar
|
||||
| EventKind::ValueSep
|
||||
| EventKind::Error
|
||||
| EventKind::SimpleKey
|
||||
| EventKind::KeySep
|
||||
| EventKind::KeyValSep
|
||||
| EventKind::StdTableOpen
|
||||
| EventKind::ArrayTableOpen
|
||||
| EventKind::StdTableClose
|
||||
| EventKind::ArrayTableClose => {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("unexpected {event:?}"),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
}
|
||||
EventKind::Whitespace | EventKind::Comment => {
|
||||
let span = current_span.get_or_insert_with(|| event.span());
|
||||
*span = span.append(event.span());
|
||||
}
|
||||
EventKind::Newline => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current_span
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct State {
|
||||
root: Table,
|
||||
current_table: Table,
|
||||
current_trailing: Option<toml_parser::Span>,
|
||||
current_header: Option<TableHeader>,
|
||||
current_position: isize,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn capture_trailing(&mut self, event: &toml_parser::parser::Event) {
|
||||
let decor = self.current_trailing.get_or_insert(event.span());
|
||||
*decor = decor.append(event.span());
|
||||
}
|
||||
|
||||
fn capture_key_value(
|
||||
&mut self,
|
||||
path: Vec<Key>,
|
||||
key: Key,
|
||||
value: Value,
|
||||
errors: &mut dyn ErrorSink,
|
||||
) {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("document::capture_key_value");
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!(
|
||||
"path={:?}",
|
||||
path.iter().map(|k| k.get()).collect::<Vec<_>>()
|
||||
),
|
||||
anstyle::AnsiColor::Blue.on_default(),
|
||||
);
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("key={key}",),
|
||||
anstyle::AnsiColor::Blue.on_default(),
|
||||
);
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("value={value:?}",),
|
||||
anstyle::AnsiColor::Blue.on_default(),
|
||||
);
|
||||
|
||||
let dotted = !path.is_empty();
|
||||
let Some(parent_table) = descend_path(&mut self.current_table, &path, dotted, errors)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
// "Likewise, using dotted keys to redefine tables already defined in [table] form is not allowed"
|
||||
let mixed_table_types = dotted && !parent_table.is_implicit();
|
||||
if mixed_table_types {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("dotted={dotted}"),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("parent_table.is_implicit={}", parent_table.is_implicit()),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
let key_span = get_key_span(&key).expect("all keys have spans");
|
||||
errors.report_error(ParseError::new("duplicate key").with_unexpected(key_span));
|
||||
return;
|
||||
}
|
||||
let key_span = get_key_span(&key).expect("all keys have spans");
|
||||
match parent_table.items.entry(key) {
|
||||
indexmap::map::Entry::Vacant(o) => {
|
||||
o.insert(Item::Value(value));
|
||||
}
|
||||
indexmap::map::Entry::Occupied(existing) => {
|
||||
// "Since tables cannot be defined more than once, redefining such tables using a [table] header is not allowed"
|
||||
let old_span = existing.key().span().expect("all items have spans");
|
||||
let old_span = toml_parser::Span::new_unchecked(old_span.start, old_span.end);
|
||||
errors.report_error(
|
||||
ParseError::new("duplicate key")
|
||||
.with_unexpected(key_span)
|
||||
.with_context(old_span),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn finish_table(&mut self, errors: &mut dyn ErrorSink) {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("document::finish_table");
|
||||
let mut prev_table = std::mem::take(&mut self.current_table);
|
||||
if let Some(header) = self.current_header.take() {
|
||||
let Some(key) = &header.key else {
|
||||
return;
|
||||
};
|
||||
prev_table.span = Some(header.span.start()..header.span.end());
|
||||
|
||||
let parent_key = &header.path;
|
||||
let dotted = false;
|
||||
let Some(parent_table) = descend_path(&mut self.root, parent_key, dotted, errors)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("key={key}",),
|
||||
anstyle::AnsiColor::Blue.on_default(),
|
||||
);
|
||||
if header.is_array {
|
||||
let entry = parent_table
|
||||
.entry_format(key)
|
||||
.or_insert(Item::ArrayOfTables(ArrayOfTables::new()));
|
||||
let Some(array) = entry.as_array_of_tables_mut() else {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
"is_array_of_tables=false",
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
let key_span = get_key_span(key).expect("all keys have spans");
|
||||
let old_span = entry.span().unwrap_or_default();
|
||||
let old_span = toml_parser::Span::new_unchecked(old_span.start, old_span.end);
|
||||
errors.report_error(
|
||||
ParseError::new("duplicate key")
|
||||
.with_unexpected(key_span)
|
||||
.with_context(old_span),
|
||||
);
|
||||
return;
|
||||
};
|
||||
array.push(prev_table);
|
||||
let span = if let (Some(first), Some(last)) = (
|
||||
array.values.first().and_then(|t| t.span()),
|
||||
array.values.last().and_then(|t| t.span()),
|
||||
) {
|
||||
Some((first.start)..(last.end))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
array.span = span;
|
||||
} else {
|
||||
let existing = parent_table.insert_formatted(key, Item::Table(prev_table));
|
||||
debug_assert!(existing.is_none());
|
||||
}
|
||||
} else {
|
||||
prev_table.span = Some(Default::default());
|
||||
self.root = prev_table;
|
||||
}
|
||||
}
|
||||
|
||||
fn start_table(&mut self, header: TableHeader, decor: Decor, errors: &mut dyn ErrorSink) {
|
||||
if !header.is_array {
|
||||
// 1. Look up the table on start to ensure the duplicate_key error points to the right line
|
||||
// 2. Ensure any child tables from an implicit table are preserved
|
||||
let root = &mut self.root;
|
||||
if let (Some(parent_table), Some(key)) =
|
||||
(descend_path(root, &header.path, false, errors), &header.key)
|
||||
{
|
||||
if let Some((old_key, old_value)) = parent_table.remove_entry(key.get()) {
|
||||
match old_value {
|
||||
Item::Table(t) if t.implicit && !t.is_dotted() => {
|
||||
self.current_table = t;
|
||||
}
|
||||
// Since tables cannot be defined more than once, redefining such tables using a [table] header is not allowed. Likewise, using dotted keys to redefine tables already defined in [table] form is not allowed.
|
||||
old_value => {
|
||||
#[cfg(feature = "debug")]
|
||||
if let Item::Table(t) = &old_value {
|
||||
trace(
|
||||
&format!("t.dotted={}", t.is_dotted()),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
trace(
|
||||
&format!("t.is_implicit={}", t.is_implicit()),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
} else {
|
||||
trace(
|
||||
&format!("old_value.type_str={}", old_value.type_name()),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
}
|
||||
let old_span = old_key.span().expect("all items have spans");
|
||||
let old_span =
|
||||
toml_parser::Span::new_unchecked(old_span.start, old_span.end);
|
||||
let key_span = get_key_span(key).expect("all keys have spans");
|
||||
errors.report_error(
|
||||
ParseError::new("duplicate key")
|
||||
.with_unexpected(key_span)
|
||||
.with_context(old_span),
|
||||
);
|
||||
|
||||
if let Item::Table(t) = old_value {
|
||||
self.current_table = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.current_position += 1;
|
||||
self.current_table.decor = decor;
|
||||
self.current_table.set_implicit(false);
|
||||
self.current_table.set_dotted(false);
|
||||
self.current_table.set_position(Some(self.current_position));
|
||||
self.current_table.span = Some(header.span.start()..header.span.end());
|
||||
self.current_header = Some(header);
|
||||
}
|
||||
|
||||
fn take_trailing(&mut self) -> RawString {
|
||||
self.current_trailing
|
||||
.take()
|
||||
.map(|s| RawString::with_span(s.start()..s.end()))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
fn descend_path<'t>(
|
||||
mut table: &'t mut Table,
|
||||
path: &[Key],
|
||||
dotted: bool,
|
||||
errors: &mut dyn ErrorSink,
|
||||
) -> Option<&'t mut Table> {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("document::descend_path");
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!(
|
||||
"path={:?}",
|
||||
path.iter().map(|k| k.get()).collect::<Vec<_>>()
|
||||
),
|
||||
anstyle::AnsiColor::Blue.on_default(),
|
||||
);
|
||||
for key in path.iter() {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("path[_]={key:?}"),
|
||||
anstyle::AnsiColor::Blue.on_default(),
|
||||
);
|
||||
table = match table.entry_format(key) {
|
||||
crate::Entry::Vacant(entry) => {
|
||||
let mut new_table = Table::new();
|
||||
new_table.span = key.span();
|
||||
new_table.set_implicit(true);
|
||||
new_table.set_dotted(dotted);
|
||||
|
||||
entry.insert(Item::Table(new_table)).as_table_mut().unwrap()
|
||||
}
|
||||
crate::Entry::Occupied(entry) => {
|
||||
match entry.into_mut() {
|
||||
Item::ArrayOfTables(array) => {
|
||||
debug_assert!(!array.is_empty());
|
||||
|
||||
let index = array.len() - 1;
|
||||
let last_child = array.get_mut(index).unwrap();
|
||||
|
||||
last_child
|
||||
}
|
||||
Item::Table(sweet_child_of_mine) => {
|
||||
if dotted && sweet_child_of_mine.is_implicit() {
|
||||
// Since tables cannot be defined more than once, redefining such tables using a
|
||||
// [table] header is not allowed. Likewise, using dotted keys to redefine tables
|
||||
// already defined in [table] form is not allowed.
|
||||
sweet_child_of_mine.set_dotted(true);
|
||||
}
|
||||
// Since tables cannot be defined more than once, redefining such tables using a
|
||||
// [table] header is not allowed. Likewise, using dotted keys to redefine tables
|
||||
// already defined in [table] form is not allowed.
|
||||
let mixed_table_types = dotted && !sweet_child_of_mine.is_implicit();
|
||||
if mixed_table_types {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("dotted={dotted}"),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!(
|
||||
"sweet_child_of_mine.is_implicit={}",
|
||||
sweet_child_of_mine.is_implicit()
|
||||
),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
let key_span = get_key_span(key).expect("all keys have spans");
|
||||
errors.report_error(
|
||||
ParseError::new("duplicate key").with_unexpected(key_span),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
sweet_child_of_mine
|
||||
}
|
||||
Item::Value(existing) => {
|
||||
let old_span = existing.span().expect("all items have spans");
|
||||
let old_span =
|
||||
toml_parser::Span::new_unchecked(old_span.start, old_span.end);
|
||||
let key_span = get_key_span(key).expect("all keys have spans");
|
||||
errors.report_error(
|
||||
ParseError::new(format!(
|
||||
"cannot extend value of type {} with a dotted key",
|
||||
existing.type_name()
|
||||
))
|
||||
.with_unexpected(key_span)
|
||||
.with_context(old_span),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
Item::None => unreachable!(),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Some(table)
|
||||
}
|
||||
|
||||
fn get_key_span(key: &Key) -> Option<toml_parser::Span> {
|
||||
key.as_repr()
|
||||
.and_then(|r| r.span())
|
||||
.map(|s| toml_parser::Span::new_unchecked(s.start, s.end))
|
||||
}
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
use crate::key::Key;
|
||||
use crate::parser::array::on_array;
|
||||
use crate::parser::key::on_key;
|
||||
use crate::parser::prelude::*;
|
||||
use crate::parser::value::on_scalar;
|
||||
use crate::repr::Decor;
|
||||
use crate::{InlineTable, Item, RawString, Value};
|
||||
|
||||
use indexmap::map::Entry;
|
||||
|
||||
/// ```abnf
|
||||
/// ;; Inline Table
|
||||
///
|
||||
/// inline-table = inline-table-open [ inline-table-keyvals ] ws-comment-newline inline-table-close
|
||||
/// ```
|
||||
pub(crate) fn on_inline_table(
|
||||
open_event: &toml_parser::parser::Event,
|
||||
input: &mut Input<'_>,
|
||||
source: toml_parser::Source<'_>,
|
||||
errors: &mut dyn ErrorSink,
|
||||
) -> Value {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("inline_table::on_inline_table");
|
||||
let mut result = InlineTable::new();
|
||||
|
||||
let mut state = State::default();
|
||||
state.open(open_event);
|
||||
while let Some(event) = input.next_token() {
|
||||
match event.kind() {
|
||||
EventKind::StdTableOpen
|
||||
| EventKind::ArrayTableOpen
|
||||
| EventKind::StdTableClose
|
||||
| EventKind::ArrayClose
|
||||
| EventKind::ArrayTableClose
|
||||
| EventKind::KeySep => {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("unexpected {event:?}"),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
break;
|
||||
}
|
||||
EventKind::Error => {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("unexpected {event:?}"),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
EventKind::SimpleKey => {
|
||||
let (path, key) = on_key(event, input, source, errors);
|
||||
state.capture_key(event, path, key);
|
||||
}
|
||||
EventKind::KeyValSep => {
|
||||
state.finish_key(event);
|
||||
}
|
||||
EventKind::InlineTableOpen => {
|
||||
let value = on_inline_table(event, input, source, errors);
|
||||
state.capture_value(event, value);
|
||||
}
|
||||
EventKind::ArrayOpen => {
|
||||
let value = on_array(event, input, source, errors);
|
||||
state.capture_value(event, value);
|
||||
}
|
||||
EventKind::Scalar => {
|
||||
let value = on_scalar(event, source, errors);
|
||||
state.capture_value(event, value);
|
||||
}
|
||||
EventKind::ValueSep => {
|
||||
state.finish_value(event, &mut result, errors);
|
||||
state.sep_value(event);
|
||||
}
|
||||
EventKind::Whitespace | EventKind::Comment | EventKind::Newline => {
|
||||
state.whitespace(event);
|
||||
}
|
||||
EventKind::InlineTableClose => {
|
||||
state.finish_value(event, &mut result, errors);
|
||||
state.close(open_event, event, &mut result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value::InlineTable(result)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct State {
|
||||
current_prefix: Option<toml_parser::Span>,
|
||||
current_key: Option<(Vec<Key>, Key)>,
|
||||
seen_keyval_sep: bool,
|
||||
current_value: Option<Value>,
|
||||
trailing_start: Option<usize>,
|
||||
current_suffix: Option<toml_parser::Span>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn open(&mut self, open_event: &toml_parser::parser::Event) {
|
||||
self.trailing_start = Some(open_event.span().end());
|
||||
}
|
||||
|
||||
fn whitespace(&mut self, event: &toml_parser::parser::Event) {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("inline_table::whitespace");
|
||||
let decor = if self.is_prefix() {
|
||||
self.current_prefix.get_or_insert(event.span())
|
||||
} else {
|
||||
self.current_suffix.get_or_insert(event.span())
|
||||
};
|
||||
*decor = decor.append(event.span());
|
||||
}
|
||||
|
||||
fn is_prefix(&self) -> bool {
|
||||
if self.seen_keyval_sep {
|
||||
self.current_value.is_none()
|
||||
} else {
|
||||
self.current_key.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
fn capture_key(
|
||||
&mut self,
|
||||
event: &toml_parser::parser::Event,
|
||||
path: Vec<Key>,
|
||||
key: Option<Key>,
|
||||
) {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("inline_table::capture_key");
|
||||
self.trailing_start = None;
|
||||
self.current_prefix
|
||||
.get_or_insert_with(|| event.span().before());
|
||||
if let Some(key) = key {
|
||||
self.current_key = Some((path, key));
|
||||
}
|
||||
}
|
||||
|
||||
fn finish_key(&mut self, event: &toml_parser::parser::Event) {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("inline_table::finish_key");
|
||||
self.seen_keyval_sep = true;
|
||||
if let Some(last_key) = self.current_key.as_mut().map(|(_, k)| k) {
|
||||
let prefix = self
|
||||
.current_prefix
|
||||
.take()
|
||||
.expect("setting a key should set a prefix");
|
||||
let suffix = self
|
||||
.current_suffix
|
||||
.take()
|
||||
.unwrap_or_else(|| event.span().before());
|
||||
let prefix = RawString::with_span(prefix.start()..prefix.end());
|
||||
let suffix = RawString::with_span(suffix.start()..suffix.end());
|
||||
let leaf_decor = Decor::new(prefix, suffix);
|
||||
*last_key.leaf_decor_mut() = leaf_decor;
|
||||
}
|
||||
}
|
||||
|
||||
fn capture_value(&mut self, event: &toml_parser::parser::Event, value: Value) {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("inline_table::capture_value");
|
||||
self.current_prefix
|
||||
.get_or_insert_with(|| event.span().before());
|
||||
self.current_value = Some(value);
|
||||
}
|
||||
|
||||
fn finish_value(
|
||||
&mut self,
|
||||
event: &toml_parser::parser::Event,
|
||||
result: &mut InlineTable,
|
||||
errors: &mut dyn ErrorSink,
|
||||
) {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("inline_table::finish_value");
|
||||
self.seen_keyval_sep = false;
|
||||
if let (Some((path, key)), Some(mut value)) =
|
||||
(self.current_key.take(), self.current_value.take())
|
||||
{
|
||||
let prefix = self
|
||||
.current_prefix
|
||||
.take()
|
||||
.expect("setting a value should set a prefix");
|
||||
let suffix = self
|
||||
.current_suffix
|
||||
.take()
|
||||
.unwrap_or_else(|| event.span().before());
|
||||
let Some(table) = descend_path(result, &path, true, errors) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let decor = value.decor_mut();
|
||||
decor.set_prefix(RawString::with_span(prefix.start()..prefix.end()));
|
||||
decor.set_suffix(RawString::with_span(suffix.start()..suffix.end()));
|
||||
|
||||
// "Likewise, using dotted keys to redefine tables already defined in [table] form is not allowed"
|
||||
let mixed_table_types = table.is_dotted() == path.is_empty();
|
||||
if mixed_table_types {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("table.dotted={}", table.is_dotted()),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("path.is_empty={}", path.is_empty()),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
let key_span = get_key_span(&key).unwrap_or_else(|| event.span());
|
||||
errors.report_error(ParseError::new("duplicate key").with_unexpected(key_span));
|
||||
} else {
|
||||
let key_span = get_key_span(&key).unwrap_or_else(|| event.span());
|
||||
match table.items.entry(key) {
|
||||
Entry::Vacant(o) => {
|
||||
o.insert(Item::Value(value));
|
||||
}
|
||||
Entry::Occupied(o) => {
|
||||
let old_span = get_key_span(o.key()).unwrap_or_else(|| event.span());
|
||||
errors.report_error(
|
||||
ParseError::new("duplicate key")
|
||||
.with_unexpected(key_span)
|
||||
.with_context(old_span),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sep_value(&mut self, event: &toml_parser::parser::Event) {
|
||||
self.trailing_start = Some(event.span().end());
|
||||
}
|
||||
|
||||
fn close(
|
||||
&mut self,
|
||||
open_event: &toml_parser::parser::Event,
|
||||
close_event: &toml_parser::parser::Event,
|
||||
result: &mut InlineTable,
|
||||
) {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("inline_table::close");
|
||||
let trailing_comma = self.trailing_start.is_some() && !result.is_empty();
|
||||
let span = open_event.span().append(close_event.span());
|
||||
let trailing_start = self
|
||||
.trailing_start
|
||||
.unwrap_or_else(|| close_event.span().start());
|
||||
let trailing_end = close_event.span().start();
|
||||
|
||||
result.set_trailing_comma(trailing_comma);
|
||||
result.set_trailing(RawString::with_span(trailing_start..trailing_end));
|
||||
result.span = Some(span.start()..span.end());
|
||||
}
|
||||
}
|
||||
|
||||
fn descend_path<'a>(
|
||||
mut table: &'a mut InlineTable,
|
||||
path: &'a [Key],
|
||||
dotted: bool,
|
||||
errors: &mut dyn ErrorSink,
|
||||
) -> Option<&'a mut InlineTable> {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("inline_table::descend_path");
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!(
|
||||
"path={:?}",
|
||||
path.iter().map(|k| k.get()).collect::<Vec<_>>()
|
||||
),
|
||||
anstyle::AnsiColor::Blue.on_default(),
|
||||
);
|
||||
for key in path.iter() {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("path[_]={key:?}"),
|
||||
anstyle::AnsiColor::Blue.on_default(),
|
||||
);
|
||||
table = match table.entry_format(key) {
|
||||
crate::InlineEntry::Vacant(entry) => {
|
||||
let mut new_table = InlineTable::new();
|
||||
new_table.span = key.span();
|
||||
new_table.set_implicit(true);
|
||||
new_table.set_dotted(dotted);
|
||||
entry
|
||||
.insert(Value::InlineTable(new_table))
|
||||
.as_inline_table_mut()
|
||||
.unwrap()
|
||||
}
|
||||
crate::InlineEntry::Occupied(entry) => {
|
||||
match entry.into_mut() {
|
||||
Value::InlineTable(sweet_child_of_mine) => {
|
||||
// Since tables cannot be defined more than once, redefining such tables using a
|
||||
// [table] header is not allowed. Likewise, using dotted keys to redefine tables
|
||||
// already defined in [table] form is not allowed.
|
||||
let mixed_table_types = dotted && !sweet_child_of_mine.is_implicit();
|
||||
if mixed_table_types {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("dotted={dotted}"),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!(
|
||||
"sweet_child_of_mine.is_implicit={}",
|
||||
sweet_child_of_mine.is_implicit()
|
||||
),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
let key_span = get_key_span(key).expect("all keys have spans");
|
||||
errors.report_error(
|
||||
ParseError::new("duplicate key").with_unexpected(key_span),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
sweet_child_of_mine
|
||||
}
|
||||
existing => {
|
||||
let key_span = get_key_span(key).expect("all keys have spans");
|
||||
errors.report_error(
|
||||
ParseError::new(format!(
|
||||
"cannot extend value of type {} with a dotted key",
|
||||
existing.type_name()
|
||||
))
|
||||
.with_unexpected(key_span),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Some(table)
|
||||
}
|
||||
|
||||
fn get_key_span(key: &Key) -> Option<toml_parser::Span> {
|
||||
key.as_repr()
|
||||
.and_then(|r| r.span())
|
||||
.map(|s| toml_parser::Span::new_unchecked(s.start, s.end))
|
||||
}
|
||||
Vendored
+168
@@ -0,0 +1,168 @@
|
||||
use crate::RawString;
|
||||
use crate::key::Key;
|
||||
use crate::parser::prelude::*;
|
||||
use crate::repr::Decor;
|
||||
use crate::repr::Repr;
|
||||
|
||||
/// ```abnf
|
||||
/// key = simple-key / dotted-key
|
||||
/// dotted-key = simple-key 1*( dot-sep simple-key )
|
||||
/// ```
|
||||
pub(crate) fn on_key(
|
||||
key_event: &toml_parser::parser::Event,
|
||||
input: &mut Input<'_>,
|
||||
source: toml_parser::Source<'_>,
|
||||
errors: &mut dyn ErrorSink,
|
||||
) -> (Vec<Key>, Option<Key>) {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("key::on_key");
|
||||
let mut result_path = Vec::new();
|
||||
let mut result_key = None;
|
||||
|
||||
let mut state = State::new(key_event);
|
||||
if more_key(input) {
|
||||
while let Some(event) = input.next_token() {
|
||||
match event.kind() {
|
||||
EventKind::StdTableOpen
|
||||
| EventKind::ArrayTableOpen
|
||||
| EventKind::InlineTableOpen
|
||||
| EventKind::InlineTableClose
|
||||
| EventKind::ArrayOpen
|
||||
| EventKind::ArrayClose
|
||||
| EventKind::Scalar
|
||||
| EventKind::ValueSep
|
||||
| EventKind::Comment
|
||||
| EventKind::Newline
|
||||
| EventKind::KeyValSep
|
||||
| EventKind::StdTableClose
|
||||
| EventKind::ArrayTableClose
|
||||
| EventKind::Error => {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("unexpected {event:?}"),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
EventKind::SimpleKey => {
|
||||
state.current_key = Some(*event);
|
||||
|
||||
if !more_key(input) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
EventKind::Whitespace => {
|
||||
state.whitespace(event);
|
||||
}
|
||||
EventKind::KeySep => {
|
||||
state.close_key(&mut result_path, &mut result_key, source, errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.close_key(&mut result_path, &mut result_key, source, errors);
|
||||
|
||||
#[cfg(not(feature = "unbounded"))]
|
||||
if super::LIMIT <= result_path.len() as u32 {
|
||||
errors.report_error(ParseError::new("recursion limit"));
|
||||
return (Vec::new(), None);
|
||||
}
|
||||
|
||||
(result_path, result_key)
|
||||
}
|
||||
|
||||
fn more_key(input: &Input<'_>) -> bool {
|
||||
let first = input.get(0).map(|e| e.kind());
|
||||
let second = input.get(1).map(|e| e.kind());
|
||||
if first == Some(EventKind::KeySep) {
|
||||
true
|
||||
} else if first == Some(EventKind::Whitespace) && second == Some(EventKind::KeySep) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
current_prefix: Option<toml_parser::Span>,
|
||||
current_key: Option<toml_parser::parser::Event>,
|
||||
current_suffix: Option<toml_parser::Span>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn new(key_event: &toml_parser::parser::Event) -> Self {
|
||||
Self {
|
||||
current_prefix: None,
|
||||
current_key: Some(*key_event),
|
||||
current_suffix: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn whitespace(&mut self, event: &toml_parser::parser::Event) {
|
||||
if self.current_key.is_some() {
|
||||
self.current_suffix = Some(event.span());
|
||||
} else {
|
||||
self.current_prefix = Some(event.span());
|
||||
}
|
||||
}
|
||||
|
||||
fn close_key(
|
||||
&mut self,
|
||||
result_path: &mut Vec<Key>,
|
||||
result_key: &mut Option<Key>,
|
||||
source: toml_parser::Source<'_>,
|
||||
errors: &mut dyn ErrorSink,
|
||||
) {
|
||||
let Some(key) = self.current_key.take() else {
|
||||
return;
|
||||
};
|
||||
let prefix_span = self
|
||||
.current_prefix
|
||||
.take()
|
||||
.unwrap_or_else(|| key.span().before());
|
||||
let prefix = RawString::with_span(prefix_span.start()..prefix_span.end());
|
||||
|
||||
let suffix_span = self
|
||||
.current_suffix
|
||||
.take()
|
||||
.unwrap_or_else(|| key.span().after());
|
||||
let suffix = RawString::with_span(suffix_span.start()..suffix_span.end());
|
||||
|
||||
let key_span = key.span();
|
||||
let key_raw = RawString::with_span(key_span.start()..key_span.end());
|
||||
|
||||
let raw = source.get(key).unwrap();
|
||||
let mut decoded = std::borrow::Cow::Borrowed("");
|
||||
raw.decode_key(&mut decoded, errors);
|
||||
|
||||
let key = Key::new(decoded)
|
||||
.with_repr_unchecked(Repr::new_unchecked(key_raw))
|
||||
.with_dotted_decor(Decor::new(prefix, suffix));
|
||||
if let Some(last_key) = result_key.replace(key) {
|
||||
result_path.push(last_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ```abnf
|
||||
/// simple-key = quoted-key / unquoted-key
|
||||
/// quoted-key = basic-string / literal-string
|
||||
/// ```
|
||||
pub(crate) fn on_simple_key(
|
||||
event: &toml_parser::parser::Event,
|
||||
source: toml_parser::Source<'_>,
|
||||
errors: &mut dyn ErrorSink,
|
||||
) -> (RawString, String) {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("key::on_simple_key");
|
||||
let raw = source.get(event).unwrap();
|
||||
|
||||
let mut key = std::borrow::Cow::Borrowed("");
|
||||
raw.decode_key(&mut key, errors);
|
||||
|
||||
let span = event.span();
|
||||
let raw = RawString::with_span(span.start()..span.end());
|
||||
let key = String::from(key);
|
||||
(raw, key)
|
||||
}
|
||||
Vendored
+156
@@ -0,0 +1,156 @@
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
use crate::RawString;
|
||||
#[cfg(not(feature = "unbounded"))]
|
||||
use toml_parser::parser::RecursionGuard;
|
||||
use toml_parser::parser::ValidateWhitespace;
|
||||
use winnow::stream::Stream as _;
|
||||
|
||||
pub(crate) mod array;
|
||||
#[cfg(feature = "debug")]
|
||||
pub(crate) mod debug;
|
||||
pub(crate) mod document;
|
||||
pub(crate) mod inline_table;
|
||||
pub(crate) mod key;
|
||||
pub(crate) mod value;
|
||||
|
||||
pub(crate) fn parse_document<'s>(
|
||||
source: toml_parser::Source<'s>,
|
||||
errors: &mut dyn prelude::ErrorSink,
|
||||
) -> crate::Document<&'s str> {
|
||||
let tokens = source.lex().into_vec();
|
||||
|
||||
let mut events = Vec::with_capacity(tokens.len());
|
||||
let mut receiver = ValidateWhitespace::new(&mut events, source);
|
||||
#[cfg(not(feature = "unbounded"))]
|
||||
let mut receiver = RecursionGuard::new(&mut receiver, LIMIT);
|
||||
#[cfg(not(feature = "unbounded"))]
|
||||
let receiver = &mut receiver;
|
||||
#[cfg(feature = "unbounded")]
|
||||
let receiver = &mut receiver;
|
||||
toml_parser::parser::parse_document(&tokens, receiver, errors);
|
||||
|
||||
let mut input = prelude::Input::new(&events);
|
||||
let doc = document::document(&mut input, source, errors);
|
||||
doc
|
||||
}
|
||||
|
||||
pub(crate) fn parse_key(
|
||||
source: toml_parser::Source<'_>,
|
||||
errors: &mut dyn prelude::ErrorSink,
|
||||
) -> crate::Key {
|
||||
let tokens = source.lex().into_vec();
|
||||
|
||||
let mut events = Vec::with_capacity(tokens.len());
|
||||
let mut receiver = ValidateWhitespace::new(&mut events, source);
|
||||
#[cfg(not(feature = "unbounded"))]
|
||||
let mut receiver = RecursionGuard::new(&mut receiver, LIMIT);
|
||||
#[cfg(not(feature = "unbounded"))]
|
||||
let receiver = &mut receiver;
|
||||
#[cfg(feature = "unbounded")]
|
||||
let receiver = &mut receiver;
|
||||
toml_parser::parser::parse_simple_key(&tokens, receiver, errors);
|
||||
|
||||
if let Some(event) = events
|
||||
.iter()
|
||||
.find(|e| e.kind() == toml_parser::parser::EventKind::SimpleKey)
|
||||
{
|
||||
let (raw, key) = key::on_simple_key(event, source, errors);
|
||||
crate::Key::new(key).with_repr_unchecked(crate::Repr::new_unchecked(raw))
|
||||
} else {
|
||||
let key = source.input();
|
||||
let raw = RawString::with_span(0..source.input().len());
|
||||
crate::Key::new(key).with_repr_unchecked(crate::Repr::new_unchecked(raw))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_key_path(
|
||||
source: toml_parser::Source<'_>,
|
||||
errors: &mut dyn prelude::ErrorSink,
|
||||
) -> Vec<crate::Key> {
|
||||
let tokens = source.lex().into_vec();
|
||||
|
||||
let mut events = Vec::with_capacity(tokens.len());
|
||||
let mut receiver = ValidateWhitespace::new(&mut events, source);
|
||||
#[cfg(not(feature = "unbounded"))]
|
||||
let mut receiver = RecursionGuard::new(&mut receiver, LIMIT);
|
||||
#[cfg(not(feature = "unbounded"))]
|
||||
let receiver = &mut receiver;
|
||||
#[cfg(feature = "unbounded")]
|
||||
let receiver = &mut receiver;
|
||||
toml_parser::parser::parse_key(&tokens, receiver, errors);
|
||||
|
||||
let mut input = prelude::Input::new(&events);
|
||||
let mut prefix = None;
|
||||
let mut path = None;
|
||||
let mut key = None;
|
||||
let mut suffix = None;
|
||||
while let Some(event) = input.next_token() {
|
||||
match event.kind() {
|
||||
toml_parser::parser::EventKind::Whitespace => {
|
||||
let raw = RawString::with_span(event.span().start()..event.span().end());
|
||||
if prefix.is_none() {
|
||||
prefix = Some(raw);
|
||||
} else if suffix.is_none() {
|
||||
suffix = Some(raw);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let (local_path, local_key) = key::on_key(event, &mut input, source, errors);
|
||||
path = Some(local_path);
|
||||
key = local_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(mut key) = key {
|
||||
if let Some(prefix) = prefix {
|
||||
key.leaf_decor.set_prefix(prefix);
|
||||
}
|
||||
if let Some(suffix) = suffix {
|
||||
key.leaf_decor.set_suffix(suffix);
|
||||
}
|
||||
let mut path = path.unwrap_or_default();
|
||||
path.push(key);
|
||||
path
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_value(
|
||||
source: toml_parser::Source<'_>,
|
||||
errors: &mut dyn prelude::ErrorSink,
|
||||
) -> crate::Value {
|
||||
let tokens = source.lex().into_vec();
|
||||
|
||||
let mut events = Vec::with_capacity(tokens.len());
|
||||
let mut receiver = ValidateWhitespace::new(&mut events, source);
|
||||
#[cfg(not(feature = "unbounded"))]
|
||||
let mut receiver = RecursionGuard::new(&mut receiver, LIMIT);
|
||||
#[cfg(not(feature = "unbounded"))]
|
||||
let receiver = &mut receiver;
|
||||
#[cfg(feature = "unbounded")]
|
||||
let receiver = &mut receiver;
|
||||
toml_parser::parser::parse_value(&tokens, receiver, errors);
|
||||
|
||||
let mut input = prelude::Input::new(&events);
|
||||
let value = value::value(&mut input, source, errors);
|
||||
value
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unbounded"))]
|
||||
const LIMIT: u32 = 80;
|
||||
|
||||
pub(crate) mod prelude {
|
||||
pub(crate) use toml_parser::ErrorSink;
|
||||
pub(crate) use toml_parser::ParseError;
|
||||
pub(crate) use toml_parser::parser::EventKind;
|
||||
pub(crate) use winnow::stream::Stream as _;
|
||||
|
||||
pub(crate) type Input<'i> = winnow::stream::TokenSlice<'i, toml_parser::parser::Event>;
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
pub(crate) use super::debug::TraceScope;
|
||||
#[cfg(feature = "debug")]
|
||||
pub(crate) use super::debug::trace;
|
||||
}
|
||||
Vendored
+150
@@ -0,0 +1,150 @@
|
||||
use crate::RawString;
|
||||
use crate::Value;
|
||||
use crate::parser::array::on_array;
|
||||
use crate::parser::inline_table::on_inline_table;
|
||||
use crate::parser::prelude::*;
|
||||
use crate::repr::{Formatted, Repr};
|
||||
|
||||
/// ```abnf
|
||||
/// val = string / boolean / array / inline-table / date-time / float / integer
|
||||
/// ```
|
||||
pub(crate) fn value(
|
||||
input: &mut Input<'_>,
|
||||
source: toml_parser::Source<'_>,
|
||||
errors: &mut dyn ErrorSink,
|
||||
) -> Value {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("value");
|
||||
if let Some(event) = input.next_token() {
|
||||
match event.kind() {
|
||||
EventKind::StdTableOpen
|
||||
| EventKind::ArrayTableOpen
|
||||
| EventKind::InlineTableClose
|
||||
| EventKind::ArrayClose
|
||||
| EventKind::ValueSep
|
||||
| EventKind::Comment
|
||||
| EventKind::Newline
|
||||
| EventKind::Error
|
||||
| EventKind::SimpleKey
|
||||
| EventKind::KeySep
|
||||
| EventKind::KeyValSep
|
||||
| EventKind::StdTableClose
|
||||
| EventKind::ArrayTableClose => {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("unexpected {event:?}"),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
}
|
||||
EventKind::Whitespace => {
|
||||
#[cfg(feature = "debug")]
|
||||
trace(
|
||||
&format!("unexpected {event:?}"),
|
||||
anstyle::AnsiColor::Red.on_default(),
|
||||
);
|
||||
}
|
||||
EventKind::InlineTableOpen => {
|
||||
return on_inline_table(event, input, source, errors);
|
||||
}
|
||||
EventKind::ArrayOpen => {
|
||||
return on_array(event, input, source, errors);
|
||||
}
|
||||
EventKind::Scalar => {
|
||||
return on_scalar(event, source, errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let value_raw = RawString::with_span(0..0);
|
||||
let mut f = Formatted::new(0);
|
||||
f.set_repr_unchecked(Repr::new_unchecked(value_raw));
|
||||
Value::Integer(f)
|
||||
}
|
||||
|
||||
pub(crate) fn on_scalar(
|
||||
event: &toml_parser::parser::Event,
|
||||
source: toml_parser::Source<'_>,
|
||||
errors: &mut dyn ErrorSink,
|
||||
) -> Value {
|
||||
#[cfg(feature = "debug")]
|
||||
let _scope = TraceScope::new("on_scalar");
|
||||
let value_span = event.span();
|
||||
let value_raw = RawString::with_span(value_span.start()..value_span.end());
|
||||
|
||||
let raw = source.get(event).unwrap();
|
||||
let mut decoded = std::borrow::Cow::Borrowed("");
|
||||
let kind = raw.decode_scalar(&mut decoded, errors);
|
||||
match kind {
|
||||
toml_parser::decoder::ScalarKind::String => {
|
||||
let mut f = Formatted::new(decoded.into());
|
||||
f.set_repr_unchecked(Repr::new_unchecked(value_raw));
|
||||
Value::String(f)
|
||||
}
|
||||
toml_parser::decoder::ScalarKind::Boolean(value) => {
|
||||
let mut f = Formatted::new(value);
|
||||
f.set_repr_unchecked(Repr::new_unchecked(value_raw));
|
||||
Value::Boolean(f)
|
||||
}
|
||||
toml_parser::decoder::ScalarKind::DateTime => {
|
||||
let value = match decoded.parse::<toml_datetime::Datetime>() {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
errors.report_error(
|
||||
ParseError::new(err.to_string()).with_unexpected(event.span()),
|
||||
);
|
||||
toml_datetime::Datetime {
|
||||
date: None,
|
||||
time: None,
|
||||
offset: None,
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut f = Formatted::new(value);
|
||||
f.set_repr_unchecked(Repr::new_unchecked(value_raw));
|
||||
Value::Datetime(f)
|
||||
}
|
||||
toml_parser::decoder::ScalarKind::Float => {
|
||||
let value = match decoded.parse::<f64>() {
|
||||
Ok(value) => {
|
||||
if value.is_infinite()
|
||||
&& !(decoded
|
||||
.strip_prefix(['+', '-'])
|
||||
.unwrap_or(&decoded)
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphabetic()))
|
||||
{
|
||||
errors.report_error(
|
||||
ParseError::new("floating-point number overflowed")
|
||||
.with_unexpected(event.span()),
|
||||
);
|
||||
}
|
||||
value
|
||||
}
|
||||
Err(_) => {
|
||||
errors.report_error(
|
||||
ParseError::new(kind.invalid_description()).with_unexpected(event.span()),
|
||||
);
|
||||
f64::NAN
|
||||
}
|
||||
};
|
||||
let mut f = Formatted::new(value);
|
||||
f.set_repr_unchecked(Repr::new_unchecked(value_raw));
|
||||
Value::Float(f)
|
||||
}
|
||||
toml_parser::decoder::ScalarKind::Integer(radix) => {
|
||||
let value = match i64::from_str_radix(&decoded, radix.value()) {
|
||||
Ok(value) => value,
|
||||
Err(_) => {
|
||||
// Assuming the decoder fully validated it, leaving only overflow errors
|
||||
errors.report_error(
|
||||
ParseError::new("integer number overflowed").with_unexpected(event.span()),
|
||||
);
|
||||
i64::MAX
|
||||
}
|
||||
};
|
||||
let mut f = Formatted::new(value);
|
||||
f.set_repr_unchecked(Repr::new_unchecked(value_raw));
|
||||
Value::Integer(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+168
@@ -0,0 +1,168 @@
|
||||
/// Opaque string storage for raw TOML; internal to `toml_edit`
|
||||
#[derive(PartialEq, Eq, Clone, Hash)]
|
||||
pub struct RawString(RawStringInner);
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Hash)]
|
||||
enum RawStringInner {
|
||||
Empty,
|
||||
Explicit(String),
|
||||
Spanned(std::ops::Range<usize>),
|
||||
}
|
||||
|
||||
impl RawString {
|
||||
pub(crate) fn with_span(span: std::ops::Range<usize>) -> Self {
|
||||
Self(RawStringInner::Spanned(span))
|
||||
}
|
||||
|
||||
/// Access the underlying string
|
||||
///
|
||||
/// This generally requires a [`DocumentMut`][crate::DocumentMut].
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
match &self.0 {
|
||||
RawStringInner::Empty => Some(""),
|
||||
RawStringInner::Explicit(s) => Some(s.as_str()),
|
||||
RawStringInner::Spanned(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The location within the original document
|
||||
///
|
||||
/// This generally requires a [`Document`][crate::Document].
|
||||
pub fn span(&self) -> Option<std::ops::Range<usize>> {
|
||||
match &self.0 {
|
||||
RawStringInner::Empty => None,
|
||||
RawStringInner::Explicit(_) => None,
|
||||
RawStringInner::Spanned(span) => Some(span.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_str<'s>(&'s self, input: &'s str) -> &'s str {
|
||||
match &self.0 {
|
||||
RawStringInner::Empty => "",
|
||||
RawStringInner::Explicit(s) => s.as_str(),
|
||||
RawStringInner::Spanned(span) => input
|
||||
.get(span.clone())
|
||||
.unwrap_or_else(|| panic!("span {span:?} should be in input:\n```\n{input}\n```")),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_str_with_default<'s>(
|
||||
&'s self,
|
||||
input: Option<&'s str>,
|
||||
default: &'s str,
|
||||
) -> &'s str {
|
||||
match &self.0 {
|
||||
RawStringInner::Empty => "",
|
||||
RawStringInner::Explicit(s) => s.as_str(),
|
||||
RawStringInner::Spanned(span) => {
|
||||
if let Some(input) = input {
|
||||
input.get(span.clone()).unwrap_or_else(|| {
|
||||
panic!("span {span:?} should be in input:\n```\n{input}\n```")
|
||||
})
|
||||
} else {
|
||||
default
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn despan(&mut self, input: &str) {
|
||||
match &self.0 {
|
||||
RawStringInner::Empty => {}
|
||||
RawStringInner::Explicit(_) => {}
|
||||
RawStringInner::Spanned(span) => {
|
||||
if span.start == span.end {
|
||||
*self = Self(RawStringInner::Empty);
|
||||
} else {
|
||||
*self = Self::from(input.get(span.clone()).unwrap_or_else(|| {
|
||||
panic!("span {span:?} should be in input:\n```\n{input}\n```")
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
pub(crate) fn encode(&self, buf: &mut dyn std::fmt::Write, input: &str) -> std::fmt::Result {
|
||||
let raw = self.to_str(input);
|
||||
for part in raw.split('\r') {
|
||||
write!(buf, "{part}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
pub(crate) fn encode_with_default(
|
||||
&self,
|
||||
buf: &mut dyn std::fmt::Write,
|
||||
input: Option<&str>,
|
||||
default: &str,
|
||||
) -> std::fmt::Result {
|
||||
let raw = self.to_str_with_default(input, default);
|
||||
for part in raw.split('\r') {
|
||||
write!(buf, "{part}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RawString {
|
||||
fn default() -> Self {
|
||||
Self(RawStringInner::Empty)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for RawString {
|
||||
#[inline]
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
match &self.0 {
|
||||
RawStringInner::Empty => write!(formatter, "empty"),
|
||||
RawStringInner::Explicit(s) => write!(formatter, "{s:?}"),
|
||||
RawStringInner::Spanned(s) => write!(formatter, "{s:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for RawString {
|
||||
#[inline]
|
||||
fn from(s: &str) -> Self {
|
||||
if s.is_empty() {
|
||||
Self(RawStringInner::Empty)
|
||||
} else {
|
||||
String::from(s).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for RawString {
|
||||
#[inline]
|
||||
fn from(s: String) -> Self {
|
||||
if s.is_empty() {
|
||||
Self(RawStringInner::Empty)
|
||||
} else {
|
||||
Self(RawStringInner::Explicit(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&String> for RawString {
|
||||
#[inline]
|
||||
fn from(s: &String) -> Self {
|
||||
if s.is_empty() {
|
||||
Self(RawStringInner::Empty)
|
||||
} else {
|
||||
String::from(s).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<str>> for RawString {
|
||||
#[inline]
|
||||
fn from(s: Box<str>) -> Self {
|
||||
if s.is_empty() {
|
||||
Self(RawStringInner::Empty)
|
||||
} else {
|
||||
String::from(s).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+281
@@ -0,0 +1,281 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::RawString;
|
||||
|
||||
/// A scalar TOML [`Value`][crate::Value]'s logical value and its representation in a `&str`
|
||||
///
|
||||
/// This includes the surrounding whitespace and comments.
|
||||
#[derive(Eq, PartialEq, Clone, Hash)]
|
||||
pub struct Formatted<T> {
|
||||
value: T,
|
||||
repr: Option<Repr>,
|
||||
decor: Decor,
|
||||
}
|
||||
|
||||
impl<T> Formatted<T>
|
||||
where
|
||||
T: ValueRepr,
|
||||
{
|
||||
/// Default-formatted value
|
||||
pub fn new(value: T) -> Self {
|
||||
Self {
|
||||
value,
|
||||
repr: None,
|
||||
decor: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_repr_unchecked(&mut self, repr: Repr) {
|
||||
self.repr = Some(repr);
|
||||
}
|
||||
|
||||
/// The wrapped value
|
||||
pub fn value(&self) -> &T {
|
||||
&self.value
|
||||
}
|
||||
|
||||
/// The wrapped value
|
||||
pub fn into_value(self) -> T {
|
||||
self.value
|
||||
}
|
||||
|
||||
/// Returns the raw representation, if available.
|
||||
pub fn as_repr(&self) -> Option<&Repr> {
|
||||
self.repr.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the default raw representation.
|
||||
#[cfg(feature = "display")]
|
||||
pub fn default_repr(&self) -> Repr {
|
||||
self.value.to_repr()
|
||||
}
|
||||
|
||||
/// Returns a raw representation.
|
||||
#[cfg(feature = "display")]
|
||||
pub fn display_repr(&self) -> Cow<'_, str> {
|
||||
self.as_repr()
|
||||
.and_then(|r| r.as_raw().as_str())
|
||||
.map(Cow::Borrowed)
|
||||
.unwrap_or_else(|| {
|
||||
Cow::Owned(self.default_repr().as_raw().as_str().unwrap().to_owned())
|
||||
})
|
||||
}
|
||||
|
||||
/// The location within the original document
|
||||
///
|
||||
/// This generally requires a [`Document`][crate::Document].
|
||||
pub fn span(&self) -> Option<std::ops::Range<usize>> {
|
||||
self.repr.as_ref().and_then(|r| r.span())
|
||||
}
|
||||
|
||||
pub(crate) fn despan(&mut self, input: &str) {
|
||||
self.decor.despan(input);
|
||||
if let Some(repr) = &mut self.repr {
|
||||
repr.despan(input);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace
|
||||
pub fn decor_mut(&mut self) -> &mut Decor {
|
||||
&mut self.decor
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace
|
||||
pub fn decor(&self) -> &Decor {
|
||||
&self.decor
|
||||
}
|
||||
|
||||
/// Auto formats the value.
|
||||
pub fn fmt(&mut self) {
|
||||
self.repr = None;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::fmt::Debug for Formatted<T>
|
||||
where
|
||||
T: std::fmt::Debug,
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
let mut d = formatter.debug_struct("Formatted");
|
||||
d.field("value", &self.value);
|
||||
match &self.repr {
|
||||
Some(r) => d.field("repr", r),
|
||||
None => d.field("repr", &"default"),
|
||||
};
|
||||
d.field("decor", &self.decor);
|
||||
d.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
impl<T> std::fmt::Display for Formatted<T>
|
||||
where
|
||||
T: ValueRepr,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
crate::encode::encode_formatted(self, f, None, ("", ""))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ValueRepr: crate::private::Sealed {
|
||||
/// The TOML representation of the value
|
||||
#[cfg(feature = "display")]
|
||||
fn to_repr(&self) -> Repr;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "display"))]
|
||||
mod inner {
|
||||
use super::ValueRepr;
|
||||
|
||||
impl ValueRepr for String {}
|
||||
impl ValueRepr for i64 {}
|
||||
impl ValueRepr for f64 {}
|
||||
impl ValueRepr for bool {}
|
||||
impl ValueRepr for toml_datetime::Datetime {}
|
||||
}
|
||||
|
||||
/// A TOML [`Value`][crate::Value] encoded as a `&str`
|
||||
#[derive(Eq, PartialEq, Clone, Hash)]
|
||||
pub struct Repr {
|
||||
raw_value: RawString,
|
||||
}
|
||||
|
||||
impl Repr {
|
||||
pub(crate) fn new_unchecked(raw: impl Into<RawString>) -> Self {
|
||||
Self {
|
||||
raw_value: raw.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the underlying value
|
||||
pub fn as_raw(&self) -> &RawString {
|
||||
&self.raw_value
|
||||
}
|
||||
|
||||
/// The location within the original document
|
||||
///
|
||||
/// This generally requires a [`Document`][crate::Document].
|
||||
pub fn span(&self) -> Option<std::ops::Range<usize>> {
|
||||
self.raw_value.span()
|
||||
}
|
||||
|
||||
pub(crate) fn despan(&mut self, input: &str) {
|
||||
self.raw_value.despan(input);
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
pub(crate) fn encode(&self, buf: &mut dyn std::fmt::Write, input: &str) -> std::fmt::Result {
|
||||
self.as_raw().encode(buf, input)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Repr {
|
||||
#[inline]
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
self.raw_value.fmt(formatter)
|
||||
}
|
||||
}
|
||||
|
||||
/// A prefix and suffix,
|
||||
///
|
||||
/// Including comments, whitespaces and newlines.
|
||||
#[derive(Eq, PartialEq, Clone, Default, Hash)]
|
||||
pub struct Decor {
|
||||
prefix: Option<RawString>,
|
||||
suffix: Option<RawString>,
|
||||
}
|
||||
|
||||
impl Decor {
|
||||
pub(crate) const EMPTY: Self = Self {
|
||||
prefix: None,
|
||||
suffix: None,
|
||||
};
|
||||
|
||||
/// Creates a new decor from the given prefix and suffix.
|
||||
pub fn new(prefix: impl Into<RawString>, suffix: impl Into<RawString>) -> Self {
|
||||
Self {
|
||||
prefix: Some(prefix.into()),
|
||||
suffix: Some(suffix.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Go back to default decor
|
||||
pub fn clear(&mut self) {
|
||||
self.prefix = None;
|
||||
self.suffix = None;
|
||||
}
|
||||
|
||||
/// Get the prefix.
|
||||
pub fn prefix(&self) -> Option<&RawString> {
|
||||
self.prefix.as_ref()
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
pub(crate) fn prefix_encode(
|
||||
&self,
|
||||
buf: &mut dyn std::fmt::Write,
|
||||
input: Option<&str>,
|
||||
default: &str,
|
||||
) -> std::fmt::Result {
|
||||
if let Some(prefix) = self.prefix() {
|
||||
prefix.encode_with_default(buf, input, default)
|
||||
} else {
|
||||
write!(buf, "{default}")
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the prefix.
|
||||
pub fn set_prefix(&mut self, prefix: impl Into<RawString>) {
|
||||
self.prefix = Some(prefix.into());
|
||||
}
|
||||
|
||||
/// Get the suffix.
|
||||
pub fn suffix(&self) -> Option<&RawString> {
|
||||
self.suffix.as_ref()
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
pub(crate) fn suffix_encode(
|
||||
&self,
|
||||
buf: &mut dyn std::fmt::Write,
|
||||
input: Option<&str>,
|
||||
default: &str,
|
||||
) -> std::fmt::Result {
|
||||
if let Some(suffix) = self.suffix() {
|
||||
suffix.encode_with_default(buf, input, default)
|
||||
} else {
|
||||
write!(buf, "{default}")
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the suffix.
|
||||
pub fn set_suffix(&mut self, suffix: impl Into<RawString>) {
|
||||
self.suffix = Some(suffix.into());
|
||||
}
|
||||
|
||||
pub(crate) fn despan(&mut self, input: &str) {
|
||||
if let Some(prefix) = &mut self.prefix {
|
||||
prefix.despan(input);
|
||||
}
|
||||
if let Some(suffix) = &mut self.suffix {
|
||||
suffix.despan(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Decor {
|
||||
#[inline]
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
let mut d = formatter.debug_struct("Decor");
|
||||
match &self.prefix {
|
||||
Some(r) => d.field("prefix", r),
|
||||
None => d.field("prefix", &"default"),
|
||||
};
|
||||
match &self.suffix {
|
||||
Some(r) => d.field("suffix", r),
|
||||
None => d.field("suffix", &"default"),
|
||||
};
|
||||
d.finish()
|
||||
}
|
||||
}
|
||||
Vendored
+102
@@ -0,0 +1,102 @@
|
||||
use super::Error;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeValueArray {
|
||||
values: Vec<crate::Item>,
|
||||
}
|
||||
|
||||
impl SerializeValueArray {
|
||||
pub(crate) fn seq(len: Option<usize>) -> Self {
|
||||
let mut values = Vec::new();
|
||||
if let Some(len) = len {
|
||||
values.reserve(len);
|
||||
}
|
||||
Self { values }
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::SerializeSeq for SerializeValueArray {
|
||||
type Ok = crate::Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
let value = value.serialize(super::ValueSerializer {})?;
|
||||
self.values.push(crate::Item::Value(value));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(crate::Value::Array(crate::Array::with_vec(self.values)))
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::SerializeTuple for SerializeValueArray {
|
||||
type Ok = crate::Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
serde_core::ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
serde_core::ser::SerializeSeq::end(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::SerializeTupleStruct for SerializeValueArray {
|
||||
type Ok = crate::Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
serde_core::ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
serde_core::ser::SerializeSeq::end(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SerializeTupleVariant {
|
||||
variant: &'static str,
|
||||
inner: SerializeValueArray,
|
||||
}
|
||||
|
||||
impl SerializeTupleVariant {
|
||||
pub(crate) fn tuple(variant: &'static str, len: usize) -> Self {
|
||||
Self {
|
||||
variant,
|
||||
inner: SerializeValueArray::seq(Some(len)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::SerializeTupleVariant for SerializeTupleVariant {
|
||||
type Ok = crate::Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
serde_core::ser::SerializeSeq::serialize_element(&mut self.inner, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
let inner = serde_core::ser::SerializeSeq::end(self.inner)?;
|
||||
let mut items = crate::table::KeyValuePairs::new();
|
||||
let value = crate::Item::Value(inner);
|
||||
items.insert(crate::Key::new(self.variant), value);
|
||||
Ok(crate::Value::InlineTable(crate::InlineTable::with_pairs(
|
||||
items,
|
||||
)))
|
||||
}
|
||||
}
|
||||
Vendored
+84
@@ -0,0 +1,84 @@
|
||||
/// Errors that can occur when deserializing a type.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Type could not be serialized to TOML
|
||||
UnsupportedType(Option<&'static str>),
|
||||
/// Value was out of range for the given type
|
||||
OutOfRange(Option<&'static str>),
|
||||
/// `None` could not be serialized to TOML
|
||||
UnsupportedNone,
|
||||
/// Key was not convertible to `String` for serializing to TOML
|
||||
KeyNotString,
|
||||
/// A serialized date was invalid
|
||||
DateInvalid,
|
||||
/// Other serialization error
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub(crate) fn custom<T>(msg: T) -> Self
|
||||
where
|
||||
T: std::fmt::Display,
|
||||
{
|
||||
Self::Custom(msg.to_string())
|
||||
}
|
||||
|
||||
pub(crate) fn unsupported_type(t: Option<&'static str>) -> Self {
|
||||
Self::UnsupportedType(t)
|
||||
}
|
||||
|
||||
pub(crate) fn out_of_range(t: Option<&'static str>) -> Self {
|
||||
Self::OutOfRange(t)
|
||||
}
|
||||
|
||||
pub(crate) fn unsupported_none() -> Self {
|
||||
Self::UnsupportedNone
|
||||
}
|
||||
|
||||
pub(crate) fn key_not_string() -> Self {
|
||||
Self::KeyNotString
|
||||
}
|
||||
|
||||
pub(crate) fn date_invalid() -> Self {
|
||||
Self::DateInvalid
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::Error for Error {
|
||||
fn custom<T>(msg: T) -> Self
|
||||
where
|
||||
T: std::fmt::Display,
|
||||
{
|
||||
Self::custom(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::UnsupportedType(Some(t)) => write!(formatter, "unsupported {t} type"),
|
||||
Self::UnsupportedType(None) => write!(formatter, "unsupported rust type"),
|
||||
Self::OutOfRange(Some(t)) => write!(formatter, "out-of-range value for {t} type"),
|
||||
Self::OutOfRange(None) => write!(formatter, "out-of-range value"),
|
||||
Self::UnsupportedNone => "unsupported None value".fmt(formatter),
|
||||
Self::KeyNotString => "map key was not a string".fmt(formatter),
|
||||
Self::DateInvalid => "a serialized date was invalid".fmt(formatter),
|
||||
Self::Custom(s) => s.fmt(formatter),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::TomlError> for Error {
|
||||
fn from(e: crate::TomlError) -> Self {
|
||||
Self::custom(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for crate::TomlError {
|
||||
fn from(e: Error) -> Self {
|
||||
Self::custom(e.to_string(), None)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
Vendored
+181
@@ -0,0 +1,181 @@
|
||||
use crate::Key;
|
||||
|
||||
use super::Error;
|
||||
|
||||
pub(crate) struct KeySerializer;
|
||||
|
||||
impl serde_core::ser::Serializer for KeySerializer {
|
||||
type Ok = Key;
|
||||
type Error = Error;
|
||||
type SerializeSeq = serde_core::ser::Impossible<Self::Ok, Error>;
|
||||
type SerializeTuple = serde_core::ser::Impossible<Self::Ok, Error>;
|
||||
type SerializeTupleStruct = serde_core::ser::Impossible<Self::Ok, Error>;
|
||||
type SerializeTupleVariant = serde_core::ser::Impossible<Self::Ok, Error>;
|
||||
type SerializeMap = serde_core::ser::Impossible<Self::Ok, Error>;
|
||||
type SerializeStruct = serde_core::ser::Impossible<Self::Ok, Error>;
|
||||
type SerializeStructVariant = serde_core::ser::Impossible<Self::Ok, Error>;
|
||||
|
||||
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(Key::new(v.to_string()))
|
||||
}
|
||||
|
||||
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(Key::new(v.to_string()))
|
||||
}
|
||||
|
||||
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(Key::new(v.to_string()))
|
||||
}
|
||||
|
||||
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(Key::new(v.to_string()))
|
||||
}
|
||||
|
||||
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(Key::new(v.to_string()))
|
||||
}
|
||||
|
||||
fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(Key::new(v.to_string()))
|
||||
}
|
||||
|
||||
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(Key::new(v.to_string()))
|
||||
}
|
||||
|
||||
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(Key::new(v.to_string()))
|
||||
}
|
||||
|
||||
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(Key::new(v.to_string()))
|
||||
}
|
||||
|
||||
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(Key::new(v.to_string()))
|
||||
}
|
||||
|
||||
fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(Key::new(v.to_string()))
|
||||
}
|
||||
|
||||
fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> {
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> {
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(Key::new(v.to_string()))
|
||||
}
|
||||
|
||||
fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(Key::new(value))
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_some<T>(self, _value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(variant.into())
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStruct, Self::Error> {
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStructVariant, Self::Error> {
|
||||
Err(Error::key_not_string())
|
||||
}
|
||||
}
|
||||
Vendored
+445
@@ -0,0 +1,445 @@
|
||||
use super::Error;
|
||||
use super::array::SerializeTupleVariant;
|
||||
use super::array::SerializeValueArray;
|
||||
use super::key::KeySerializer;
|
||||
use super::value::ValueSerializer;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum SerializeMap {
|
||||
Datetime(SerializeDatetime),
|
||||
Table(SerializeInlineTable),
|
||||
}
|
||||
|
||||
impl SerializeMap {
|
||||
pub(crate) fn map(len: Option<usize>) -> Self {
|
||||
Self::Table(SerializeInlineTable::map(len))
|
||||
}
|
||||
|
||||
pub(crate) fn struct_(name: &'static str, len: Option<usize>) -> Self {
|
||||
if toml_datetime::ser::is_datetime(name) {
|
||||
Self::Datetime(SerializeDatetime::new())
|
||||
} else {
|
||||
Self::map(len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::SerializeMap for SerializeMap {
|
||||
type Ok = crate::Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_key<T>(&mut self, input: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
match self {
|
||||
Self::Datetime(s) => s.serialize_key(input),
|
||||
Self::Table(s) => s.serialize_key(input),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
match self {
|
||||
Self::Datetime(s) => s.serialize_value(value),
|
||||
Self::Table(s) => s.serialize_value(value),
|
||||
}
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
match self {
|
||||
Self::Datetime(s) => s.end().map(|items| items.into()),
|
||||
Self::Table(s) => s.end().map(|items| items.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::SerializeStruct for SerializeMap {
|
||||
type Ok = crate::Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
match self {
|
||||
Self::Datetime(s) => s.serialize_field(key, value),
|
||||
Self::Table(s) => s.serialize_field(key, value),
|
||||
}
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
match self {
|
||||
Self::Datetime(s) => s.end().map(|items| items.into()),
|
||||
Self::Table(s) => s.end().map(|items| items.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeDatetime {
|
||||
inner: toml_datetime::ser::DatetimeSerializer,
|
||||
}
|
||||
|
||||
impl SerializeDatetime {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
inner: toml_datetime::ser::DatetimeSerializer::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::SerializeMap for SerializeDatetime {
|
||||
type Ok = crate::Datetime;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_key<T>(&mut self, _input: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
unreachable!("datetimes should only be serialized as structs, not maps")
|
||||
}
|
||||
|
||||
fn serialize_value<T>(&mut self, _value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
unreachable!("datetimes should only be serialized as structs, not maps")
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
unreachable!("datetimes should only be serialized as structs, not maps")
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::SerializeStruct for SerializeDatetime {
|
||||
type Ok = crate::Datetime;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
self.inner.serialize_field(key, value).map_err(dt_err)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
let value = self.inner.end().map_err(dt_err)?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn dt_err(err: toml_datetime::ser::SerializerError) -> Error {
|
||||
match err {
|
||||
toml_datetime::ser::SerializerError::InvalidFormat(err) => Error::custom(err),
|
||||
_ => Error::date_invalid(),
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeInlineTable {
|
||||
items: crate::table::KeyValuePairs,
|
||||
key: Option<crate::Key>,
|
||||
}
|
||||
|
||||
impl SerializeInlineTable {
|
||||
pub(crate) fn map(len: Option<usize>) -> Self {
|
||||
let mut items: crate::table::KeyValuePairs = Default::default();
|
||||
let key = Default::default();
|
||||
if let Some(len) = len {
|
||||
items.reserve(len);
|
||||
}
|
||||
Self { items, key }
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::SerializeMap for SerializeInlineTable {
|
||||
type Ok = crate::InlineTable;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_key<T>(&mut self, input: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
self.key = Some(input.serialize(KeySerializer)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
let mut is_none = false;
|
||||
let value_serializer = MapValueSerializer::new(&mut is_none);
|
||||
let res = value.serialize(value_serializer);
|
||||
match res {
|
||||
Ok(item) => {
|
||||
let key = self.key.take().unwrap();
|
||||
let item = crate::Item::Value(item);
|
||||
self.items.insert(key, item);
|
||||
}
|
||||
Err(e) => {
|
||||
if !(e == Error::unsupported_none() && is_none) {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(crate::InlineTable::with_pairs(self.items))
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::SerializeStruct for SerializeInlineTable {
|
||||
type Ok = crate::InlineTable;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
let mut is_none = false;
|
||||
let value_serializer = MapValueSerializer::new(&mut is_none);
|
||||
let res = value.serialize(value_serializer);
|
||||
match res {
|
||||
Ok(item) => {
|
||||
let item = crate::Item::Value(item);
|
||||
self.items.insert(crate::Key::new(key), item);
|
||||
}
|
||||
Err(e) => {
|
||||
if !(e == Error::unsupported_none() && is_none) {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(crate::InlineTable::with_pairs(self.items))
|
||||
}
|
||||
}
|
||||
|
||||
struct MapValueSerializer<'d> {
|
||||
is_none: &'d mut bool,
|
||||
}
|
||||
|
||||
impl<'d> MapValueSerializer<'d> {
|
||||
fn new(is_none: &'d mut bool) -> Self {
|
||||
Self { is_none }
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::Serializer for MapValueSerializer<'_> {
|
||||
type Ok = crate::Value;
|
||||
type Error = Error;
|
||||
type SerializeSeq = SerializeValueArray;
|
||||
type SerializeTuple = SerializeValueArray;
|
||||
type SerializeTupleStruct = SerializeValueArray;
|
||||
type SerializeTupleVariant = SerializeTupleVariant;
|
||||
type SerializeMap = SerializeMap;
|
||||
type SerializeStruct = SerializeMap;
|
||||
type SerializeStructVariant = SerializeStructVariant;
|
||||
|
||||
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_bool(v)
|
||||
}
|
||||
|
||||
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_i8(v)
|
||||
}
|
||||
|
||||
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_i16(v)
|
||||
}
|
||||
|
||||
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_i32(v)
|
||||
}
|
||||
|
||||
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_i64(v)
|
||||
}
|
||||
|
||||
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_u8(v)
|
||||
}
|
||||
|
||||
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_u16(v)
|
||||
}
|
||||
|
||||
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_u32(v)
|
||||
}
|
||||
|
||||
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_u64(v)
|
||||
}
|
||||
|
||||
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_f32(v)
|
||||
}
|
||||
|
||||
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_f64(v)
|
||||
}
|
||||
|
||||
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_char(v)
|
||||
}
|
||||
|
||||
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_str(v)
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, value: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_bytes(value)
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
*self.is_none = true;
|
||||
Err(Error::unsupported_none())
|
||||
}
|
||||
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
ValueSerializer::new().serialize_some(value)
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_unit()
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_unit_struct(name)
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
name: &'static str,
|
||||
variant_index: u32,
|
||||
variant: &'static str,
|
||||
) -> Result<Self::Ok, Self::Error> {
|
||||
ValueSerializer::new().serialize_unit_variant(name, variant_index, variant)
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T>(
|
||||
self,
|
||||
name: &'static str,
|
||||
variant_index: u32,
|
||||
variant: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
ValueSerializer::new().serialize_newtype_variant(name, variant_index, variant, value)
|
||||
}
|
||||
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
||||
ValueSerializer::new().serialize_seq(len)
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
||||
ValueSerializer::new().serialize_tuple(len)
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
||||
ValueSerializer::new().serialize_tuple_struct(name, len)
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
name: &'static str,
|
||||
variant_index: u32,
|
||||
variant: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
||||
ValueSerializer::new().serialize_tuple_variant(name, variant_index, variant, len)
|
||||
}
|
||||
|
||||
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
||||
ValueSerializer::new().serialize_map(len)
|
||||
}
|
||||
|
||||
fn serialize_struct(
|
||||
self,
|
||||
name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStruct, Self::Error> {
|
||||
ValueSerializer::new().serialize_struct(name, len)
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
name: &'static str,
|
||||
variant_index: u32,
|
||||
variant: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStructVariant, Self::Error> {
|
||||
ValueSerializer::new().serialize_struct_variant(name, variant_index, variant, len)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SerializeStructVariant {
|
||||
variant: &'static str,
|
||||
inner: SerializeInlineTable,
|
||||
}
|
||||
|
||||
impl SerializeStructVariant {
|
||||
pub(crate) fn struct_(variant: &'static str, len: usize) -> Self {
|
||||
Self {
|
||||
variant,
|
||||
inner: SerializeInlineTable::map(Some(len)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::SerializeStructVariant for SerializeStructVariant {
|
||||
type Ok = crate::Value;
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
serde_core::ser::SerializeStruct::serialize_field(&mut self.inner, key, value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
let inner = serde_core::ser::SerializeStruct::end(self.inner)?.into();
|
||||
let mut items = crate::table::KeyValuePairs::new();
|
||||
let value = crate::Item::Value(inner);
|
||||
items.insert(crate::Key::new(self.variant), value);
|
||||
Ok(crate::Value::InlineTable(crate::InlineTable::with_pairs(
|
||||
items,
|
||||
)))
|
||||
}
|
||||
}
|
||||
Vendored
+105
@@ -0,0 +1,105 @@
|
||||
//! Serializing Rust structures into TOML.
|
||||
//!
|
||||
//! This module contains all the Serde support for serializing Rust structures into TOML.
|
||||
|
||||
mod array;
|
||||
mod error;
|
||||
mod key;
|
||||
mod map;
|
||||
mod pretty;
|
||||
mod value;
|
||||
|
||||
use crate::visit_mut::VisitMut as _;
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use array::*;
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use map::*;
|
||||
|
||||
pub use error::Error;
|
||||
pub use value::ValueSerializer;
|
||||
|
||||
/// Serialize the given data structure as a TOML byte vector.
|
||||
///
|
||||
/// Serialization can fail if `T`'s implementation of `Serialize` decides to
|
||||
/// fail, if `T` contains a map with non-string keys, or if `T` attempts to
|
||||
/// serialize an unsupported datatype such as an enum, tuple, or tuple struct.
|
||||
#[cfg(feature = "display")]
|
||||
pub fn to_vec<T>(value: &T) -> Result<Vec<u8>, Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
to_string(value).map(|e| e.into_bytes())
|
||||
}
|
||||
|
||||
/// Serialize the given data structure as a String of TOML.
|
||||
///
|
||||
/// Serialization can fail if `T`'s implementation of `Serialize` decides to
|
||||
/// fail, if `T` contains a map with non-string keys, or if `T` attempts to
|
||||
/// serialize an unsupported datatype such as an enum, tuple, or tuple struct.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use serde::Serialize;
|
||||
///
|
||||
/// #[derive(Serialize)]
|
||||
/// struct Config {
|
||||
/// database: Database,
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Serialize)]
|
||||
/// struct Database {
|
||||
/// ip: String,
|
||||
/// port: Vec<u16>,
|
||||
/// connection_max: u32,
|
||||
/// enabled: bool,
|
||||
/// }
|
||||
///
|
||||
/// let config = Config {
|
||||
/// database: Database {
|
||||
/// ip: "192.168.1.1".to_string(),
|
||||
/// port: vec![8001, 8002, 8003],
|
||||
/// connection_max: 5000,
|
||||
/// enabled: false,
|
||||
/// },
|
||||
/// };
|
||||
///
|
||||
/// let toml = toml_edit::ser::to_string(&config).unwrap();
|
||||
/// println!("{}", toml)
|
||||
/// ```
|
||||
#[cfg(feature = "display")]
|
||||
pub fn to_string<T>(value: &T) -> Result<String, Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
to_document(value).map(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Serialize the given data structure as a "pretty" String of TOML.
|
||||
///
|
||||
/// This is identical to `to_string` except the output string has a more
|
||||
/// "pretty" output. See `ValueSerializer::pretty` for more details.
|
||||
#[cfg(feature = "display")]
|
||||
pub fn to_string_pretty<T>(value: &T) -> Result<String, Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
let mut document = to_document(value)?;
|
||||
pretty::Pretty::new().visit_document_mut(&mut document);
|
||||
Ok(document.to_string())
|
||||
}
|
||||
|
||||
/// Serialize the given data structure into a TOML document.
|
||||
///
|
||||
/// This would allow custom formatting to be applied, mixing with format preserving edits, etc.
|
||||
pub fn to_document<T>(value: &T) -> Result<crate::DocumentMut, Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
let value = value.serialize(ValueSerializer::new())?;
|
||||
let item = crate::Item::Value(value);
|
||||
let root = item
|
||||
.into_table()
|
||||
.map_err(|_| Error::UnsupportedType(None))?;
|
||||
Ok(root.into())
|
||||
}
|
||||
Vendored
+58
@@ -0,0 +1,58 @@
|
||||
pub(crate) struct Pretty {
|
||||
in_value: bool,
|
||||
}
|
||||
|
||||
impl Pretty {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { in_value: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::visit_mut::VisitMut for Pretty {
|
||||
fn visit_document_mut(&mut self, node: &mut crate::DocumentMut) {
|
||||
crate::visit_mut::visit_document_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_item_mut(&mut self, node: &mut crate::Item) {
|
||||
if !self.in_value {
|
||||
node.make_item();
|
||||
}
|
||||
|
||||
crate::visit_mut::visit_item_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_table_mut(&mut self, node: &mut crate::Table) {
|
||||
node.decor_mut().clear();
|
||||
|
||||
// Empty tables could be semantically meaningful, so make sure they are not implicit
|
||||
if !node.is_empty() {
|
||||
node.set_implicit(true);
|
||||
}
|
||||
|
||||
crate::visit_mut::visit_table_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_value_mut(&mut self, node: &mut crate::Value) {
|
||||
node.decor_mut().clear();
|
||||
|
||||
let old_in_value = self.in_value;
|
||||
self.in_value = true;
|
||||
crate::visit_mut::visit_value_mut(self, node);
|
||||
self.in_value = old_in_value;
|
||||
}
|
||||
|
||||
fn visit_array_mut(&mut self, node: &mut crate::Array) {
|
||||
crate::visit_mut::visit_array_mut(self, node);
|
||||
|
||||
if (0..=1).contains(&node.len()) {
|
||||
node.set_trailing("");
|
||||
node.set_trailing_comma(false);
|
||||
} else {
|
||||
for item in node.iter_mut() {
|
||||
item.decor_mut().set_prefix("\n ");
|
||||
}
|
||||
node.set_trailing("\n");
|
||||
node.set_trailing_comma(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+249
@@ -0,0 +1,249 @@
|
||||
use super::Error;
|
||||
use super::SerializeMap;
|
||||
use super::SerializeStructVariant;
|
||||
use super::SerializeTupleVariant;
|
||||
use super::SerializeValueArray;
|
||||
|
||||
/// Serialization for TOML [values][crate::Value].
|
||||
///
|
||||
/// This structure implements serialization support for TOML to serialize an
|
||||
/// arbitrary type to TOML. Note that the TOML format does not support all
|
||||
/// datatypes in Rust, such as enums, tuples, and tuple structs. These types
|
||||
/// will generate an error when serialized.
|
||||
///
|
||||
/// Currently a serializer always writes its output to an in-memory `String`,
|
||||
/// which is passed in when creating the serializer itself.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "parse")] {
|
||||
/// # #[cfg(feature = "display")] {
|
||||
/// use serde::Serialize;
|
||||
///
|
||||
/// #[derive(Serialize)]
|
||||
/// struct Config {
|
||||
/// database: Database,
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Serialize)]
|
||||
/// struct Database {
|
||||
/// ip: String,
|
||||
/// port: Vec<u16>,
|
||||
/// connection_max: u32,
|
||||
/// enabled: bool,
|
||||
/// }
|
||||
///
|
||||
/// let config = Config {
|
||||
/// database: Database {
|
||||
/// ip: "192.168.1.1".to_string(),
|
||||
/// port: vec![8001, 8002, 8003],
|
||||
/// connection_max: 5000,
|
||||
/// enabled: false,
|
||||
/// },
|
||||
/// };
|
||||
///
|
||||
/// let value = serde::Serialize::serialize(
|
||||
/// &config,
|
||||
/// toml_edit::ser::ValueSerializer::new()
|
||||
/// ).unwrap();
|
||||
/// println!("{}", value)
|
||||
/// # }
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
#[non_exhaustive]
|
||||
pub struct ValueSerializer {}
|
||||
|
||||
impl ValueSerializer {
|
||||
/// Creates a new serializer generate a TOML document.
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_core::ser::Serializer for ValueSerializer {
|
||||
type Ok = crate::Value;
|
||||
type Error = Error;
|
||||
type SerializeSeq = SerializeValueArray;
|
||||
type SerializeTuple = SerializeValueArray;
|
||||
type SerializeTupleStruct = SerializeValueArray;
|
||||
type SerializeTupleVariant = SerializeTupleVariant;
|
||||
type SerializeMap = SerializeMap;
|
||||
type SerializeStruct = SerializeMap;
|
||||
type SerializeStructVariant = SerializeStructVariant;
|
||||
|
||||
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(v.into())
|
||||
}
|
||||
|
||||
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(v.into())
|
||||
}
|
||||
|
||||
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
|
||||
let v: i64 = v
|
||||
.try_into()
|
||||
.map_err(|_err| Error::out_of_range(Some("u64")))?;
|
||||
self.serialize_i64(v)
|
||||
}
|
||||
|
||||
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
|
||||
self.serialize_f64(v as f64)
|
||||
}
|
||||
|
||||
fn serialize_f64(self, mut v: f64) -> Result<Self::Ok, Self::Error> {
|
||||
// Discard sign of NaN when serialized using Serde.
|
||||
//
|
||||
// In all likelihood the sign of NaNs is not meaningful in the user's
|
||||
// program. Ending up with `-nan` in the TOML document would usually be
|
||||
// surprising and undesirable, when the sign of the NaN was not
|
||||
// intentionally controlled by the caller, or may even be
|
||||
// nondeterministic if it comes from arithmetic operations or a cast.
|
||||
if v.is_nan() {
|
||||
v = v.copysign(1.0);
|
||||
}
|
||||
Ok(v.into())
|
||||
}
|
||||
|
||||
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
|
||||
let mut buf = [0; 4];
|
||||
self.serialize_str(v.encode_utf8(&mut buf))
|
||||
}
|
||||
|
||||
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(v.into())
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, value: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
use serde_core::ser::Serialize;
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Err(Error::unsupported_none())
|
||||
}
|
||||
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
||||
Err(Error::unsupported_type(Some("unit")))
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
|
||||
Err(Error::unsupported_type(Some(name)))
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
) -> Result<Self::Ok, Self::Error> {
|
||||
self.serialize_str(variant)
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: serde_core::ser::Serialize + ?Sized,
|
||||
{
|
||||
let value = value.serialize(self)?;
|
||||
let mut table = crate::InlineTable::new();
|
||||
table.insert(variant, value);
|
||||
Ok(table.into())
|
||||
}
|
||||
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
||||
Ok(SerializeValueArray::seq(len))
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
||||
Ok(SerializeTupleVariant::tuple(variant, len))
|
||||
}
|
||||
|
||||
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
||||
Ok(SerializeMap::map(len))
|
||||
}
|
||||
|
||||
fn serialize_struct(
|
||||
self,
|
||||
name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStruct, Self::Error> {
|
||||
Ok(SerializeMap::struct_(name, Some(len)))
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStructVariant, Self::Error> {
|
||||
Ok(SerializeStructVariant::struct_(variant, len))
|
||||
}
|
||||
}
|
||||
Vendored
+817
@@ -0,0 +1,817 @@
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use indexmap::map::IndexMap;
|
||||
|
||||
use crate::key::Key;
|
||||
use crate::repr::Decor;
|
||||
use crate::value::DEFAULT_VALUE_DECOR;
|
||||
use crate::{InlineTable, Item, KeyMut, Value};
|
||||
|
||||
/// A TOML table, a top-level collection of key/[`Value`] pairs under a header and logical
|
||||
/// sub-tables
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Table {
|
||||
// Comments/spaces before and after the header
|
||||
pub(crate) decor: Decor,
|
||||
// Whether to hide an empty table
|
||||
pub(crate) implicit: bool,
|
||||
// Whether this is a proxy for dotted keys
|
||||
pub(crate) dotted: bool,
|
||||
// Used for putting tables back in their original order when serialising.
|
||||
//
|
||||
// `None` for user created tables (can be overridden with `set_position`)
|
||||
doc_position: Option<isize>,
|
||||
pub(crate) span: Option<std::ops::Range<usize>>,
|
||||
pub(crate) items: KeyValuePairs,
|
||||
}
|
||||
|
||||
/// Constructors
|
||||
///
|
||||
/// See also `FromIterator`
|
||||
impl Table {
|
||||
/// Creates an empty table.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub(crate) fn with_pos(doc_position: Option<isize>) -> Self {
|
||||
Self {
|
||||
doc_position,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_pairs(items: KeyValuePairs) -> Self {
|
||||
Self {
|
||||
items,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert to an inline table
|
||||
pub fn into_inline_table(mut self) -> InlineTable {
|
||||
for (_, value) in self.items.iter_mut() {
|
||||
value.make_value();
|
||||
}
|
||||
let mut t = InlineTable::with_pairs(self.items);
|
||||
t.fmt();
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
/// Formatting
|
||||
impl Table {
|
||||
/// Get key/values for values that are visually children of this table
|
||||
///
|
||||
/// For example, this will return dotted keys
|
||||
pub fn get_values(&self) -> Vec<(Vec<&Key>, &Value)> {
|
||||
let mut values = Vec::new();
|
||||
let root = Vec::new();
|
||||
self.append_values(&root, &mut values);
|
||||
values
|
||||
}
|
||||
|
||||
fn append_values<'s>(
|
||||
&'s self,
|
||||
parent: &[&'s Key],
|
||||
values: &mut Vec<(Vec<&'s Key>, &'s Value)>,
|
||||
) {
|
||||
for (key, value) in self.items.iter() {
|
||||
let mut path = parent.to_vec();
|
||||
path.push(key);
|
||||
match value {
|
||||
Item::Table(table) if table.is_dotted() => {
|
||||
table.append_values(&path, values);
|
||||
}
|
||||
Item::Value(value) => {
|
||||
if let Some(table) = value.as_inline_table() {
|
||||
if table.is_dotted() {
|
||||
table.append_values(&path, values);
|
||||
} else {
|
||||
values.push((path, value));
|
||||
}
|
||||
} else {
|
||||
values.push((path, value));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn append_all_values<'s>(
|
||||
&'s self,
|
||||
parent: &[&'s Key],
|
||||
values: &mut Vec<(Vec<&'s Key>, &'s Value)>,
|
||||
) {
|
||||
for (key, value) in self.items.iter() {
|
||||
let mut path = parent.to_vec();
|
||||
path.push(key);
|
||||
match value {
|
||||
Item::Table(table) => {
|
||||
table.append_all_values(&path, values);
|
||||
}
|
||||
Item::Value(value) => {
|
||||
if let Some(table) = value.as_inline_table() {
|
||||
if table.is_dotted() {
|
||||
table.append_values(&path, values);
|
||||
} else {
|
||||
values.push((path, value));
|
||||
}
|
||||
} else {
|
||||
values.push((path, value));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Auto formats the table.
|
||||
pub fn fmt(&mut self) {
|
||||
decorate_table(self);
|
||||
}
|
||||
|
||||
/// Sorts [Key]/[Value]-pairs of the table
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// This sorts the syntactic table (everything under the `[header]`) and not the logical map of
|
||||
/// key-value pairs.
|
||||
/// This does not affect the order of [sub-tables][Table] or [sub-arrays][crate::ArrayOfTables].
|
||||
/// This is not recursive.
|
||||
///
|
||||
/// </div>
|
||||
pub fn sort_values(&mut self) {
|
||||
// Assuming standard tables have their doc_position set and this won't negatively impact them
|
||||
self.items.sort_keys();
|
||||
for value in self.items.values_mut() {
|
||||
match value {
|
||||
Item::Table(table) if table.is_dotted() => {
|
||||
table.sort_values();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sort [Key]/[Value]-pairs of the table using the using the comparison function `compare`
|
||||
///
|
||||
/// The comparison function receives two key and value pairs to compare (you can sort by keys or
|
||||
/// values or their combination as needed).
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// This sorts the syntactic table (everything under the `[header]`) and not the logical map of
|
||||
/// key-value pairs.
|
||||
/// This does not affect the order of [sub-tables][Table] or [sub-arrays][crate::ArrayOfTables].
|
||||
/// This is not recursive.
|
||||
///
|
||||
/// </div>
|
||||
pub fn sort_values_by<F>(&mut self, mut compare: F)
|
||||
where
|
||||
F: FnMut(&Key, &Item, &Key, &Item) -> std::cmp::Ordering,
|
||||
{
|
||||
self.sort_values_by_internal(&mut compare);
|
||||
}
|
||||
|
||||
fn sort_values_by_internal<F>(&mut self, compare: &mut F)
|
||||
where
|
||||
F: FnMut(&Key, &Item, &Key, &Item) -> std::cmp::Ordering,
|
||||
{
|
||||
let modified_cmp =
|
||||
|key1: &Key, val1: &Item, key2: &Key, val2: &Item| -> std::cmp::Ordering {
|
||||
compare(key1, val1, key2, val2)
|
||||
};
|
||||
|
||||
self.items.sort_by(modified_cmp);
|
||||
|
||||
for value in self.items.values_mut() {
|
||||
match value {
|
||||
Item::Table(table) if table.is_dotted() => {
|
||||
table.sort_values_by_internal(compare);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If a table has no key/value pairs and implicit, it will not be displayed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```notrust
|
||||
/// [target."x86_64/windows.json".dependencies]
|
||||
/// ```
|
||||
///
|
||||
/// In the document above, tables `target` and `target."x86_64/windows.json"` are implicit.
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "parse")] {
|
||||
/// # #[cfg(feature = "display")] {
|
||||
/// use toml_edit::DocumentMut;
|
||||
/// let mut doc = "[a]\n[a.b]\n".parse::<DocumentMut>().expect("invalid toml");
|
||||
///
|
||||
/// doc["a"].as_table_mut().unwrap().set_implicit(true);
|
||||
/// assert_eq!(doc.to_string(), "[a.b]\n");
|
||||
/// # }
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn set_implicit(&mut self, implicit: bool) {
|
||||
self.implicit = implicit;
|
||||
}
|
||||
|
||||
/// If a table has no key/value pairs and implicit, it will not be displayed.
|
||||
pub fn is_implicit(&self) -> bool {
|
||||
self.implicit
|
||||
}
|
||||
|
||||
/// Change this table's dotted status
|
||||
pub fn set_dotted(&mut self, yes: bool) {
|
||||
self.dotted = yes;
|
||||
}
|
||||
|
||||
/// Check if this is a wrapper for dotted keys, rather than a standard table
|
||||
pub fn is_dotted(&self) -> bool {
|
||||
self.dotted
|
||||
}
|
||||
|
||||
/// Sets the position of the `Table` within the [`DocumentMut`][crate::DocumentMut].
|
||||
///
|
||||
/// Use `None` for having an unspecified location
|
||||
pub fn set_position(&mut self, doc_position: Option<isize>) {
|
||||
self.doc_position = doc_position;
|
||||
}
|
||||
|
||||
/// The position of the `Table` within the [`DocumentMut`][crate::DocumentMut].
|
||||
///
|
||||
/// Returns `None` if the `Table` was created manually (i.e. not via parsing)
|
||||
/// in which case its position is set automatically. This can be overridden with
|
||||
/// [`Table::set_position`].
|
||||
pub fn position(&self) -> Option<isize> {
|
||||
self.doc_position
|
||||
}
|
||||
|
||||
/// Returns the surrounding whitespace
|
||||
pub fn decor_mut(&mut self) -> &mut Decor {
|
||||
&mut self.decor
|
||||
}
|
||||
|
||||
/// Returns the decor associated with a given key of the table.
|
||||
pub fn decor(&self) -> &Decor {
|
||||
&self.decor
|
||||
}
|
||||
|
||||
/// Returns an accessor to a key's formatting
|
||||
pub fn key(&self, key: &str) -> Option<&'_ Key> {
|
||||
self.items.get_full(key).map(|(_, key, _)| key)
|
||||
}
|
||||
|
||||
/// Returns an accessor to a key's formatting
|
||||
pub fn key_mut(&mut self, key: &str) -> Option<KeyMut<'_>> {
|
||||
use indexmap::map::MutableKeys;
|
||||
self.items
|
||||
.get_full_mut2(key)
|
||||
.map(|(_, key, _)| key.as_mut())
|
||||
}
|
||||
|
||||
/// The location within the original document
|
||||
///
|
||||
/// This generally requires a [`Document`][crate::Document].
|
||||
pub fn span(&self) -> Option<std::ops::Range<usize>> {
|
||||
self.span.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn despan(&mut self, input: &str) {
|
||||
use indexmap::map::MutableKeys;
|
||||
self.span = None;
|
||||
self.decor.despan(input);
|
||||
for (key, value) in self.items.iter_mut2() {
|
||||
key.despan(input);
|
||||
value.despan(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Table {
|
||||
/// Returns an iterator over all key/value pairs, including empty.
|
||||
pub fn iter(&self) -> Iter<'_> {
|
||||
Box::new(
|
||||
self.items
|
||||
.iter()
|
||||
.filter(|(_, value)| !value.is_none())
|
||||
.map(|(key, value)| (key.get(), value)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns an mutable iterator over all key/value pairs, including empty.
|
||||
pub fn iter_mut(&mut self) -> IterMut<'_> {
|
||||
use indexmap::map::MutableKeys;
|
||||
Box::new(
|
||||
self.items
|
||||
.iter_mut2()
|
||||
.filter(|(_, value)| !value.is_none())
|
||||
.map(|(key, value)| (key.as_mut(), value)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the number of non-empty items in the table.
|
||||
pub fn len(&self) -> usize {
|
||||
self.iter().count()
|
||||
}
|
||||
|
||||
/// Returns true if the table is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Clears the table, removing all key-value pairs. Keeps the allocated memory for reuse.
|
||||
pub fn clear(&mut self) {
|
||||
self.items.clear();
|
||||
}
|
||||
|
||||
/// Gets the given key's corresponding entry in the Table for in-place manipulation.
|
||||
pub fn entry<'a>(&'a mut self, key: &str) -> Entry<'a> {
|
||||
// Accept a `&str` rather than an owned type to keep `String`, well, internal
|
||||
match self.items.entry(key.into()) {
|
||||
indexmap::map::Entry::Occupied(entry) => Entry::Occupied(OccupiedEntry { entry }),
|
||||
indexmap::map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry { entry }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the given key's corresponding entry in the Table for in-place manipulation.
|
||||
pub fn entry_format<'a>(&'a mut self, key: &Key) -> Entry<'a> {
|
||||
// Accept a `&Key` to be consistent with `entry`
|
||||
match self.items.entry(key.clone()) {
|
||||
indexmap::map::Entry::Occupied(entry) => Entry::Occupied(OccupiedEntry { entry }),
|
||||
indexmap::map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry { entry }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an optional reference to an item given the key.
|
||||
pub fn get<'a>(&'a self, key: &str) -> Option<&'a Item> {
|
||||
self.items
|
||||
.get(key)
|
||||
.and_then(|value| if !value.is_none() { Some(value) } else { None })
|
||||
}
|
||||
|
||||
/// Returns an optional mutable reference to an item given the key.
|
||||
pub fn get_mut<'a>(&'a mut self, key: &str) -> Option<&'a mut Item> {
|
||||
self.items
|
||||
.get_mut(key)
|
||||
.and_then(|value| if !value.is_none() { Some(value) } else { None })
|
||||
}
|
||||
|
||||
/// Return references to the key-value pair stored for key, if it is present, else None.
|
||||
pub fn get_key_value<'a>(&'a self, key: &str) -> Option<(&'a Key, &'a Item)> {
|
||||
self.items.get_full(key).and_then(|(_, key, value)| {
|
||||
if !value.is_none() {
|
||||
Some((key, value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Return mutable references to the key-value pair stored for key, if it is present, else None.
|
||||
pub fn get_key_value_mut<'a>(&'a mut self, key: &str) -> Option<(KeyMut<'a>, &'a mut Item)> {
|
||||
use indexmap::map::MutableKeys;
|
||||
self.items.get_full_mut2(key).and_then(|(_, key, value)| {
|
||||
if !value.is_none() {
|
||||
Some((key.as_mut(), value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns true if the table contains an item with the given key.
|
||||
pub fn contains_key(&self, key: &str) -> bool {
|
||||
if let Some(value) = self.items.get(key) {
|
||||
!value.is_none()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the table contains a table with the given key.
|
||||
pub fn contains_table(&self, key: &str) -> bool {
|
||||
if let Some(value) = self.items.get(key) {
|
||||
value.is_table()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the table contains a value with the given key.
|
||||
pub fn contains_value(&self, key: &str) -> bool {
|
||||
if let Some(value) = self.items.get(key) {
|
||||
value.is_value()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the table contains an array of tables with the given key.
|
||||
pub fn contains_array_of_tables(&self, key: &str) -> bool {
|
||||
if let Some(value) = self.items.get(key) {
|
||||
value.is_array_of_tables()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts a key-value pair into the map.
|
||||
pub fn insert(&mut self, key: &str, item: Item) -> Option<Item> {
|
||||
use indexmap::map::MutableEntryKey;
|
||||
let key = Key::new(key);
|
||||
match self.items.entry(key.clone()) {
|
||||
indexmap::map::Entry::Occupied(mut entry) => {
|
||||
entry.key_mut().fmt();
|
||||
let old = std::mem::replace(entry.get_mut(), item);
|
||||
Some(old)
|
||||
}
|
||||
indexmap::map::Entry::Vacant(entry) => {
|
||||
entry.insert(item);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts a key-value pair into the map.
|
||||
pub fn insert_formatted(&mut self, key: &Key, item: Item) -> Option<Item> {
|
||||
use indexmap::map::MutableEntryKey;
|
||||
match self.items.entry(key.clone()) {
|
||||
indexmap::map::Entry::Occupied(mut entry) => {
|
||||
*entry.key_mut() = key.clone();
|
||||
let old = std::mem::replace(entry.get_mut(), item);
|
||||
Some(old)
|
||||
}
|
||||
indexmap::map::Entry::Vacant(entry) => {
|
||||
entry.insert(item);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes an item given the key.
|
||||
pub fn remove(&mut self, key: &str) -> Option<Item> {
|
||||
self.items.shift_remove(key)
|
||||
}
|
||||
|
||||
/// Removes a key from the map, returning the stored key and value if the key was previously in the map.
|
||||
pub fn remove_entry(&mut self, key: &str) -> Option<(Key, Item)> {
|
||||
self.items.shift_remove_entry(key)
|
||||
}
|
||||
|
||||
/// Retains only the elements specified by the `keep` predicate.
|
||||
///
|
||||
/// In other words, remove all pairs `(key, item)` for which
|
||||
/// `keep(&key, &mut item)` returns `false`.
|
||||
///
|
||||
/// The elements are visited in iteration order.
|
||||
pub fn retain<F>(&mut self, mut keep: F)
|
||||
where
|
||||
F: FnMut(&str, &mut Item) -> bool,
|
||||
{
|
||||
self.items.retain(|key, value| keep(key, value));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
impl std::fmt::Display for Table {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let children = self.get_values();
|
||||
// print table body
|
||||
for (key_path, value) in children {
|
||||
crate::encode::encode_key_path_ref(&key_path, f, None, DEFAULT_KEY_DECOR)?;
|
||||
write!(f, "=")?;
|
||||
crate::encode::encode_value(value, f, None, DEFAULT_VALUE_DECOR)?;
|
||||
writeln!(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Into<Key>, V: Into<Item>> Extend<(K, V)> for Table {
|
||||
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
|
||||
for (key, value) in iter {
|
||||
let key = key.into();
|
||||
let value = value.into();
|
||||
self.items.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Into<Key>, V: Into<Item>> FromIterator<(K, V)> for Table {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = (K, V)>,
|
||||
{
|
||||
let mut table = Self::new();
|
||||
table.extend(iter);
|
||||
table
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Table {
|
||||
type Item = (String, Item);
|
||||
type IntoIter = IntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Box::new(self.items.into_iter().map(|(k, value)| (k.into(), value)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> IntoIterator for &'s Table {
|
||||
type Item = (&'s str, &'s Item);
|
||||
type IntoIter = Iter<'s>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type KeyValuePairs = IndexMap<Key, Item>;
|
||||
|
||||
fn decorate_table(table: &mut Table) {
|
||||
use indexmap::map::MutableKeys;
|
||||
for (mut key, value) in table
|
||||
.items
|
||||
.iter_mut2()
|
||||
.filter(|(_, value)| value.is_value())
|
||||
.map(|(key, value)| (key.as_mut(), value.as_value_mut().unwrap()))
|
||||
{
|
||||
key.leaf_decor_mut().clear();
|
||||
key.dotted_decor_mut().clear();
|
||||
value.decor_mut().clear();
|
||||
}
|
||||
}
|
||||
|
||||
// `key1 = value1`
|
||||
pub(crate) const DEFAULT_ROOT_DECOR: (&str, &str) = ("", "");
|
||||
pub(crate) const DEFAULT_KEY_DECOR: (&str, &str) = ("", " ");
|
||||
pub(crate) const DEFAULT_TABLE_DECOR: (&str, &str) = ("\n", "");
|
||||
pub(crate) const DEFAULT_KEY_PATH_DECOR: (&str, &str) = ("", "");
|
||||
|
||||
/// An owned iterator type over [`Table`]'s [`Key`]/[`Item`] pairs
|
||||
pub type IntoIter = Box<dyn Iterator<Item = (String, Item)>>;
|
||||
/// An iterator type over [`Table`]'s [`Key`]/[`Item`] pairs
|
||||
pub type Iter<'a> = Box<dyn Iterator<Item = (&'a str, &'a Item)> + 'a>;
|
||||
/// A mutable iterator type over [`Table`]'s [`Key`]/[`Item`] pairs
|
||||
pub type IterMut<'a> = Box<dyn Iterator<Item = (KeyMut<'a>, &'a mut Item)> + 'a>;
|
||||
|
||||
/// This trait represents either a `Table`, or an `InlineTable`.
|
||||
pub trait TableLike: crate::private::Sealed {
|
||||
/// Returns an iterator over key/value pairs.
|
||||
fn iter(&self) -> Iter<'_>;
|
||||
/// Returns an mutable iterator over all key/value pairs, including empty.
|
||||
fn iter_mut(&mut self) -> IterMut<'_>;
|
||||
/// Returns the number of nonempty items.
|
||||
fn len(&self) -> usize {
|
||||
self.iter().filter(|&(_, v)| !v.is_none()).count()
|
||||
}
|
||||
/// Returns true if the table is empty.
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
/// Clears the table, removing all key-value pairs. Keeps the allocated memory for reuse.
|
||||
fn clear(&mut self);
|
||||
/// Gets the given key's corresponding entry in the Table for in-place manipulation.
|
||||
fn entry<'a>(&'a mut self, key: &str) -> Entry<'a>;
|
||||
/// Gets the given key's corresponding entry in the Table for in-place manipulation.
|
||||
fn entry_format<'a>(&'a mut self, key: &Key) -> Entry<'a>;
|
||||
/// Returns an optional reference to an item given the key.
|
||||
fn get<'s>(&'s self, key: &str) -> Option<&'s Item>;
|
||||
/// Returns an optional mutable reference to an item given the key.
|
||||
fn get_mut<'s>(&'s mut self, key: &str) -> Option<&'s mut Item>;
|
||||
/// Return references to the key-value pair stored for key, if it is present, else None.
|
||||
fn get_key_value<'a>(&'a self, key: &str) -> Option<(&'a Key, &'a Item)>;
|
||||
/// Return mutable references to the key-value pair stored for key, if it is present, else None.
|
||||
fn get_key_value_mut<'a>(&'a mut self, key: &str) -> Option<(KeyMut<'a>, &'a mut Item)>;
|
||||
/// Returns true if the table contains an item with the given key.
|
||||
fn contains_key(&self, key: &str) -> bool;
|
||||
/// Inserts a key-value pair into the map.
|
||||
fn insert(&mut self, key: &str, value: Item) -> Option<Item>;
|
||||
/// Removes an item given the key.
|
||||
fn remove(&mut self, key: &str) -> Option<Item>;
|
||||
|
||||
/// Get key/values for values that are visually children of this table
|
||||
///
|
||||
/// For example, this will return dotted keys
|
||||
fn get_values(&self) -> Vec<(Vec<&Key>, &Value)>;
|
||||
|
||||
/// Auto formats the table.
|
||||
fn fmt(&mut self);
|
||||
/// Sorts [Key]/[Value]-pairs of the table
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// This sorts the syntactic table (everything under the `[header]`) and not the logical map of
|
||||
/// key-value pairs.
|
||||
/// This does not affect the order of [sub-tables][Table] or [sub-arrays][crate::ArrayOfTables].
|
||||
/// This is not recursive.
|
||||
///
|
||||
/// </div>
|
||||
fn sort_values(&mut self);
|
||||
/// Change this table's dotted status
|
||||
fn set_dotted(&mut self, yes: bool);
|
||||
/// Check if this is a wrapper for dotted keys, rather than a standard table
|
||||
fn is_dotted(&self) -> bool;
|
||||
|
||||
/// Returns an accessor to a key's formatting
|
||||
fn key(&self, key: &str) -> Option<&'_ Key>;
|
||||
/// Returns an accessor to a key's formatting
|
||||
fn key_mut(&mut self, key: &str) -> Option<KeyMut<'_>>;
|
||||
}
|
||||
|
||||
impl TableLike for Table {
|
||||
fn iter(&self) -> Iter<'_> {
|
||||
self.iter()
|
||||
}
|
||||
fn iter_mut(&mut self) -> IterMut<'_> {
|
||||
self.iter_mut()
|
||||
}
|
||||
fn clear(&mut self) {
|
||||
self.clear();
|
||||
}
|
||||
fn entry<'a>(&'a mut self, key: &str) -> Entry<'a> {
|
||||
self.entry(key)
|
||||
}
|
||||
fn entry_format<'a>(&'a mut self, key: &Key) -> Entry<'a> {
|
||||
self.entry_format(key)
|
||||
}
|
||||
fn get<'s>(&'s self, key: &str) -> Option<&'s Item> {
|
||||
self.get(key)
|
||||
}
|
||||
fn get_mut<'s>(&'s mut self, key: &str) -> Option<&'s mut Item> {
|
||||
self.get_mut(key)
|
||||
}
|
||||
fn get_key_value<'a>(&'a self, key: &str) -> Option<(&'a Key, &'a Item)> {
|
||||
self.get_key_value(key)
|
||||
}
|
||||
fn get_key_value_mut<'a>(&'a mut self, key: &str) -> Option<(KeyMut<'a>, &'a mut Item)> {
|
||||
self.get_key_value_mut(key)
|
||||
}
|
||||
fn contains_key(&self, key: &str) -> bool {
|
||||
self.contains_key(key)
|
||||
}
|
||||
fn insert(&mut self, key: &str, value: Item) -> Option<Item> {
|
||||
self.insert(key, value)
|
||||
}
|
||||
fn remove(&mut self, key: &str) -> Option<Item> {
|
||||
self.remove(key)
|
||||
}
|
||||
|
||||
fn get_values(&self) -> Vec<(Vec<&Key>, &Value)> {
|
||||
self.get_values()
|
||||
}
|
||||
fn fmt(&mut self) {
|
||||
self.fmt();
|
||||
}
|
||||
fn sort_values(&mut self) {
|
||||
self.sort_values();
|
||||
}
|
||||
fn is_dotted(&self) -> bool {
|
||||
self.is_dotted()
|
||||
}
|
||||
fn set_dotted(&mut self, yes: bool) {
|
||||
self.set_dotted(yes);
|
||||
}
|
||||
|
||||
fn key(&self, key: &str) -> Option<&'_ Key> {
|
||||
self.key(key)
|
||||
}
|
||||
fn key_mut(&mut self, key: &str) -> Option<KeyMut<'_>> {
|
||||
self.key_mut(key)
|
||||
}
|
||||
}
|
||||
|
||||
/// A view into a single location in a [`Table`], which may be vacant or occupied.
|
||||
pub enum Entry<'a> {
|
||||
/// An occupied Entry.
|
||||
Occupied(OccupiedEntry<'a>),
|
||||
/// A vacant Entry.
|
||||
Vacant(VacantEntry<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Entry<'a> {
|
||||
/// Returns the entry key
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use toml_edit::Table;
|
||||
///
|
||||
/// let mut map = Table::new();
|
||||
///
|
||||
/// assert_eq!("hello", map.entry("hello").key());
|
||||
/// ```
|
||||
pub fn key(&self) -> &str {
|
||||
match self {
|
||||
Entry::Occupied(e) => e.key(),
|
||||
Entry::Vacant(e) => e.key(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures a value is in the entry by inserting the default if empty, and returns
|
||||
/// a mutable reference to the value in the entry.
|
||||
pub fn or_insert(self, default: Item) -> &'a mut Item {
|
||||
match self {
|
||||
Entry::Occupied(entry) => entry.into_mut(),
|
||||
Entry::Vacant(entry) => entry.insert(default),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures a value is in the entry by inserting the result of the default function if empty,
|
||||
/// and returns a mutable reference to the value in the entry.
|
||||
pub fn or_insert_with<F: FnOnce() -> Item>(self, default: F) -> &'a mut Item {
|
||||
match self {
|
||||
Entry::Occupied(entry) => entry.into_mut(),
|
||||
Entry::Vacant(entry) => entry.insert(default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A view into a single occupied location in a [`Table`].
|
||||
pub struct OccupiedEntry<'a> {
|
||||
pub(crate) entry: indexmap::map::OccupiedEntry<'a, Key, Item>,
|
||||
}
|
||||
|
||||
impl<'a> OccupiedEntry<'a> {
|
||||
/// Gets a reference to the entry key
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use toml_edit::Table;
|
||||
///
|
||||
/// let mut map = Table::new();
|
||||
///
|
||||
/// assert_eq!("foo", map.entry("foo").key());
|
||||
/// ```
|
||||
pub fn key(&self) -> &str {
|
||||
self.entry.key().get()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the entry key
|
||||
pub fn key_mut(&mut self) -> KeyMut<'_> {
|
||||
use indexmap::map::MutableEntryKey;
|
||||
self.entry.key_mut().as_mut()
|
||||
}
|
||||
|
||||
/// Gets a reference to the value in the entry.
|
||||
pub fn get(&self) -> &Item {
|
||||
self.entry.get()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the value in the entry.
|
||||
pub fn get_mut(&mut self) -> &mut Item {
|
||||
self.entry.get_mut()
|
||||
}
|
||||
|
||||
/// Converts the `OccupiedEntry` into a mutable reference to the value in the entry
|
||||
/// with a lifetime bound to the map itself
|
||||
pub fn into_mut(self) -> &'a mut Item {
|
||||
self.entry.into_mut()
|
||||
}
|
||||
|
||||
/// Sets the value of the entry, and returns the entry's old value
|
||||
pub fn insert(&mut self, value: Item) -> Item {
|
||||
self.entry.insert(value)
|
||||
}
|
||||
|
||||
/// Takes the value out of the entry, and returns it
|
||||
pub fn remove(self) -> Item {
|
||||
self.entry.shift_remove()
|
||||
}
|
||||
}
|
||||
|
||||
/// A view into a single empty location in a [`Table`].
|
||||
pub struct VacantEntry<'a> {
|
||||
pub(crate) entry: indexmap::map::VacantEntry<'a, Key, Item>,
|
||||
}
|
||||
|
||||
impl<'a> VacantEntry<'a> {
|
||||
/// Gets a reference to the entry key
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use toml_edit::Table;
|
||||
///
|
||||
/// let mut map = Table::new();
|
||||
///
|
||||
/// assert_eq!("foo", map.entry("foo").key());
|
||||
/// ```
|
||||
pub fn key(&self) -> &str {
|
||||
self.entry.key().get()
|
||||
}
|
||||
|
||||
/// Sets the value of the entry with the `VacantEntry`'s key,
|
||||
/// and returns a mutable reference to it
|
||||
pub fn insert(self, value: Item) -> &'a mut Item {
|
||||
let entry = self.entry;
|
||||
entry.insert(value)
|
||||
}
|
||||
}
|
||||
Vendored
+386
@@ -0,0 +1,386 @@
|
||||
use std::iter::FromIterator;
|
||||
use std::str::FromStr;
|
||||
|
||||
use toml_datetime::{Date, Datetime, Time};
|
||||
|
||||
use crate::key::Key;
|
||||
use crate::repr::{Decor, Formatted};
|
||||
use crate::{Array, InlineTable, RawString};
|
||||
|
||||
/// For [`Key`]/Value pairs under a [`Table`][crate::Table] header or inside another
|
||||
/// Value
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value {
|
||||
/// A string value.
|
||||
String(Formatted<String>),
|
||||
/// A 64-bit integer value.
|
||||
Integer(Formatted<i64>),
|
||||
/// A 64-bit float value.
|
||||
Float(Formatted<f64>),
|
||||
/// A boolean value.
|
||||
Boolean(Formatted<bool>),
|
||||
/// An RFC 3339 formatted date-time with offset.
|
||||
Datetime(Formatted<Datetime>),
|
||||
/// An inline array of values.
|
||||
Array(Array),
|
||||
/// An inline table of key/value pairs.
|
||||
InlineTable(InlineTable),
|
||||
}
|
||||
|
||||
/// Downcasting
|
||||
impl Value {
|
||||
/// Text description of value type
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::String(..) => "string",
|
||||
Self::Integer(..) => "integer",
|
||||
Self::Float(..) => "float",
|
||||
Self::Boolean(..) => "boolean",
|
||||
Self::Datetime(..) => "datetime",
|
||||
Self::Array(..) => "array",
|
||||
Self::InlineTable(..) => "inline table",
|
||||
}
|
||||
}
|
||||
|
||||
/// Casts `self` to str.
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
match *self {
|
||||
Self::String(ref value) => Some(value.value()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `self` is a string.
|
||||
pub fn is_str(&self) -> bool {
|
||||
self.as_str().is_some()
|
||||
}
|
||||
|
||||
/// Casts `self` to integer.
|
||||
pub fn as_integer(&self) -> Option<i64> {
|
||||
match *self {
|
||||
Self::Integer(ref value) => Some(*value.value()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `self` is an integer.
|
||||
pub fn is_integer(&self) -> bool {
|
||||
self.as_integer().is_some()
|
||||
}
|
||||
|
||||
/// Casts `self` to float.
|
||||
pub fn as_float(&self) -> Option<f64> {
|
||||
match *self {
|
||||
Self::Float(ref value) => Some(*value.value()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `self` is a float.
|
||||
pub fn is_float(&self) -> bool {
|
||||
self.as_float().is_some()
|
||||
}
|
||||
|
||||
/// Casts `self` to boolean.
|
||||
pub fn as_bool(&self) -> Option<bool> {
|
||||
match *self {
|
||||
Self::Boolean(ref value) => Some(*value.value()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `self` is a boolean.
|
||||
pub fn is_bool(&self) -> bool {
|
||||
self.as_bool().is_some()
|
||||
}
|
||||
|
||||
/// Casts `self` to date-time.
|
||||
pub fn as_datetime(&self) -> Option<&Datetime> {
|
||||
match *self {
|
||||
Self::Datetime(ref value) => Some(value.value()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `self` is a date-time.
|
||||
pub fn is_datetime(&self) -> bool {
|
||||
self.as_datetime().is_some()
|
||||
}
|
||||
|
||||
/// Casts `self` to array.
|
||||
pub fn as_array(&self) -> Option<&Array> {
|
||||
match *self {
|
||||
Self::Array(ref value) => Some(value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Casts `self` to mutable array.
|
||||
pub fn as_array_mut(&mut self) -> Option<&mut Array> {
|
||||
match *self {
|
||||
Self::Array(ref mut value) => Some(value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `self` is an array.
|
||||
pub fn is_array(&self) -> bool {
|
||||
self.as_array().is_some()
|
||||
}
|
||||
|
||||
/// Casts `self` to inline table.
|
||||
pub fn as_inline_table(&self) -> Option<&InlineTable> {
|
||||
match *self {
|
||||
Self::InlineTable(ref value) => Some(value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Casts `self` to mutable inline table.
|
||||
pub fn as_inline_table_mut(&mut self) -> Option<&mut InlineTable> {
|
||||
match *self {
|
||||
Self::InlineTable(ref mut value) => Some(value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `self` is an inline table.
|
||||
pub fn is_inline_table(&self) -> bool {
|
||||
self.as_inline_table().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
/// Get the decoration of the value.
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// let v = toml_edit::Value::from(true);
|
||||
/// assert_eq!(v.decor().suffix(), None);
|
||||
///```
|
||||
pub fn decor_mut(&mut self) -> &mut Decor {
|
||||
match self {
|
||||
Self::String(f) => f.decor_mut(),
|
||||
Self::Integer(f) => f.decor_mut(),
|
||||
Self::Float(f) => f.decor_mut(),
|
||||
Self::Boolean(f) => f.decor_mut(),
|
||||
Self::Datetime(f) => f.decor_mut(),
|
||||
Self::Array(a) => a.decor_mut(),
|
||||
Self::InlineTable(t) => t.decor_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the decoration of the value.
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// let v = toml_edit::Value::from(true);
|
||||
/// assert_eq!(v.decor().suffix(), None);
|
||||
///```
|
||||
pub fn decor(&self) -> &Decor {
|
||||
match *self {
|
||||
Self::String(ref f) => f.decor(),
|
||||
Self::Integer(ref f) => f.decor(),
|
||||
Self::Float(ref f) => f.decor(),
|
||||
Self::Boolean(ref f) => f.decor(),
|
||||
Self::Datetime(ref f) => f.decor(),
|
||||
Self::Array(ref a) => a.decor(),
|
||||
Self::InlineTable(ref t) => t.decor(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the prefix and the suffix for value.
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "display")] {
|
||||
/// let mut v = toml_edit::Value::from(42);
|
||||
/// assert_eq!(&v.to_string(), "42");
|
||||
/// let d = v.decorated(" ", " ");
|
||||
/// assert_eq!(&d.to_string(), " 42 ");
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn decorated(mut self, prefix: impl Into<RawString>, suffix: impl Into<RawString>) -> Self {
|
||||
self.decorate(prefix, suffix);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn decorate(&mut self, prefix: impl Into<RawString>, suffix: impl Into<RawString>) {
|
||||
let decor = self.decor_mut();
|
||||
*decor = Decor::new(prefix, suffix);
|
||||
}
|
||||
|
||||
/// The location within the original document
|
||||
///
|
||||
/// This generally requires a [`Document`][crate::Document].
|
||||
pub fn span(&self) -> Option<std::ops::Range<usize>> {
|
||||
match self {
|
||||
Self::String(f) => f.span(),
|
||||
Self::Integer(f) => f.span(),
|
||||
Self::Float(f) => f.span(),
|
||||
Self::Boolean(f) => f.span(),
|
||||
Self::Datetime(f) => f.span(),
|
||||
Self::Array(a) => a.span(),
|
||||
Self::InlineTable(t) => t.span(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn despan(&mut self, input: &str) {
|
||||
match self {
|
||||
Self::String(f) => f.despan(input),
|
||||
Self::Integer(f) => f.despan(input),
|
||||
Self::Float(f) => f.despan(input),
|
||||
Self::Boolean(f) => f.despan(input),
|
||||
Self::Datetime(f) => f.despan(input),
|
||||
Self::Array(a) => a.despan(input),
|
||||
Self::InlineTable(t) => t.despan(input),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parse")]
|
||||
impl FromStr for Value {
|
||||
type Err = crate::TomlError;
|
||||
|
||||
/// Parses a value from a &str
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let source = toml_parser::Source::new(s);
|
||||
let mut sink = crate::error::TomlSink::<Option<_>>::new(source);
|
||||
let mut value = crate::parser::parse_value(source, &mut sink);
|
||||
if let Some(err) = sink.into_inner() {
|
||||
Err(err)
|
||||
} else {
|
||||
// Only take the repr and not decor, as its probably not intended
|
||||
value.decor_mut().clear();
|
||||
value.despan(s);
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> From<&'b Self> for Value {
|
||||
fn from(s: &'b Self) -> Self {
|
||||
s.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> From<&'b str> for Value {
|
||||
fn from(s: &'b str) -> Self {
|
||||
s.to_owned().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> From<&'b String> for Value {
|
||||
fn from(s: &'b String) -> Self {
|
||||
s.to_owned().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Value {
|
||||
fn from(s: String) -> Self {
|
||||
Self::String(Formatted::new(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Value {
|
||||
fn from(i: i64) -> Self {
|
||||
Self::Integer(Formatted::new(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Value {
|
||||
fn from(f: f64) -> Self {
|
||||
// Preserve sign of NaN. It may get written to TOML as `-nan`.
|
||||
Self::Float(Formatted::new(f))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Value {
|
||||
fn from(b: bool) -> Self {
|
||||
Self::Boolean(Formatted::new(b))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Datetime> for Value {
|
||||
fn from(d: Datetime) -> Self {
|
||||
Self::Datetime(Formatted::new(d))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Date> for Value {
|
||||
fn from(d: Date) -> Self {
|
||||
let d: Datetime = d.into();
|
||||
d.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Time> for Value {
|
||||
fn from(d: Time) -> Self {
|
||||
let d: Datetime = d.into();
|
||||
d.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Array> for Value {
|
||||
fn from(array: Array) -> Self {
|
||||
Self::Array(array)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InlineTable> for Value {
|
||||
fn from(table: InlineTable) -> Self {
|
||||
Self::InlineTable(table)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Into<Self>> FromIterator<V> for Value {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = V>,
|
||||
{
|
||||
let array: Array = iter.into_iter().collect();
|
||||
Self::Array(array)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Into<Key>, V: Into<Self>> FromIterator<(K, V)> for Value {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = (K, V)>,
|
||||
{
|
||||
let table: InlineTable = iter.into_iter().collect();
|
||||
Self::InlineTable(table)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
impl std::fmt::Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
crate::encode::encode_value(self, f, None, ("", ""))
|
||||
}
|
||||
}
|
||||
|
||||
// `key1 = value1`
|
||||
pub(crate) const DEFAULT_VALUE_DECOR: (&str, &str) = (" ", "");
|
||||
// `{ key = value }`
|
||||
pub(crate) const DEFAULT_TRAILING_VALUE_DECOR: (&str, &str) = (" ", " ");
|
||||
// `[value1, value2]`
|
||||
pub(crate) const DEFAULT_LEADING_VALUE_DECOR: (&str, &str) = ("", "");
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "parse")]
|
||||
#[cfg(feature = "display")]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn from_iter_formatting() {
|
||||
let features = ["node".to_owned(), "mouth".to_owned()];
|
||||
let features: Value = features.iter().cloned().collect();
|
||||
assert_eq!(features.to_string(), r#"["node", "mouth"]"#);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "parse")]
|
||||
#[cfg(feature = "display")]
|
||||
fn string_roundtrip() {
|
||||
Value::from("hello").to_string().parse::<Value>().unwrap();
|
||||
}
|
||||
Vendored
+239
@@ -0,0 +1,239 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
//! Document tree traversal to walk a shared borrow of a document tree.
|
||||
//!
|
||||
//! Each method of the [`Visit`] trait is a hook that can be overridden
|
||||
//! to customize the behavior when mutating the corresponding type of node.
|
||||
//! By default, every method recursively visits the substructure of the
|
||||
//! input by invoking the right visitor method of each of its fields.
|
||||
//!
|
||||
//! ```
|
||||
//! # use toml_edit::{Item, ArrayOfTables, Table, Value};
|
||||
//!
|
||||
//! pub trait Visit<'doc> {
|
||||
//! /* ... */
|
||||
//!
|
||||
//! fn visit_item(&mut self, i: &'doc Item) {
|
||||
//! visit_item(self, i);
|
||||
//! }
|
||||
//!
|
||||
//! /* ... */
|
||||
//! # fn visit_value(&mut self, i: &'doc Value);
|
||||
//! # fn visit_table(&mut self, i: &'doc Table);
|
||||
//! # fn visit_array_of_tables(&mut self, i: &'doc ArrayOfTables);
|
||||
//! }
|
||||
//!
|
||||
//! pub fn visit_item<'doc, V>(v: &mut V, node: &'doc Item)
|
||||
//! where
|
||||
//! V: Visit<'doc> + ?Sized,
|
||||
//! {
|
||||
//! match node {
|
||||
//! Item::None => {}
|
||||
//! Item::Value(value) => v.visit_value(value),
|
||||
//! Item::Table(table) => v.visit_table(table),
|
||||
//! Item::ArrayOfTables(array) => v.visit_array_of_tables(array),
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The API is modeled after [`syn::visit`](https://docs.rs/syn/1/syn/visit).
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! This visitor stores every string in the document.
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "parse")] {
|
||||
//! # use toml_edit::*;
|
||||
//! use toml_edit::visit::*;
|
||||
//!
|
||||
//! #[derive(Default)]
|
||||
//! struct StringCollector<'doc> {
|
||||
//! strings: Vec<&'doc str>,
|
||||
//! }
|
||||
//!
|
||||
//! impl<'doc> Visit<'doc> for StringCollector<'doc> {
|
||||
//! fn visit_string(&mut self, node: &'doc Formatted<String>) {
|
||||
//! self.strings.push(node.value().as_str());
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! let input = r#"
|
||||
//! laputa = "sky-castle"
|
||||
//! the-force = { value = "surrounds-you" }
|
||||
//! "#;
|
||||
//!
|
||||
//! let mut document: DocumentMut = input.parse().unwrap();
|
||||
//! let mut visitor = StringCollector::default();
|
||||
//! visitor.visit_document(&document);
|
||||
//!
|
||||
//! assert_eq!(visitor.strings, vec!["sky-castle", "surrounds-you"]);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! For a more complex example where the visitor has internal state, see `examples/visit.rs`
|
||||
//! [on GitHub](https://github.com/toml-rs/toml/blob/main/crates/toml_edit/examples/visit.rs).
|
||||
|
||||
use crate::{
|
||||
Array, ArrayOfTables, Datetime, DocumentMut, Formatted, InlineTable, Item, Table, TableLike,
|
||||
Value,
|
||||
};
|
||||
|
||||
/// Document tree traversal to mutate an exclusive borrow of a document tree in-place.
|
||||
///
|
||||
/// See the [module documentation](self) for details.
|
||||
pub trait Visit<'doc> {
|
||||
fn visit_document(&mut self, node: &'doc DocumentMut) {
|
||||
visit_document(self, node);
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, node: &'doc Item) {
|
||||
visit_item(self, node);
|
||||
}
|
||||
|
||||
fn visit_table(&mut self, node: &'doc Table) {
|
||||
visit_table(self, node);
|
||||
}
|
||||
|
||||
fn visit_inline_table(&mut self, node: &'doc InlineTable) {
|
||||
visit_inline_table(self, node);
|
||||
}
|
||||
|
||||
fn visit_table_like(&mut self, node: &'doc dyn TableLike) {
|
||||
visit_table_like(self, node);
|
||||
}
|
||||
|
||||
fn visit_table_like_kv(&mut self, key: &'doc str, node: &'doc Item) {
|
||||
visit_table_like_kv(self, key, node);
|
||||
}
|
||||
|
||||
fn visit_array(&mut self, node: &'doc Array) {
|
||||
visit_array(self, node);
|
||||
}
|
||||
|
||||
fn visit_array_of_tables(&mut self, node: &'doc ArrayOfTables) {
|
||||
visit_array_of_tables(self, node);
|
||||
}
|
||||
|
||||
fn visit_value(&mut self, node: &'doc Value) {
|
||||
visit_value(self, node);
|
||||
}
|
||||
|
||||
fn visit_boolean(&mut self, node: &'doc Formatted<bool>) {
|
||||
visit_boolean(self, node);
|
||||
}
|
||||
|
||||
fn visit_datetime(&mut self, node: &'doc Formatted<Datetime>) {
|
||||
visit_datetime(self, node);
|
||||
}
|
||||
|
||||
fn visit_float(&mut self, node: &'doc Formatted<f64>) {
|
||||
visit_float(self, node);
|
||||
}
|
||||
|
||||
fn visit_integer(&mut self, node: &'doc Formatted<i64>) {
|
||||
visit_integer(self, node);
|
||||
}
|
||||
|
||||
fn visit_string(&mut self, node: &'doc Formatted<String>) {
|
||||
visit_string(self, node);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_document<'doc, V>(v: &mut V, node: &'doc DocumentMut)
|
||||
where
|
||||
V: Visit<'doc> + ?Sized,
|
||||
{
|
||||
v.visit_table(node.as_table());
|
||||
}
|
||||
|
||||
pub fn visit_item<'doc, V>(v: &mut V, node: &'doc Item)
|
||||
where
|
||||
V: Visit<'doc> + ?Sized,
|
||||
{
|
||||
match node {
|
||||
Item::None => {}
|
||||
Item::Value(value) => v.visit_value(value),
|
||||
Item::Table(table) => v.visit_table(table),
|
||||
Item::ArrayOfTables(array) => v.visit_array_of_tables(array),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_table<'doc, V>(v: &mut V, node: &'doc Table)
|
||||
where
|
||||
V: Visit<'doc> + ?Sized,
|
||||
{
|
||||
v.visit_table_like(node);
|
||||
}
|
||||
|
||||
pub fn visit_inline_table<'doc, V>(v: &mut V, node: &'doc InlineTable)
|
||||
where
|
||||
V: Visit<'doc> + ?Sized,
|
||||
{
|
||||
v.visit_table_like(node);
|
||||
}
|
||||
|
||||
pub fn visit_table_like<'doc, V>(v: &mut V, node: &'doc dyn TableLike)
|
||||
where
|
||||
V: Visit<'doc> + ?Sized,
|
||||
{
|
||||
for (key, item) in node.iter() {
|
||||
v.visit_table_like_kv(key, item);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_table_like_kv<'doc, V>(v: &mut V, _key: &'doc str, node: &'doc Item)
|
||||
where
|
||||
V: Visit<'doc> + ?Sized,
|
||||
{
|
||||
v.visit_item(node);
|
||||
}
|
||||
|
||||
pub fn visit_array<'doc, V>(v: &mut V, node: &'doc Array)
|
||||
where
|
||||
V: Visit<'doc> + ?Sized,
|
||||
{
|
||||
for value in node.iter() {
|
||||
v.visit_value(value);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_array_of_tables<'doc, V>(v: &mut V, node: &'doc ArrayOfTables)
|
||||
where
|
||||
V: Visit<'doc> + ?Sized,
|
||||
{
|
||||
for table in node.iter() {
|
||||
v.visit_table(table);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_value<'doc, V>(v: &mut V, node: &'doc Value)
|
||||
where
|
||||
V: Visit<'doc> + ?Sized,
|
||||
{
|
||||
match node {
|
||||
Value::String(s) => v.visit_string(s),
|
||||
Value::Integer(i) => v.visit_integer(i),
|
||||
Value::Float(f) => v.visit_float(f),
|
||||
Value::Boolean(b) => v.visit_boolean(b),
|
||||
Value::Datetime(dt) => v.visit_datetime(dt),
|
||||
Value::Array(array) => v.visit_array(array),
|
||||
Value::InlineTable(table) => v.visit_inline_table(table),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! empty_visit {
|
||||
($name: ident, $t: ty) => {
|
||||
fn $name<'doc, V>(_v: &mut V, _node: &'doc $t)
|
||||
where
|
||||
V: Visit<'doc> + ?Sized,
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
empty_visit!(visit_boolean, Formatted<bool>);
|
||||
empty_visit!(visit_datetime, Formatted<Datetime>);
|
||||
empty_visit!(visit_float, Formatted<f64>);
|
||||
empty_visit!(visit_integer, Formatted<i64>);
|
||||
empty_visit!(visit_string, Formatted<String>);
|
||||
Vendored
+256
@@ -0,0 +1,256 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
//! Document tree traversal to mutate an exclusive borrow of a document tree in place.
|
||||
//!
|
||||
//!
|
||||
//! Each method of the [`VisitMut`] trait is a hook that can be overridden
|
||||
//! to customize the behavior when mutating the corresponding type of node.
|
||||
//! By default, every method recursively visits the substructure of the
|
||||
//! input by invoking the right visitor method of each of its fields.
|
||||
//!
|
||||
//! ```
|
||||
//! # use toml_edit::{Item, ArrayOfTables, Table, Value};
|
||||
//!
|
||||
//! pub trait VisitMut {
|
||||
//! /* ... */
|
||||
//!
|
||||
//! fn visit_item_mut(&mut self, i: &mut Item) {
|
||||
//! visit_item_mut(self, i);
|
||||
//! }
|
||||
//!
|
||||
//! /* ... */
|
||||
//! # fn visit_value_mut(&mut self, i: &mut Value);
|
||||
//! # fn visit_table_mut(&mut self, i: &mut Table);
|
||||
//! # fn visit_array_of_tables_mut(&mut self, i: &mut ArrayOfTables);
|
||||
//! }
|
||||
//!
|
||||
//! pub fn visit_item_mut<V>(v: &mut V, node: &mut Item)
|
||||
//! where
|
||||
//! V: VisitMut + ?Sized,
|
||||
//! {
|
||||
//! match node {
|
||||
//! Item::None => {}
|
||||
//! Item::Value(value) => v.visit_value_mut(value),
|
||||
//! Item::Table(table) => v.visit_table_mut(table),
|
||||
//! Item::ArrayOfTables(array) => v.visit_array_of_tables_mut(array),
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The API is modeled after [`syn::visit_mut`](https://docs.rs/syn/1/syn/visit_mut).
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! This visitor replaces every floating point value with its decimal string representation, to
|
||||
//! 2 decimal points.
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "parse")] {
|
||||
//! # #[cfg(feature = "display")] {
|
||||
//! # use toml_edit::*;
|
||||
//! use toml_edit::visit_mut::*;
|
||||
//!
|
||||
//! struct FloatToString;
|
||||
//!
|
||||
//! impl VisitMut for FloatToString {
|
||||
//! fn visit_value_mut(&mut self, node: &mut Value) {
|
||||
//! if let Value::Float(f) = node {
|
||||
//! // Convert the float to a string.
|
||||
//! let mut s = Formatted::new(format!("{:.2}", f.value()));
|
||||
//! // Copy over the formatting.
|
||||
//! std::mem::swap(s.decor_mut(), f.decor_mut());
|
||||
//! *node = Value::String(s);
|
||||
//! }
|
||||
//! // Most of the time, you will also need to call the default implementation to recurse
|
||||
//! // further down the document tree.
|
||||
//! visit_value_mut(self, node);
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! let input = r#"
|
||||
//! banana = 3.26
|
||||
//! table = { apple = 4.5 }
|
||||
//! "#;
|
||||
//!
|
||||
//! let mut document: DocumentMut = input.parse().unwrap();
|
||||
//! let mut visitor = FloatToString;
|
||||
//! visitor.visit_document_mut(&mut document);
|
||||
//!
|
||||
//! let output = r#"
|
||||
//! banana = "3.26"
|
||||
//! table = { apple = "4.50" }
|
||||
//! "#;
|
||||
//!
|
||||
//! assert_eq!(format!("{}", document), output);
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! For a more complex example where the visitor has internal state, see `examples/visit.rs`
|
||||
//! [on GitHub](https://github.com/toml-rs/toml/blob/main/crates/toml_edit/examples/visit.rs).
|
||||
|
||||
use crate::{
|
||||
Array, ArrayOfTables, Datetime, DocumentMut, Formatted, InlineTable, Item, KeyMut, Table,
|
||||
TableLike, Value,
|
||||
};
|
||||
|
||||
/// Document tree traversal to mutate an exclusive borrow of a document tree in-place.
|
||||
///
|
||||
/// See the [module documentation](self) for details.
|
||||
pub trait VisitMut {
|
||||
fn visit_document_mut(&mut self, node: &mut DocumentMut) {
|
||||
visit_document_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_item_mut(&mut self, node: &mut Item) {
|
||||
visit_item_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_table_mut(&mut self, node: &mut Table) {
|
||||
visit_table_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_inline_table_mut(&mut self, node: &mut InlineTable) {
|
||||
visit_inline_table_mut(self, node);
|
||||
}
|
||||
|
||||
/// [`visit_table_mut`](Self::visit_table_mut) and
|
||||
/// [`visit_inline_table_mut`](Self::visit_inline_table_mut) both recurse into this method.
|
||||
fn visit_table_like_mut(&mut self, node: &mut dyn TableLike) {
|
||||
visit_table_like_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_table_like_kv_mut(&mut self, key: KeyMut<'_>, node: &mut Item) {
|
||||
visit_table_like_kv_mut(self, key, node);
|
||||
}
|
||||
|
||||
fn visit_array_mut(&mut self, node: &mut Array) {
|
||||
visit_array_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_array_of_tables_mut(&mut self, node: &mut ArrayOfTables) {
|
||||
visit_array_of_tables_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_value_mut(&mut self, node: &mut Value) {
|
||||
visit_value_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_boolean_mut(&mut self, node: &mut Formatted<bool>) {
|
||||
visit_boolean_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_datetime_mut(&mut self, node: &mut Formatted<Datetime>) {
|
||||
visit_datetime_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_float_mut(&mut self, node: &mut Formatted<f64>) {
|
||||
visit_float_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_integer_mut(&mut self, node: &mut Formatted<i64>) {
|
||||
visit_integer_mut(self, node);
|
||||
}
|
||||
|
||||
fn visit_string_mut(&mut self, node: &mut Formatted<String>) {
|
||||
visit_string_mut(self, node);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_document_mut<V>(v: &mut V, node: &mut DocumentMut)
|
||||
where
|
||||
V: VisitMut + ?Sized,
|
||||
{
|
||||
v.visit_table_mut(node.as_table_mut());
|
||||
}
|
||||
|
||||
pub fn visit_item_mut<V>(v: &mut V, node: &mut Item)
|
||||
where
|
||||
V: VisitMut + ?Sized,
|
||||
{
|
||||
match node {
|
||||
Item::None => {}
|
||||
Item::Value(value) => v.visit_value_mut(value),
|
||||
Item::Table(table) => v.visit_table_mut(table),
|
||||
Item::ArrayOfTables(array) => v.visit_array_of_tables_mut(array),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_table_mut<V>(v: &mut V, node: &mut Table)
|
||||
where
|
||||
V: VisitMut + ?Sized,
|
||||
{
|
||||
v.visit_table_like_mut(node);
|
||||
}
|
||||
|
||||
pub fn visit_inline_table_mut<V>(v: &mut V, node: &mut InlineTable)
|
||||
where
|
||||
V: VisitMut + ?Sized,
|
||||
{
|
||||
v.visit_table_like_mut(node);
|
||||
}
|
||||
|
||||
pub fn visit_table_like_mut<V>(v: &mut V, node: &mut dyn TableLike)
|
||||
where
|
||||
V: VisitMut + ?Sized,
|
||||
{
|
||||
for (key, item) in node.iter_mut() {
|
||||
v.visit_table_like_kv_mut(key, item);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_table_like_kv_mut<V>(v: &mut V, _key: KeyMut<'_>, node: &mut Item)
|
||||
where
|
||||
V: VisitMut + ?Sized,
|
||||
{
|
||||
v.visit_item_mut(node);
|
||||
}
|
||||
|
||||
pub fn visit_array_mut<V>(v: &mut V, node: &mut Array)
|
||||
where
|
||||
V: VisitMut + ?Sized,
|
||||
{
|
||||
for value in node.iter_mut() {
|
||||
v.visit_value_mut(value);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_array_of_tables_mut<V>(v: &mut V, node: &mut ArrayOfTables)
|
||||
where
|
||||
V: VisitMut + ?Sized,
|
||||
{
|
||||
for table in node.iter_mut() {
|
||||
v.visit_table_mut(table);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_value_mut<V>(v: &mut V, node: &mut Value)
|
||||
where
|
||||
V: VisitMut + ?Sized,
|
||||
{
|
||||
match node {
|
||||
Value::String(s) => v.visit_string_mut(s),
|
||||
Value::Integer(i) => v.visit_integer_mut(i),
|
||||
Value::Float(f) => v.visit_float_mut(f),
|
||||
Value::Boolean(b) => v.visit_boolean_mut(b),
|
||||
Value::Datetime(dt) => v.visit_datetime_mut(dt),
|
||||
Value::Array(array) => v.visit_array_mut(array),
|
||||
Value::InlineTable(table) => v.visit_inline_table_mut(table),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! empty_visit_mut {
|
||||
($name: ident, $t: ty) => {
|
||||
fn $name<V>(_v: &mut V, _node: &mut $t)
|
||||
where
|
||||
V: VisitMut + ?Sized,
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
empty_visit_mut!(visit_boolean_mut, Formatted<bool>);
|
||||
empty_visit_mut!(visit_datetime_mut, Formatted<Datetime>);
|
||||
empty_visit_mut!(visit_float_mut, Formatted<f64>);
|
||||
empty_visit_mut!(visit_integer_mut, Formatted<i64>);
|
||||
empty_visit_mut!(visit_string_mut, Formatted<String>);
|
||||
Reference in new issue
Block a user