aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/array.rs59
-rw-r--r--src/array_of_tables.rs1
-rw-r--r--src/de/key.rs13
-rw-r--r--src/de/mod.rs3
-rw-r--r--src/de/table.rs8
-rw-r--r--src/de/table_enum.rs76
-rw-r--r--src/de/value.rs3
-rw-r--r--src/document.rs4
-rw-r--r--src/encode.rs387
-rw-r--r--src/error.rs (renamed from src/parser/errors.rs)86
-rw-r--r--src/inline_table.rs74
-rw-r--r--src/item.rs13
-rw-r--r--src/key.rs134
-rw-r--r--src/lib.rs17
-rw-r--r--src/parser/array.rs2
-rw-r--r--src/parser/datetime.rs40
-rw-r--r--src/parser/error.rs87
-rw-r--r--src/parser/inline_table.rs27
-rw-r--r--src/parser/key.rs36
-rw-r--r--src/parser/mod.rs18
-rw-r--r--src/parser/numbers.rs11
-rw-r--r--src/parser/state.rs23
-rw-r--r--src/parser/strings.rs12
-rw-r--r--src/parser/trivia.rs2
-rw-r--r--src/parser/value.rs4
-rw-r--r--src/raw_string.rs2
-rw-r--r--src/repr.rs22
-rw-r--r--src/ser/map.rs271
-rw-r--r--src/ser/mod.rs5
-rw-r--r--src/ser/value.rs28
-rw-r--r--src/table.rs55
-rw-r--r--src/value.rs12
-rw-r--r--src/visit.rs4
-rw-r--r--src/visit_mut.rs6
34 files changed, 1099 insertions, 446 deletions
diff --git a/src/array.rs b/src/array.rs
index 045b451..377f676 100644
--- a/src/array.rs
+++ b/src/array.rs
@@ -183,9 +183,11 @@ impl 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));
@@ -223,12 +225,14 @@ impl Array {
/// # 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))
@@ -269,12 +273,14 @@ impl Array {
/// # 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)) {
@@ -317,6 +323,56 @@ impl Array {
.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
+ }
+ });
+ }
+
fn value_op<T>(
&mut self,
v: Value,
@@ -333,9 +389,10 @@ impl Array {
}
}
+#[cfg(feature = "display")]
impl std::fmt::Display for Array {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- crate::encode::Encode::encode(self, f, None, ("", ""))
+ crate::encode::encode_array(self, f, None, ("", ""))
}
}
diff --git a/src/array_of_tables.rs b/src/array_of_tables.rs
index c4d7194..2e602a2 100644
--- a/src/array_of_tables.rs
+++ b/src/array_of_tables.rs
@@ -158,6 +158,7 @@ impl<'s> IntoIterator for &'s ArrayOfTables {
}
}
+#[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
diff --git a/src/de/key.rs b/src/de/key.rs
index 3da41df..a3b2825 100644
--- a/src/de/key.rs
+++ b/src/de/key.rs
@@ -62,9 +62,20 @@ impl<'de> serde::de::Deserializer<'de> for KeyDeserializer {
self.deserialize_any(visitor)
}
+ fn deserialize_newtype_struct<V>(
+ self,
+ _name: &'static str,
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: serde::de::Visitor<'de>,
+ {
+ visitor.visit_newtype_struct(self)
+ }
+
serde::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
+ bytes byte_buf map option unit
ignored_any unit_struct tuple_struct tuple identifier
}
}
diff --git a/src/de/mod.rs b/src/de/mod.rs
index 09ea120..9b8a2c7 100644
--- a/src/de/mod.rs
+++ b/src/de/mod.rs
@@ -87,6 +87,7 @@ impl From<Error> for crate::TomlError {
impl std::error::Error for Error {}
/// Convert a value into `T`.
+#[cfg(feature = "parse")]
pub fn from_str<T>(s: &'_ str) -> Result<T, Error>
where
T: DeserializeOwned,
@@ -96,6 +97,7 @@ where
}
/// Convert a value into `T`.
+#[cfg(feature = "parse")]
pub fn from_slice<T>(s: &'_ [u8]) -> Result<T, Error>
where
T: DeserializeOwned,
@@ -125,6 +127,7 @@ impl Deserializer {
}
}
+#[cfg(feature = "parse")]
impl std::str::FromStr for Deserializer {
type Err = Error;
diff --git a/src/de/table.rs b/src/de/table.rs
index 0b6183e..33aa397 100644
--- a/src/de/table.rs
+++ b/src/de/table.rs
@@ -118,7 +118,7 @@ impl crate::InlineTable {
pub(crate) struct TableMapAccess {
iter: indexmap::map::IntoIter<crate::InternalString, crate::table::TableKeyValue>,
span: Option<std::ops::Range<usize>>,
- value: Option<(crate::InternalString, crate::Item)>,
+ value: Option<(crate::Key, crate::Item)>,
}
impl TableMapAccess {
@@ -149,7 +149,7 @@ impl<'de> serde::de::MapAccess<'de> for TableMapAccess {
}
e
});
- self.value = Some((v.key.into(), v.value));
+ self.value = Some((v.key, v.value));
ret
}
None => Ok(None),
@@ -162,13 +162,13 @@ impl<'de> serde::de::MapAccess<'de> for TableMapAccess {
{
match self.value.take() {
Some((k, v)) => {
- let span = v.span();
+ 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.as_str().to_owned());
+ e.add_key(k.get().to_owned());
e
})
}
diff --git a/src/de/table_enum.rs b/src/de/table_enum.rs
index 197ad6e..0ceeab6 100644
--- a/src/de/table_enum.rs
+++ b/src/de/table_enum.rs
@@ -16,6 +16,20 @@ impl<'de> serde::de::VariantAccess<'de> for TableEnumDeserializer {
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(())
@@ -49,9 +63,41 @@ impl<'de> serde::de::VariantAccess<'de> for TableEnumDeserializer {
V: serde::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::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::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 = values
+ let tuple_values: Result<Vec<_>, _> = values
.items
.into_iter()
.enumerate()
@@ -68,17 +114,8 @@ impl<'de> serde::de::VariantAccess<'de> for TableEnumDeserializer {
)),
},
)
- // Fold all values into a `Vec`, or return the first error.
- .fold(Ok(Vec::with_capacity(len)), |result, value_result| {
- result.and_then(move |mut tuple_values| match value_result {
- Ok(value) => {
- tuple_values.push(value);
- Ok(tuple_values)
- }
- // `Result<de::Value, Self::Error>` to `Result<Vec<_>, Self::Error>`
- Err(e) => Err(e),
- })
- })?;
+ .collect();
+ let tuple_values = tuple_values?;
if tuple_values.len() == len {
serde::de::Deserializer::deserialize_seq(
@@ -94,7 +131,7 @@ impl<'de> serde::de::VariantAccess<'de> for TableEnumDeserializer {
}
crate::Item::Value(crate::Value::InlineTable(values)) => {
let values_span = values.span();
- let tuple_values = values
+ let tuple_values: Result<Vec<_>, _> = values
.items
.into_iter()
.enumerate()
@@ -111,17 +148,8 @@ impl<'de> serde::de::VariantAccess<'de> for TableEnumDeserializer {
)),
},
)
- // Fold all values into a `Vec`, or return the first error.
- .fold(Ok(Vec::with_capacity(len)), |result, value_result| {
- result.and_then(move |mut tuple_values| match value_result {
- Ok(value) => {
- tuple_values.push(value);
- Ok(tuple_values)
- }
- // `Result<de::Value, Self::Error>` to `Result<Vec<_>, Self::Error>`
- Err(e) => Err(e),
- })
- })?;
+ .collect();
+ let tuple_values = tuple_values?;
if tuple_values.len() == len {
serde::de::Deserializer::deserialize_seq(
diff --git a/src/de/value.rs b/src/de/value.rs
index 3984287..ba6ce6d 100644
--- a/src/de/value.rs
+++ b/src/de/value.rs
@@ -5,7 +5,7 @@ use crate::de::Error;
/// Deserialization implementation for TOML [values][crate::Value].
///
-/// Can be creater either directly from TOML strings, using [`std::str::FromStr`],
+/// Can be created either directly from TOML strings, using [`std::str::FromStr`],
/// or from parsed [values][crate::Value] using [`serde::de::IntoDeserializer::into_deserializer`].
///
/// # Example
@@ -241,6 +241,7 @@ impl crate::Item {
}
}
+#[cfg(feature = "parse")]
impl std::str::FromStr for ValueDeserializer {
type Err = Error;
diff --git a/src/document.rs b/src/document.rs
index 67dd293..f20e61a 100644
--- a/src/document.rs
+++ b/src/document.rs
@@ -1,6 +1,5 @@
use std::str::FromStr;
-use crate::parser;
use crate::table::Iter;
use crate::{Item, RawString, Table};
@@ -78,12 +77,13 @@ impl Default for Document {
}
}
+#[cfg(feature = "parse")]
impl FromStr for Document {
type Err = crate::TomlError;
/// Parses a document from a &str
fn from_str(s: &str) -> Result<Self, Self::Err> {
- let mut d = parser::parse_document(s)?;
+ let mut d = crate::parser::parse_document(s)?;
d.despan();
Ok(d)
}
diff --git a/src/encode.rs b/src/encode.rs
index 9940f28..30b153a 100644
--- a/src/encode.rs
+++ b/src/encode.rs
@@ -13,218 +13,185 @@ use crate::value::{
};
use crate::{Array, InlineTable, Item, Table, Value};
-pub(crate) trait Encode {
- fn encode(
- &self,
- buf: &mut dyn Write,
- input: Option<&str>,
- default_decor: (&str, &str),
- ) -> Result;
+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(())
}
-impl Encode for Key {
- fn encode(
- &self,
- buf: &mut dyn Write,
- input: Option<&str>,
- default_decor: (&str, &str),
- ) -> Result {
- let decor = self.decor();
- decor.prefix_encode(buf, input, default_decor.0)?;
-
- if let Some(input) = input {
- let repr = self
- .as_repr()
- .map(Cow::Borrowed)
- .unwrap_or_else(|| Cow::Owned(self.default_repr()));
- repr.encode(buf, input)?;
+fn encode_key_path(
+ this: &[Key],
+ 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 {
- let repr = self.display_repr();
- write!(buf, "{}", repr)?;
- };
+ write!(buf, ".")?;
+ dotted_decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
+ }
+
+ encode_key(key, buf, input)?;
- decor.suffix_encode(buf, input, default_decor.1)?;
- Ok(())
+ if last {
+ leaf_decor.suffix_encode(buf, input, default_decor.1)?;
+ } else {
+ dotted_decor.suffix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.1)?;
+ }
}
+ Ok(())
}
-impl<'k> Encode for &'k [Key] {
- fn encode(
- &self,
- buf: &mut dyn Write,
- input: Option<&str>,
- default_decor: (&str, &str),
- ) -> Result {
- for (i, key) in self.iter().enumerate() {
- let first = i == 0;
- let last = i + 1 == self.len();
-
- let prefix = if first {
- default_decor.0
- } else {
- DEFAULT_KEY_PATH_DECOR.0
- };
- let suffix = if last {
- default_decor.1
- } else {
- DEFAULT_KEY_PATH_DECOR.1
- };
+pub(crate) fn encode_key_path_ref(
+ this: &[&Key],
+ 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();
- if !first {
- write!(buf, ".")?;
- }
- key.encode(buf, input, (prefix, suffix))?;
+ let first = i == 0;
+ let last = i + 1 == this.len();
+
+ if first {
+ leaf_decor.prefix_encode(buf, input, default_decor.0)?;
+ } else {
+ write!(buf, ".")?;
+ dotted_decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
}
- Ok(())
- }
-}
-impl<'k> Encode for &'k [&'k Key] {
- fn encode(
- &self,
- buf: &mut dyn Write,
- input: Option<&str>,
- default_decor: (&str, &str),
- ) -> Result {
- for (i, key) in self.iter().enumerate() {
- let first = i == 0;
- let last = i + 1 == self.len();
-
- let prefix = if first {
- default_decor.0
- } else {
- DEFAULT_KEY_PATH_DECOR.0
- };
- let suffix = if last {
- default_decor.1
- } else {
- DEFAULT_KEY_PATH_DECOR.1
- };
+ encode_key(key, buf, input)?;
- if !first {
- write!(buf, ".")?;
- }
- key.encode(buf, input, (prefix, suffix))?;
+ if last {
+ leaf_decor.suffix_encode(buf, input, default_decor.1)?;
+ } else {
+ dotted_decor.suffix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.1)?;
}
- Ok(())
}
+ Ok(())
}
-impl<T> Encode for Formatted<T>
-where
- T: ValueRepr,
-{
- fn encode(
- &self,
- buf: &mut dyn Write,
- input: Option<&str>,
- default_decor: (&str, &str),
- ) -> Result {
- let decor = self.decor();
- decor.prefix_encode(buf, input, default_decor.0)?;
-
- if let Some(input) = input {
- let repr = self
- .as_repr()
- .map(Cow::Borrowed)
- .unwrap_or_else(|| Cow::Owned(self.default_repr()));
- repr.encode(buf, input)?;
- } else {
- let repr = self.display_repr();
- write!(buf, "{}", repr)?;
- };
+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(())
- }
+ decor.suffix_encode(buf, input, default_decor.1)?;
+ Ok(())
}
-impl Encode for Array {
- fn encode(
- &self,
- buf: &mut dyn Write,
- input: Option<&str>,
- default_decor: (&str, &str),
- ) -> Result {
- let decor = self.decor();
- decor.prefix_encode(buf, input, default_decor.0)?;
- write!(buf, "[")?;
-
- for (i, elem) in self.iter().enumerate() {
- let inner_decor;
- if i == 0 {
- inner_decor = DEFAULT_LEADING_VALUE_DECOR;
- } else {
- inner_decor = DEFAULT_VALUE_DECOR;
- write!(buf, ",")?;
- }
- elem.encode(buf, input, inner_decor)?;
- }
- if self.trailing_comma() && !self.is_empty() {
+pub(crate) fn encode_array(
+ this: &Array,
+ buf: &mut dyn Write,
+ input: Option<&str>,
+ default_decor: (&str, &str),
+) -> Result {
+ let decor = this.decor();
+ decor.prefix_encode(buf, input, default_decor.0)?;
+ write!(buf, "[")?;
+
+ 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;
write!(buf, ",")?;
}
+ encode_value(elem, buf, input, inner_decor)?;
+ }
+ if this.trailing_comma() && !this.is_empty() {
+ write!(buf, ",")?;
+ }
- self.trailing().encode_with_default(buf, input, "")?;
- write!(buf, "]")?;
- decor.suffix_encode(buf, input, default_decor.1)?;
+ this.trailing().encode_with_default(buf, input, "")?;
+ write!(buf, "]")?;
+ decor.suffix_encode(buf, input, default_decor.1)?;
- Ok(())
- }
+ Ok(())
}
-impl Encode for InlineTable {
- fn encode(
- &self,
- buf: &mut dyn Write,
- input: Option<&str>,
- default_decor: (&str, &str),
- ) -> Result {
- let decor = self.decor();
- decor.prefix_encode(buf, input, default_decor.0)?;
- write!(buf, "{{")?;
- self.preamble().encode_with_default(buf, input, "")?;
-
- let children = self.get_values();
- let len = children.len();
- for (i, (key_path, value)) in children.into_iter().enumerate() {
- if i != 0 {
- write!(buf, ",")?;
- }
- let inner_decor = if i == len - 1 {
- DEFAULT_TRAILING_VALUE_DECOR
- } else {
- DEFAULT_VALUE_DECOR
- };
- key_path
- .as_slice()
- .encode(buf, input, DEFAULT_INLINE_KEY_DECOR)?;
- write!(buf, "=")?;
- value.encode(buf, input, inner_decor)?;
+pub(crate) fn encode_table(
+ this: &InlineTable,
+ buf: &mut dyn Write,
+ input: Option<&str>,
+ default_decor: (&str, &str),
+) -> Result {
+ let decor = this.decor();
+ decor.prefix_encode(buf, input, default_decor.0)?;
+ write!(buf, "{{")?;
+ this.preamble().encode_with_default(buf, input, "")?;
+
+ let children = this.get_values();
+ let len = children.len();
+ for (i, (key_path, value)) in children.into_iter().enumerate() {
+ if i != 0 {
+ write!(buf, ",")?;
}
+ 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)?;
+ write!(buf, "=")?;
+ encode_value(value, buf, input, inner_decor)?;
+ }
- write!(buf, "}}")?;
- decor.suffix_encode(buf, input, default_decor.1)?;
+ write!(buf, "}}")?;
+ decor.suffix_encode(buf, input, default_decor.1)?;
- Ok(())
- }
+ Ok(())
}
-impl Encode for Value {
- fn encode(
- &self,
- buf: &mut dyn Write,
- input: Option<&str>,
- default_decor: (&str, &str),
- ) -> Result {
- match self {
- Value::String(repr) => repr.encode(buf, input, default_decor),
- Value::Integer(repr) => repr.encode(buf, input, default_decor),
- Value::Float(repr) => repr.encode(buf, input, default_decor),
- Value::Boolean(repr) => repr.encode(buf, input, default_decor),
- Value::Datetime(repr) => repr.encode(buf, input, default_decor),
- Value::Array(array) => array.encode(buf, input, default_decor),
- Value::InlineTable(table) => table.encode(buf, input, default_decor),
- }
+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),
}
}
@@ -275,11 +242,7 @@ where
for kv in table.items.values() {
match kv.value {
Item::Table(ref t) => {
- let mut key = kv.key.clone();
- if t.is_dotted() {
- // May have newlines and generally isn't written for standard tables
- key.decor_mut().clear();
- }
+ let key = kv.key.clone();
path.push(key);
visit_nested_tables(t, path, false, callback)?;
path.pop();
@@ -332,7 +295,7 @@ fn visit_table(
};
table.decor.prefix_encode(buf, input, default_decor.0)?;
write!(buf, "[[")?;
- path.encode(buf, input, DEFAULT_KEY_PATH_DECOR)?;
+ encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR)?;
write!(buf, "]]")?;
table.decor.suffix_encode(buf, input, default_decor.1)?;
writeln!(buf)?;
@@ -345,16 +308,16 @@ fn visit_table(
};
table.decor.prefix_encode(buf, input, default_decor.0)?;
write!(buf, "[")?;
- path.encode(buf, input, DEFAULT_KEY_PATH_DECOR)?;
+ encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR)?;
write!(buf, "]")?;
table.decor.suffix_encode(buf, input, default_decor.1)?;
writeln!(buf)?;
}
// print table body
for (key_path, value) in children {
- key_path.as_slice().encode(buf, input, DEFAULT_KEY_DECOR)?;
+ encode_key_path_ref(&key_path, buf, input, DEFAULT_KEY_DECOR)?;
write!(buf, "=")?;
- value.encode(buf, input, DEFAULT_VALUE_DECOR)?;
+ encode_value(value, buf, input, DEFAULT_VALUE_DECOR)?;
writeln!(buf)?;
}
Ok(())
@@ -390,7 +353,7 @@ pub(crate) fn to_string_repr(
'\u{8}' => output.push_str("\\b"),
'\u{9}' => output.push_str("\\t"),
'\u{a}' => match style {
- StringStyle::NewlineTripple => output.push('\n'),
+ StringStyle::NewlineTriple => output.push('\n'),
StringStyle::OnelineSingle => output.push_str("\\n"),
_ => unreachable!(),
},
@@ -412,59 +375,56 @@ pub(crate) fn to_string_repr(
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum StringStyle {
- NewlineTripple,
- OnelineTripple,
+ NewlineTriple,
+ OnelineTriple,
OnelineSingle,
}
impl StringStyle {
fn literal_start(self) -> &'static str {
match self {
- Self::NewlineTripple => "'''\n",
- Self::OnelineTripple => "'''",
+ Self::NewlineTriple => "'''\n",
+ Self::OnelineTriple => "'''",
Self::OnelineSingle => "'",
}
}
fn literal_end(self) -> &'static str {
match self {
- Self::NewlineTripple => "'''",
- Self::OnelineTripple => "'''",
+ Self::NewlineTriple => "'''",
+ Self::OnelineTriple => "'''",
Self::OnelineSingle => "'",
}
}
fn standard_start(self) -> &'static str {
match self {
- Self::NewlineTripple => "\"\"\"\n",
- // note: OnelineTripple can happen if do_pretty wants to do
+ Self::NewlineTriple => "\"\"\"\n",
+ // note: OnelineTriple can happen if do_pretty wants to do
// '''it's one line'''
// but literal == false
- Self::OnelineTripple | Self::OnelineSingle => "\"",
+ Self::OnelineTriple | Self::OnelineSingle => "\"",
}
}
fn standard_end(self) -> &'static str {
match self {
- Self::NewlineTripple => "\"\"\"",
- // note: OnelineTripple can happen if do_pretty wants to do
+ Self::NewlineTriple => "\"\"\"",
+ // note: OnelineTriple can happen if do_pretty wants to do
// '''it's one line'''
// but literal == false
- Self::OnelineTripple | Self::OnelineSingle => "\"",
+ Self::OnelineTriple | Self::OnelineSingle => "\"",
}
}
}
fn infer_style(value: &str) -> (StringStyle, bool) {
- // For doing pretty prints we store in a new String
- // because there are too many cases where pretty cannot
- // work. We need to determine:
+ // We need to determine:
// - if we are a "multi-line" pretty (if there are \n)
// - if ['''] appears if multi or ['] if single
// - if there are any invalid control characters
//
// Doing it any other way would require multiple passes
// to determine if a pretty string works or not.
- let mut out = String::with_capacity(value.len() * 2);
let mut ty = StringStyle::OnelineSingle;
// found consecutive single quotes
let mut max_found_singles = 0;
@@ -490,18 +450,17 @@ fn infer_style(value: &str) -> (StringStyle, bool) {
'\\' => {
prefer_literal = true;
}
- '\n' => ty = StringStyle::NewlineTripple,
+ '\n' => ty = StringStyle::NewlineTriple,
// Escape codes are needed if any ascii control
// characters are present, including \b \f \r.
c if c <= '\u{1f}' || c == '\u{7f}' => can_be_pretty = false,
_ => {}
}
- out.push(ch);
} else {
// the string cannot be represented as pretty,
// still check if it should be multiline
if ch == '\n' {
- ty = StringStyle::NewlineTripple;
+ ty = StringStyle::NewlineTriple;
}
}
}
@@ -513,7 +472,7 @@ fn infer_style(value: &str) -> (StringStyle, bool) {
can_be_pretty = false;
}
if !can_be_pretty {
- debug_assert!(ty != StringStyle::OnelineTripple);
+ debug_assert!(ty != StringStyle::OnelineTriple);
return (ty, false);
}
if found_singles > max_found_singles {
@@ -522,7 +481,7 @@ fn infer_style(value: &str) -> (StringStyle, bool) {
debug_assert!(max_found_singles < 3);
if ty == StringStyle::OnelineSingle && max_found_singles >= 1 {
// no newlines, but must use ''' because it has ' in it
- ty = StringStyle::OnelineTripple;
+ ty = StringStyle::OnelineTriple;
}
(ty, true)
}
diff --git a/src/parser/errors.rs b/src/error.rs
index 859ed53..a983019 100644
--- a/src/parser/errors.rs
+++ b/src/error.rs
@@ -1,12 +1,6 @@
use std::error::Error as StdError;
use std::fmt::{Display, Formatter, Result};
-use crate::parser::prelude::*;
-use crate::Key;
-
-use winnow::error::ContextError;
-use winnow::error::ParseError;
-
/// Type representing a TOML parse error
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct TomlError {
@@ -17,7 +11,14 @@ pub struct TomlError {
}
impl TomlError {
- pub(crate) fn new(error: ParseError<Input<'_>, ContextError>, mut original: Input<'_>) -> Self {
+ #[cfg(feature = "parse")]
+ pub(crate) fn new(
+ error: winnow::error::ParseError<
+ crate::parser::prelude::Input<'_>,
+ winnow::error::ContextError,
+ >,
+ mut original: crate::parser::prelude::Input<'_>,
+ ) -> Self {
use winnow::stream::Stream;
let offset = error.offset();
@@ -166,7 +167,6 @@ fn translate_position(input: &[u8], index: usize) -> (usize, usize) {
None => 0,
};
let line = input[0..line_start].iter().filter(|b| **b == b'\n').count();
- let line = line;
let column = std::str::from_utf8(&input[line_start..=index])
.map(|s| s.chars().count() - 1)
@@ -244,73 +244,3 @@ mod test_translate_position {
assert_eq!(position, (1, 2));
}
}
-
-#[derive(Debug, Clone)]
-pub(crate) enum CustomError {
- DuplicateKey {
- key: String,
- table: Option<Vec<Key>>,
- },
- DottedKeyExtendWrongType {
- key: Vec<Key>,
- actual: &'static str,
- },
- OutOfRange,
- #[cfg_attr(feature = "unbounded", allow(dead_code))]
- RecursionLimitExceeded,
-}
-
-impl CustomError {
- pub(crate) fn duplicate_key(path: &[Key], i: usize) -> Self {
- assert!(i < path.len());
- let key = &path[i];
- let repr = key.display_repr();
- Self::DuplicateKey {
- key: repr.into(),
- table: Some(path[..i].to_vec()),
- }
- }
-
- pub(crate) fn extend_wrong_type(path: &[Key], i: usize, actual: &'static str) -> Self {
- assert!(i < path.len());
- Self::DottedKeyExtendWrongType {
- key: path[..=i].to_vec(),
- actual,
- }
- }
-}
-
-impl StdError for CustomError {
- fn description(&self) -> &'static str {
- "TOML parse error"
- }
-}
-
-impl Display for CustomError {
- fn fmt(&self, f: &mut Formatter<'_>) -> Result {
- match self {
- CustomError::DuplicateKey { key, table } => {
- if let Some(table) = table {
- if table.is_empty() {
- write!(f, "duplicate key `{}` in document root", key)
- } else {
- let path = table.iter().map(|k| k.get()).collect::<Vec<_>>().join(".");
- write!(f, "duplicate key `{}` in table `{}`", key, path)
- }
- } else {
- write!(f, "duplicate key `{}`", key)
- }
- }
- CustomError::DottedKeyExtendWrongType { key, actual } => {
- let path = key.iter().map(|k| k.get()).collect::<Vec<_>>().join(".");
- write!(
- f,
- "dotted key `{}` attempted to extend non-table type ({})",
- path, actual
- )
- }
- CustomError::OutOfRange => write!(f, "value is out of range"),
- CustomError::RecursionLimitExceeded => write!(f, "recursion limit exceded"),
- }
- }
-}
diff --git a/src/inline_table.rs b/src/inline_table.rs
index 3dc6c0c..316637d 100644
--- a/src/inline_table.rs
+++ b/src/inline_table.rs
@@ -11,6 +11,8 @@ use crate::{InternalString, Item, KeyMut, RawString, Table, Value};
pub struct InlineTable {
// `preamble` represents whitespaces in an empty table
preamble: RawString,
+ // 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>>,
@@ -55,10 +57,10 @@ impl InlineTable {
values
}
- pub(crate) fn append_values<'s, 'c>(
+ pub(crate) fn append_values<'s>(
&'s self,
parent: &[&'s Key],
- values: &'c mut Vec<(Vec<&'s Key>, &'s Value)>,
+ values: &mut Vec<(Vec<&'s Key>, &'s Value)>,
) {
for value in self.items.values() {
let mut path = parent.to_vec();
@@ -133,6 +135,36 @@ impl InlineTable {
}
}
+ /// 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::Document;
+ /// let mut doc = "[a]\n[a.b]\n".parse::<Document>().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;
@@ -153,14 +185,28 @@ impl InlineTable {
&self.decor
}
+ /// Returns an accessor to a key's formatting
+ pub fn key(&self, key: &str) -> Option<&'_ Key> {
+ self.items.get(key).map(|kv| &kv.key)
+ }
+
+ /// Returns an accessor to a key's formatting
+ pub fn key_mut(&mut self, key: &str) -> Option<KeyMut<'_>> {
+ self.items.get_mut(key).map(|kv| kv.key.as_mut())
+ }
+
/// Returns the decor associated with a given key of the table.
+ #[deprecated(since = "0.21.1", note = "Replaced with `key_mut`")]
pub fn key_decor_mut(&mut self, key: &str) -> Option<&mut Decor> {
- self.items.get_mut(key).map(|kv| &mut kv.key.decor)
+ #![allow(deprecated)]
+ self.items.get_mut(key).map(|kv| kv.key.leaf_decor_mut())
}
/// Returns the decor associated with a given key of the table.
+ #[deprecated(since = "0.21.1", note = "Replaced with `key_mut`")]
pub fn key_decor(&self, key: &str) -> Option<&Decor> {
- self.items.get(key).map(|kv| &kv.key.decor)
+ #![allow(deprecated)]
+ self.items.get(key).map(|kv| kv.key.leaf_decor())
}
/// Set whitespace after before element
@@ -383,9 +429,10 @@ impl InlineTable {
}
}
+#[cfg(feature = "display")]
impl std::fmt::Display for InlineTable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- crate::encode::Encode::encode(self, f, None, ("", ""))
+ crate::encode::encode_table(self, f, None, ("", ""))
}
}
@@ -436,13 +483,14 @@ impl<'s> IntoIterator for &'s InlineTable {
}
fn decorate_inline_table(table: &mut InlineTable) {
- for (key_decor, value) in table
+ for (mut key, value) in table
.items
.iter_mut()
- .filter(|&(_, ref kv)| kv.value.is_value())
- .map(|(_, kv)| (&mut kv.key.decor, kv.value.as_value_mut().unwrap()))
+ .filter(|(_, kv)| kv.value.is_value())
+ .map(|(_, kv)| (kv.key.as_mut(), kv.value.as_value_mut().unwrap()))
{
- key_decor.clear();
+ key.leaf_decor_mut().clear();
+ key.dotted_decor_mut().clear();
value.decor_mut().clear();
}
}
@@ -530,10 +578,18 @@ impl TableLike for InlineTable {
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)
+ }
fn key_decor_mut(&mut self, key: &str) -> Option<&mut Decor> {
+ #![allow(deprecated)]
self.key_decor_mut(key)
}
fn key_decor(&self, key: &str) -> Option<&Decor> {
+ #![allow(deprecated)]
self.key_decor(key)
}
}
diff --git a/src/item.rs b/src/item.rs
index 2025fd9..b58806e 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -7,9 +7,10 @@ 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)]
+#[derive(Debug, Default)]
pub enum Item {
/// Type representing none.
+ #[default]
None,
/// Type representing value.
Value(Value),
@@ -328,12 +329,7 @@ impl Clone for Item {
}
}
-impl Default for Item {
- fn default() -> Self {
- Item::None
- }
-}
-
+#[cfg(feature = "parse")]
impl FromStr for Item {
type Err = crate::TomlError;
@@ -344,6 +340,7 @@ impl FromStr for Item {
}
}
+#[cfg(feature = "display")]
impl std::fmt::Display for Item {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
@@ -363,6 +360,7 @@ impl std::fmt::Display for Item {
///
/// # Examples
/// ```rust
+/// # #[cfg(feature = "display")] {
/// # use snapbox::assert_eq;
/// # use toml_edit::*;
/// let mut table = Table::default();
@@ -377,6 +375,7 @@ impl std::fmt::Display for Item {
/// key2 = 42
/// key3 = ["hello", '\, world']
/// "#);
+/// # }
/// ```
pub fn value<V: Into<Value>>(v: V) -> Item {
Item::Value(v.into())
diff --git a/src/key.rs b/src/key.rs
index c1ee165..2f4c30a 100644
--- a/src/key.rs
+++ b/src/key.rs
@@ -1,9 +1,6 @@
use std::borrow::Cow;
use std::str::FromStr;
-use crate::encode::{to_string_repr, StringStyle};
-use crate::parser;
-use crate::parser::key::is_unquoted_char;
use crate::repr::{Decor, Repr};
use crate::InternalString;
@@ -33,7 +30,8 @@ use crate::InternalString;
pub struct Key {
key: InternalString,
pub(crate) repr: Option<Repr>,
- pub(crate) decor: Decor,
+ pub(crate) leaf_decor: Decor,
+ pub(crate) dotted_decor: Decor,
}
impl Key {
@@ -42,13 +40,15 @@ impl Key {
Self {
key: key.into(),
repr: None,
- decor: Default::default(),
+ 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)
}
@@ -59,8 +59,20 @@ impl Key {
}
/// While creating the `Key`, add `Decor` to it
- pub fn with_decor(mut self, decor: Decor) -> Self {
- self.decor = decor;
+ #[deprecated(since = "0.21.1", note = "Replaced with `with_leaf_decor`")]
+ pub fn with_decor(self, decor: Decor) -> Self {
+ self.with_leaf_decor(decor)
+ }
+
+ /// 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
}
@@ -84,11 +96,13 @@ impl Key {
}
/// Returns the default raw representation.
+ #[cfg(feature = "display")]
pub fn default_repr(&self) -> Repr {
to_key_repr(&self.key)
}
/// 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())
@@ -99,13 +113,35 @@ impl Key {
}
/// Returns the surrounding whitespace
+ #[deprecated(since = "0.21.1", note = "Replaced with `decor_mut`")]
pub fn decor_mut(&mut self) -> &mut Decor {
- &mut self.decor
+ self.leaf_decor_mut()
+ }
+
+ /// 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
+ #[deprecated(since = "0.21.1", note = "Replaced with `decor`")]
pub fn decor(&self) -> &Decor {
- &self.decor
+ self.leaf_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
}
/// Returns the location within the original document
@@ -115,7 +151,8 @@ impl Key {
}
pub(crate) fn despan(&mut self, input: &str) {
- self.decor.despan(input);
+ self.leaf_decor.despan(input);
+ self.dotted_decor.despan(input);
if let Some(repr) = &mut self.repr {
repr.despan(input)
}
@@ -123,18 +160,21 @@ impl Key {
/// Auto formats the key.
pub fn fmt(&mut self) {
- self.repr = Some(to_key_repr(&self.key));
- self.decor.clear();
+ self.repr = None;
+ self.leaf_decor.clear();
+ self.dotted_decor.clear();
}
+ #[cfg(feature = "parse")]
fn try_parse_simple(s: &str) -> Result<Key, crate::TomlError> {
- let mut key = parser::parse_key(s)?;
+ let mut key = crate::parser::parse_key(s)?;
key.despan(s);
Ok(key)
}
+ #[cfg(feature = "parse")]
fn try_parse_path(s: &str) -> Result<Vec<Key>, crate::TomlError> {
- let mut keys = parser::parse_key_path(s)?;
+ let mut keys = crate::parser::parse_key_path(s)?;
for key in &mut keys {
key.despan(s);
}
@@ -148,7 +188,8 @@ impl Clone for Key {
Self {
key: self.key.clone(),
repr: self.repr.clone(),
- decor: self.decor.clone(),
+ leaf_decor: self.leaf_decor.clone(),
+ dotted_decor: self.dotted_decor.clone(),
}
}
}
@@ -209,12 +250,14 @@ impl PartialEq<String> for Key {
}
}
+#[cfg(feature = "display")]
impl std::fmt::Display for Key {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- crate::encode::Encode::encode(self, f, None, ("", ""))
+ crate::encode::encode_key(self, f, None)
}
}
+#[cfg(feature = "parse")]
impl FromStr for Key {
type Err = crate::TomlError;
@@ -226,11 +269,33 @@ impl FromStr for Key {
}
}
+#[cfg(feature = "display")]
fn to_key_repr(key: &str) -> Repr {
- if key.as_bytes().iter().copied().all(is_unquoted_char) && !key.is_empty() {
- Repr::new_unchecked(key)
- } else {
- to_string_repr(key, Some(StringStyle::OnelineSingle), Some(false))
+ #[cfg(feature = "parse")]
+ {
+ if key
+ .as_bytes()
+ .iter()
+ .copied()
+ .all(crate::parser::key::is_unquoted_char)
+ && !key.is_empty()
+ {
+ Repr::new_unchecked(key)
+ } else {
+ crate::encode::to_string_repr(
+ key,
+ Some(crate::encode::StringStyle::OnelineSingle),
+ Some(false),
+ )
+ }
+ }
+ #[cfg(not(feature = "parse"))]
+ {
+ crate::encode::to_string_repr(
+ key,
+ Some(crate::encode::StringStyle::OnelineSingle),
+ Some(false),
+ )
}
}
@@ -265,7 +330,7 @@ impl From<Key> for InternalString {
}
}
-/// A mutable reference to a `Key`
+/// A mutable reference to a `Key`'s formatting
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct KeyMut<'k> {
key: &'k mut Key,
@@ -283,25 +348,51 @@ impl<'k> KeyMut<'k> {
}
/// 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
+ #[deprecated(since = "0.21.1", note = "Replaced with `decor_mut`")]
pub fn decor_mut(&mut self) -> &mut Decor {
+ #![allow(deprecated)]
self.key.decor_mut()
}
+ /// 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
+ #[deprecated(since = "0.21.1", note = "Replaced with `decor`")]
pub fn decor(&self) -> &Decor {
+ #![allow(deprecated)]
self.key.decor()
}
+ /// 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()
@@ -337,6 +428,7 @@ impl<'s> PartialEq<String> for KeyMut<'s> {
}
}
+#[cfg(feature = "display")]
impl<'k> std::fmt::Display for KeyMut<'k> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.key, f)
diff --git a/src/lib.rs b/src/lib.rs
index 80c0ddd..25e3d20 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -14,6 +14,8 @@
//! # Example
//!
//! ```rust
+//! # #[cfg(feature = "parse")] {
+//! # #[cfg(feature = "display")] {
//! use toml_edit::{Document, value};
//!
//! let toml = r#"
@@ -32,47 +34,54 @@
//! c = { d = "hello" }
//! "#;
//! assert_eq!(doc.to_string(), expected);
+//! # }
+//! # }
//! ```
//!
//! ## Controlling formatting
//!
//! By default, values are created with default formatting
//! ```rust
+//! # #[cfg(feature = "display")] {
//! let mut doc = toml_edit::Document::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")] {
//! let mut doc = toml_edit::Document::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:
//!
-//! * Scattered array of tables (tables are reordered by default, see [test]).
-//! * Order of dotted keys, see [issue](https://github.com/ordian/toml_edit/issues/163).
+//! * Order of dotted keys, see [issue](https://github.com/toml-rs/toml/issues/163).
//!
//! [`toml`]: https://docs.rs/toml/latest/toml/
-//! [test]: https://github.com/ordian/toml_edit/blob/f09bd5d075fdb7d2ef8d9bb3270a34506c276753/tests/test_valid.rs#L84
mod array;
mod array_of_tables;
mod document;
+#[cfg(feature = "display")]
mod encode;
+mod error;
mod index;
mod inline_table;
mod internal_string;
mod item;
mod key;
+#[cfg(feature = "parse")]
mod parser;
mod raw_string;
mod repr;
@@ -92,6 +101,7 @@ pub use crate::array_of_tables::{
ArrayOfTables, ArrayOfTablesIntoIter, ArrayOfTablesIter, ArrayOfTablesIterMut,
};
pub use crate::document::Document;
+pub use crate::error::TomlError;
pub use crate::inline_table::{
InlineEntry, InlineOccupiedEntry, InlineTable, InlineTableIntoIter, InlineTableIter,
InlineTableIterMut, InlineVacantEntry,
@@ -99,7 +109,6 @@ pub use crate::inline_table::{
pub use crate::internal_string::InternalString;
pub use crate::item::{array, table, value, Item};
pub use crate::key::{Key, KeyMut};
-pub use crate::parser::TomlError;
pub use crate::raw_string::RawString;
pub use crate::repr::{Decor, Formatted, Repr};
pub use crate::table::{
diff --git a/src/parser/array.rs b/src/parser/array.rs
index e3b1f3f..0783191 100644
--- a/src/parser/array.rs
+++ b/src/parser/array.rs
@@ -81,6 +81,8 @@ pub(crate) fn array_value<'i>(
}
#[cfg(test)]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
mod test {
use super::*;
diff --git a/src/parser/datetime.rs b/src/parser/datetime.rs
index 6e89b97..945dc69 100644
--- a/src/parser/datetime.rs
+++ b/src/parser/datetime.rs
@@ -1,6 +1,6 @@
use std::ops::RangeInclusive;
-use crate::parser::errors::CustomError;
+use crate::parser::error::CustomError;
use crate::parser::prelude::*;
use crate::parser::trivia::from_utf8_unchecked;
@@ -9,6 +9,7 @@ use winnow::combinator::alt;
use winnow::combinator::cut_err;
use winnow::combinator::opt;
use winnow::combinator::preceded;
+use winnow::stream::Stream as _;
use winnow::token::one_of;
use winnow::token::take_while;
use winnow::trace::trace;
@@ -53,12 +54,35 @@ pub(crate) fn date_time(input: &mut Input<'_>) -> PResult<Datetime> {
// full-date = date-fullyear "-" date-month "-" date-mday
pub(crate) fn full_date(input: &mut Input<'_>) -> PResult<Date> {
- trace(
- "full-date",
- (date_fullyear, b'-', cut_err((date_month, b'-', date_mday)))
- .map(|(year, _, (month, _, day))| Date { year, month, day }),
- )
- .parse_next(input)
+ trace("full-date", full_date_).parse_next(input)
+}
+
+fn full_date_(input: &mut Input<'_>) -> PResult<Date> {
+ let year = date_fullyear.parse_next(input)?;
+ let _ = b'-'.parse_next(input)?;
+ let month = cut_err(date_month).parse_next(input)?;
+ let _ = cut_err(b'-').parse_next(input)?;
+ let day_start = input.checkpoint();
+ let day = cut_err(date_mday).parse_next(input)?;
+
+ let is_leap_year = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
+ let max_days_in_month = match month {
+ 2 if is_leap_year => 29,
+ 2 => 28,
+ 4 | 6 | 9 | 11 => 30,
+ _ => 31,
+ };
+ if max_days_in_month < day {
+ input.reset(day_start);
+ return Err(winnow::error::ErrMode::from_external_error(
+ input,
+ winnow::error::ErrorKind::Verify,
+ CustomError::OutOfRange,
+ )
+ .cut());
+ }
+
+ Ok(Date { year, month, day })
}
// partial-time = time-hour ":" time-minute ":" time-second [time-secfrac]
@@ -239,6 +263,8 @@ pub(crate) fn unsigned_digits<'i, const MIN: usize, const MAX: usize>(
const DIGIT: RangeInclusive<u8> = b'0'..=b'9';
#[cfg(test)]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
mod test {
use super::*;
diff --git a/src/parser/error.rs b/src/parser/error.rs
new file mode 100644
index 0000000..22e8e66
--- /dev/null
+++ b/src/parser/error.rs
@@ -0,0 +1,87 @@
+use std::error::Error as StdError;
+use std::fmt::{Display, Formatter, Result};
+
+use crate::Key;
+
+#[derive(Debug, Clone)]
+pub(crate) enum CustomError {
+ DuplicateKey {
+ key: String,
+ table: Option<Vec<Key>>,
+ },
+ DottedKeyExtendWrongType {
+ key: Vec<Key>,
+ actual: &'static str,
+ },
+ OutOfRange,
+ #[cfg_attr(feature = "unbounded", allow(dead_code))]
+ RecursionLimitExceeded,
+}
+
+impl CustomError {
+ pub(crate) fn duplicate_key(path: &[Key], i: usize) -> Self {
+ assert!(i < path.len());
+ let key = &path[i];
+ let repr = key
+ .as_repr()
+ .and_then(|key| key.as_raw().as_str())
+ .map(|s| s.to_owned())
+ .unwrap_or_else(|| {
+ #[cfg(feature = "display")]
+ {
+ key.default_repr().as_raw().as_str().unwrap().to_owned()
+ }
+ #[cfg(not(feature = "display"))]
+ {
+ format!("{:?}", key.get())
+ }
+ });
+ Self::DuplicateKey {
+ key: repr,
+ table: Some(path[..i].to_vec()),
+ }
+ }
+
+ pub(crate) fn extend_wrong_type(path: &[Key], i: usize, actual: &'static str) -> Self {
+ assert!(i < path.len());
+ Self::DottedKeyExtendWrongType {
+ key: path[..=i].to_vec(),
+ actual,
+ }
+ }
+}
+
+impl StdError for CustomError {
+ fn description(&self) -> &'static str {
+ "TOML parse error"
+ }
+}
+
+impl Display for CustomError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ match self {
+ CustomError::DuplicateKey { key, table } => {
+ if let Some(table) = table {
+ if table.is_empty() {
+ write!(f, "duplicate key `{}` in document root", key)
+ } else {
+ let path = table.iter().map(|k| k.get()).collect::<Vec<_>>().join(".");
+ write!(f, "duplicate key `{}` in table `{}`", key, path)
+ }
+ } else {
+ write!(f, "duplicate key `{}`", key)
+ }
+ }
+ CustomError::DottedKeyExtendWrongType { key, actual } => {
+ let path = key.iter().map(|k| k.get()).collect::<Vec<_>>().join(".");
+ write!(
+ f,
+ "dotted key `{}` attempted to extend non-table type ({})",
+ path, actual
+ )
+ }
+ CustomError::OutOfRange => write!(f, "value is out of range"),
+ CustomError::RecursionLimitExceeded => write!(f, "recursion limit exceeded"),
+ }
+ }
+}
diff --git a/src/parser/inline_table.rs b/src/parser/inline_table.rs
index 994e003..c2e6619 100644
--- a/src/parser/inline_table.rs
+++ b/src/parser/inline_table.rs
@@ -5,7 +5,7 @@ use winnow::token::one_of;
use winnow::trace::trace;
use crate::key::Key;
-use crate::parser::errors::CustomError;
+use crate::parser::error::CustomError;
use crate::parser::key::key;
use crate::parser::prelude::*;
use crate::parser::trivia::ws;
@@ -44,6 +44,16 @@ fn table_from_pairs(
for (path, kv) in v {
let table = descend_path(&mut root, &path)?;
+
+ // "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 {
+ return Err(CustomError::DuplicateKey {
+ key: kv.key.get().into(),
+ table: None,
+ });
+ }
+
let key: InternalString = kv.key.get_internal().into();
match table.items.entry(key) {
Entry::Vacant(o) => {
@@ -64,15 +74,26 @@ fn descend_path<'a>(
mut table: &'a mut InlineTable,
path: &'a [Key],
) -> Result<&'a mut InlineTable, CustomError> {
+ let dotted = !path.is_empty();
for (i, key) in path.iter().enumerate() {
let entry = table.entry_format(key).or_insert_with(|| {
let mut new_table = InlineTable::new();
- new_table.set_dotted(true);
+ new_table.set_implicit(dotted);
+ new_table.set_dotted(dotted);
Value::InlineTable(new_table)
});
match *entry {
Value::InlineTable(ref mut 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.
+ if dotted && !sweet_child_of_mine.is_implicit() {
+ return Err(CustomError::DuplicateKey {
+ key: key.get().into(),
+ table: None,
+ });
+ }
table = sweet_child_of_mine;
}
ref v => {
@@ -144,6 +165,8 @@ fn keyval<'i>(
}
#[cfg(test)]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
mod test {
use super::*;
diff --git a/src/parser/key.rs b/src/parser/key.rs
index 12715da..e72b195 100644
--- a/src/parser/key.rs
+++ b/src/parser/key.rs
@@ -7,7 +7,7 @@ use winnow::token::take_while;
use winnow::trace::trace;
use crate::key::Key;
-use crate::parser::errors::CustomError;
+use crate::parser::error::CustomError;
use crate::parser::prelude::*;
use crate::parser::strings::{basic_string, literal_string};
use crate::parser::trivia::{from_utf8_unchecked, ws};
@@ -18,13 +18,13 @@ use crate::RawString;
// key = simple-key / dotted-key
// dotted-key = simple-key 1*( dot-sep simple-key )
pub(crate) fn key(input: &mut Input<'_>) -> PResult<Vec<Key>> {
- trace(
+ let mut key_path = trace(
"dotted-key",
separated1(
(ws.span(), simple_key, ws.span()).map(|(pre, (raw, key), suffix)| {
Key::new(key)
.with_repr_unchecked(Repr::new_unchecked(raw))
- .with_decor(Decor::new(
+ .with_dotted_decor(Decor::new(
RawString::with_span(pre),
RawString::with_span(suffix),
))
@@ -38,7 +38,31 @@ pub(crate) fn key(input: &mut Input<'_>) -> PResult<Vec<Key>> {
Ok::<_, CustomError>(k)
}),
)
- .parse_next(input)
+ .parse_next(input)?;
+
+ let mut leaf_decor = Decor::new("", "");
+ {
+ let first_dotted_decor = key_path
+ .first_mut()
+ .expect("always at least one key")
+ .dotted_decor_mut();
+ if let Some(prefix) = first_dotted_decor.prefix().cloned() {
+ leaf_decor.set_prefix(prefix);
+ first_dotted_decor.set_prefix("");
+ }
+ }
+ let last_key = &mut key_path.last_mut().expect("always at least one key");
+ {
+ let last_dotted_decor = last_key.dotted_decor_mut();
+ if let Some(suffix) = last_dotted_decor.suffix().cloned() {
+ leaf_decor.set_suffix(suffix);
+ last_dotted_decor.set_suffix("");
+ }
+ }
+
+ *last_key.leaf_decor_mut() = leaf_decor;
+
+ Ok(key_path)
}
// simple-key = quoted-key / unquoted-key
@@ -88,6 +112,8 @@ const UNQUOTED_CHAR: (
const DOT_SEP: u8 = b'.';
#[cfg(test)]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
mod test {
use super::*;
@@ -96,7 +122,7 @@ mod test {
let cases = [
("a", "a"),
(r#""hello\n ""#, "hello\n "),
- (r#"'hello\n '"#, "hello\\n "),
+ (r"'hello\n '", "hello\\n "),
];
for (input, expected) in cases {
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 1b3cc4f..e032202 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -3,7 +3,7 @@
pub(crate) mod array;
pub(crate) mod datetime;
pub(crate) mod document;
-pub(crate) mod errors;
+pub(crate) mod error;
pub(crate) mod inline_table;
pub(crate) mod key;
pub(crate) mod numbers;
@@ -13,7 +13,7 @@ pub(crate) mod table;
pub(crate) mod trivia;
pub(crate) mod value;
-pub use errors::TomlError;
+pub use crate::error::TomlError;
pub(crate) fn parse_document(raw: &str) -> Result<crate::Document, TomlError> {
use prelude::*;
@@ -95,11 +95,11 @@ pub(crate) mod prelude {
#[cfg(not(feature = "unbounded"))]
impl RecursionCheck {
- pub(crate) fn check_depth(depth: usize) -> Result<(), super::errors::CustomError> {
+ pub(crate) fn check_depth(depth: usize) -> Result<(), super::error::CustomError> {
if depth < 128 {
Ok(())
} else {
- Err(super::errors::CustomError::RecursionLimitExceeded)
+ Err(super::error::CustomError::RecursionLimitExceeded)
}
}
@@ -114,7 +114,7 @@ pub(crate) mod prelude {
Err(winnow::error::ErrMode::from_external_error(
input,
winnow::error::ErrorKind::Eof,
- super::errors::CustomError::RecursionLimitExceeded,
+ super::error::CustomError::RecursionLimitExceeded,
))
}
}
@@ -126,7 +126,7 @@ pub(crate) mod prelude {
#[cfg(feature = "unbounded")]
impl RecursionCheck {
- pub(crate) fn check_depth(_depth: usize) -> Result<(), super::errors::CustomError> {
+ pub(crate) fn check_depth(_depth: usize) -> Result<(), super::error::CustomError> {
Ok(())
}
@@ -140,6 +140,8 @@ pub(crate) mod prelude {
}
#[cfg(test)]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
mod test {
use super::*;
@@ -182,10 +184,10 @@ hosts = [
"omega"
]
- 'some.wierd .stuff' = """
+ 'some.weird .stuff' = """
like
that
- # """ # this broke my sintax highlighting
+ # """ # this broke my syntax highlighting
" also. like " = '''
that
'''
diff --git a/src/parser/numbers.rs b/src/parser/numbers.rs
index 6e4757f..9681526 100644
--- a/src/parser/numbers.rs
+++ b/src/parser/numbers.rs
@@ -301,7 +301,7 @@ pub(crate) fn inf(input: &mut Input<'_>) -> PResult<f64> {
const INF: &[u8] = b"inf";
// nan = %x6e.61.6e ; nan
pub(crate) fn nan(input: &mut Input<'_>) -> PResult<f64> {
- tag(NAN).value(f64::NAN).parse_next(input)
+ tag(NAN).value(f64::NAN.copysign(1.0)).parse_next(input)
}
const NAN: &[u8] = b"nan";
@@ -319,6 +319,8 @@ pub(crate) const HEXDIG: (RangeInclusive<u8>, RangeInclusive<u8>, RangeInclusive
(DIGIT, b'A'..=b'F', b'a'..=b'f');
#[cfg(test)]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
mod test {
use super::*;
@@ -353,6 +355,7 @@ mod test {
fn assert_float_eq(actual: f64, expected: f64) {
if expected.is_nan() {
assert!(actual.is_nan());
+ assert_eq!(expected.is_sign_positive(), actual.is_sign_positive());
} else if expected.is_infinite() {
assert!(actual.is_infinite());
assert_eq!(expected.is_sign_positive(), actual.is_sign_positive());
@@ -376,9 +379,9 @@ mod test {
("9_224_617.445_991_228_313", 9_224_617.445_991_227),
("-1.7976931348623157e+308", std::f64::MIN),
("1.7976931348623157e+308", std::f64::MAX),
- ("nan", f64::NAN),
- ("+nan", f64::NAN),
- ("-nan", f64::NAN),
+ ("nan", f64::NAN.copysign(1.0)),
+ ("+nan", f64::NAN.copysign(1.0)),
+ ("-nan", f64::NAN.copysign(-1.0)),
("inf", f64::INFINITY),
("+inf", f64::INFINITY),
("-inf", f64::NEG_INFINITY),
diff --git a/src/parser/state.rs b/src/parser/state.rs
index efa884d..187dd5f 100644
--- a/src/parser/state.rs
+++ b/src/parser/state.rs
@@ -1,5 +1,5 @@
use crate::key::Key;
-use crate::parser::errors::CustomError;
+use crate::parser::error::CustomError;
use crate::repr::Decor;
use crate::table::TableKeyValue;
use crate::{ArrayOfTables, Document, InternalString, Item, RawString, Table};
@@ -39,26 +39,21 @@ impl ParseState {
pub(crate) fn on_keyval(
&mut self,
- mut path: Vec<Key>,
+ path: Vec<Key>,
mut kv: TableKeyValue,
) -> Result<(), CustomError> {
{
let mut prefix = self.trailing.take();
- let first_key = if path.is_empty() {
- &mut kv.key
- } else {
- &mut path[0]
- };
let prefix = match (
prefix.take(),
- first_key.decor.prefix().and_then(|d| d.span()),
+ kv.key.leaf_decor.prefix().and_then(|d| d.span()),
) {
(Some(p), Some(k)) => Some(p.start..k.end),
(Some(p), None) | (None, Some(p)) => Some(p),
(None, None) => None,
};
- first_key
- .decor
+ kv.key
+ .leaf_decor
.set_prefix(prefix.map(RawString::with_span).unwrap_or_default());
}
@@ -94,7 +89,7 @@ impl ParseState {
Ok(())
}
- pub(crate) fn start_aray_table(
+ pub(crate) fn start_array_table(
&mut self,
path: Vec<Key>,
decor: Decor,
@@ -217,9 +212,9 @@ impl ParseState {
Ok(())
}
- pub(crate) fn descend_path<'t, 'k>(
+ pub(crate) fn descend_path<'t>(
mut table: &'t mut Table,
- path: &'k [Key],
+ path: &[Key],
dotted: bool,
) -> Result<&'t mut Table, CustomError> {
for (i, key) in path.iter().enumerate() {
@@ -297,7 +292,7 @@ impl ParseState {
.take()
.map(RawString::with_span)
.unwrap_or_default();
- self.start_aray_table(
+ self.start_array_table(
path,
Decor::new(leading, RawString::with_span(trailing)),
span,
diff --git a/src/parser/strings.rs b/src/parser/strings.rs
index 26f9cc2..675b5c6 100644
--- a/src/parser/strings.rs
+++ b/src/parser/strings.rs
@@ -21,7 +21,7 @@ use winnow::token::tag;
use winnow::token::take_while;
use winnow::trace::trace;
-use crate::parser::errors::CustomError;
+use crate::parser::error::CustomError;
use crate::parser::numbers::HEXDIG;
use crate::parser::prelude::*;
use crate::parser::trivia::{from_utf8_unchecked, newline, ws, ws_newlines, NON_ASCII, WSCHAR};
@@ -363,6 +363,8 @@ fn mll_quotes<'i>(
}
#[cfg(test)]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
mod test {
use super::*;
@@ -440,10 +442,10 @@ The quick brown \
#[test]
fn literal_string() {
let inputs = [
- r#"'C:\Users\nodejs\templates'"#,
- r#"'\\ServerX\admin$\system32\'"#,
+ r"'C:\Users\nodejs\templates'",
+ r"'\\ServerX\admin$\system32\'",
r#"'Tom "Dubs" Preston-Werner'"#,
- r#"'<\i\c*\s*>'"#,
+ r"'<\i\c*\s*>'",
];
for input in &inputs {
@@ -456,7 +458,7 @@ The quick brown \
#[test]
fn ml_literal_string() {
let inputs = [
- r#"'''I [dw]on't need \d{2} apples'''"#,
+ r"'''I [dw]on't need \d{2} apples'''",
r#"''''one_quote''''"#,
];
for input in &inputs {
diff --git a/src/parser/trivia.rs b/src/parser/trivia.rs
index a359805..4575fb1 100644
--- a/src/parser/trivia.rs
+++ b/src/parser/trivia.rs
@@ -120,6 +120,8 @@ pub(crate) fn line_trailing(input: &mut Input<'_>) -> PResult<std::ops::Range<us
}
#[cfg(test)]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
mod test {
use super::*;
diff --git a/src/parser/value.rs b/src/parser/value.rs
index 14cd951..33300ec 100644
--- a/src/parser/value.rs
+++ b/src/parser/value.rs
@@ -121,6 +121,8 @@ fn apply_raw(mut val: Value, span: std::ops::Range<usize>) -> Result<Value, std:
}
#[cfg(test)]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
mod test {
use super::*;
@@ -131,7 +133,7 @@ mod test {
"-239",
"1e200",
"9_224_617.445_991_228_313",
- r#"'''I [dw]on't need \d{2} apples'''"#,
+ r"'''I [dw]on't need \d{2} apples'''",
r#"'''
The first newline is
trimmed in raw strings.
diff --git a/src/raw_string.rs b/src/raw_string.rs
index c5961f1..53714a1 100644
--- a/src/raw_string.rs
+++ b/src/raw_string.rs
@@ -80,6 +80,7 @@ impl RawString {
}
}
+ #[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') {
@@ -88,6 +89,7 @@ impl RawString {
Ok(())
}
+ #[cfg(feature = "display")]
pub(crate) fn encode_with_default(
&self,
buf: &mut dyn std::fmt::Write,
diff --git a/src/repr.rs b/src/repr.rs
index d4ab6c2..ad41bbf 100644
--- a/src/repr.rs
+++ b/src/repr.rs
@@ -44,11 +44,13 @@ where
}
/// 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())
@@ -82,7 +84,7 @@ where
/// Auto formats the value.
pub fn fmt(&mut self) {
- self.repr = Some(self.value.to_repr());
+ self.repr = None;
}
}
@@ -103,20 +105,33 @@ where
}
}
+#[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::encode(self, f, None, ("", ""))
+ 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 {}
+}
+
/// TOML-encoded value
#[derive(Eq, PartialEq, Clone, Hash)]
pub struct Repr {
@@ -144,6 +159,7 @@ impl Repr {
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)
}
@@ -185,6 +201,7 @@ impl Decor {
self.prefix.as_ref()
}
+ #[cfg(feature = "display")]
pub(crate) fn prefix_encode(
&self,
buf: &mut dyn std::fmt::Write,
@@ -208,6 +225,7 @@ impl Decor {
self.suffix.as_ref()
}
+ #[cfg(feature = "display")]
pub(crate) fn suffix_encode(
&self,
buf: &mut dyn std::fmt::Write,
diff --git a/src/ser/map.rs b/src/ser/map.rs
index d743e3d..47e56ba 100644
--- a/src/ser/map.rs
+++ b/src/ser/map.rs
@@ -1,4 +1,4 @@
-use super::{Error, KeySerializer};
+use super::{Error, KeySerializer, SerializeValueArray, ValueSerializer};
#[doc(hidden)]
pub enum SerializeMap {
@@ -165,7 +165,6 @@ impl serde::ser::SerializeMap for SerializeInlineTable {
where
T: serde::ser::Serialize,
{
- self.key = None;
self.key = Some(input.serialize(KeySerializer)?);
Ok(())
}
@@ -174,7 +173,8 @@ impl serde::ser::SerializeMap for SerializeInlineTable {
where
T: serde::ser::Serialize,
{
- let res = value.serialize(super::ValueSerializer {});
+ let mut value_serializer = MapValueSerializer::new();
+ let res = value.serialize(&mut value_serializer);
match res {
Ok(item) => {
let key = self.key.take().unwrap();
@@ -185,7 +185,7 @@ impl serde::ser::SerializeMap for SerializeInlineTable {
self.items.insert(key, kv);
}
Err(e) => {
- if e != Error::UnsupportedNone {
+ if !(e == Error::UnsupportedNone && value_serializer.is_none) {
return Err(e);
}
}
@@ -210,7 +210,8 @@ impl serde::ser::SerializeStruct for SerializeInlineTable {
where
T: serde::ser::Serialize,
{
- let res = value.serialize(super::ValueSerializer {});
+ let mut value_serializer = MapValueSerializer::new();
+ let res = value.serialize(&mut value_serializer);
match res {
Ok(item) => {
let kv = crate::table::TableKeyValue::new(
@@ -220,7 +221,7 @@ impl serde::ser::SerializeStruct for SerializeInlineTable {
self.items.insert(crate::InternalString::from(key), kv);
}
Err(e) => {
- if e != Error::UnsupportedNone {
+ if !(e == Error::UnsupportedNone && value_serializer.is_none) {
return Err(e);
}
}
@@ -403,3 +404,261 @@ impl serde::ser::Serializer for DatetimeFieldSerializer {
Err(Error::DateInvalid)
}
}
+
+#[derive(Default)]
+struct MapValueSerializer {
+ is_none: bool,
+}
+
+impl MapValueSerializer {
+ fn new() -> Self {
+ Self { is_none: false }
+ }
+}
+
+impl serde::ser::Serializer for &mut MapValueSerializer {
+ type Ok = crate::Value;
+ type Error = Error;
+ type SerializeSeq = super::SerializeValueArray;
+ type SerializeTuple = super::SerializeValueArray;
+ type SerializeTupleStruct = super::SerializeValueArray;
+ type SerializeTupleVariant = super::SerializeTupleVariant;
+ type SerializeMap = super::SerializeMap;
+ type SerializeStruct = super::SerializeMap;
+ type SerializeStructVariant = super::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::UnsupportedNone)
+ }
+
+ fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
+ where
+ T: serde::ser::Serialize,
+ {
+ 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: ?Sized>(
+ self,
+ name: &'static str,
+ value: &T,
+ ) -> Result<Self::Ok, Self::Error>
+ where
+ T: serde::ser::Serialize,
+ {
+ ValueSerializer::new().serialize_newtype_struct(name, value)
+ }
+
+ fn serialize_newtype_variant<T: ?Sized>(
+ self,
+ name: &'static str,
+ variant_index: u32,
+ variant: &'static str,
+ value: &T,
+ ) -> Result<Self::Ok, Self::Error>
+ where
+ T: serde::ser::Serialize,
+ {
+ 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 type SerializeTupleVariant = SerializeVariant<SerializeValueArray>;
+pub type SerializeStructVariant = SerializeVariant<SerializeMap>;
+
+pub struct SerializeVariant<T> {
+ variant: &'static str,
+ inner: T,
+}
+
+impl SerializeVariant<SerializeValueArray> {
+ pub(crate) fn tuple(variant: &'static str, len: usize) -> Self {
+ Self {
+ variant,
+ inner: SerializeValueArray::with_capacity(len),
+ }
+ }
+}
+
+impl SerializeVariant<SerializeMap> {
+ pub(crate) fn struct_(variant: &'static str, len: usize) -> Self {
+ Self {
+ variant,
+ inner: SerializeMap::table_with_capacity(len),
+ }
+ }
+}
+
+impl serde::ser::SerializeTupleVariant for SerializeVariant<SerializeValueArray> {
+ type Ok = crate::Value;
+ type Error = Error;
+
+ fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+ where
+ T: serde::ser::Serialize,
+ {
+ serde::ser::SerializeSeq::serialize_element(&mut self.inner, value)
+ }
+
+ fn end(self) -> Result<Self::Ok, Self::Error> {
+ let inner = serde::ser::SerializeSeq::end(self.inner)?;
+ let mut items = crate::table::KeyValuePairs::new();
+ let kv = crate::table::TableKeyValue::new(
+ crate::Key::new(self.variant),
+ crate::Item::Value(inner),
+ );
+ items.insert(crate::InternalString::from(self.variant), kv);
+ Ok(crate::Value::InlineTable(crate::InlineTable::with_pairs(
+ items,
+ )))
+ }
+}
+
+impl serde::ser::SerializeStructVariant for SerializeVariant<SerializeMap> {
+ 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::ser::Serialize + ?Sized,
+ {
+ serde::ser::SerializeStruct::serialize_field(&mut self.inner, key, value)
+ }
+
+ #[inline]
+ fn end(self) -> Result<Self::Ok, Self::Error> {
+ let inner = serde::ser::SerializeStruct::end(self.inner)?;
+ let mut items = crate::table::KeyValuePairs::new();
+ let kv = crate::table::TableKeyValue::new(
+ crate::Key::new(self.variant),
+ crate::Item::Value(inner),
+ );
+ items.insert(crate::InternalString::from(self.variant), kv);
+ Ok(crate::Value::InlineTable(crate::InlineTable::with_pairs(
+ items,
+ )))
+ }
+}
diff --git a/src/ser/mod.rs b/src/ser/mod.rs
index 2c31020..ba31708 100644
--- a/src/ser/mod.rs
+++ b/src/ser/mod.rs
@@ -24,7 +24,7 @@ pub enum Error {
OutOfRange(Option<&'static str>),
/// `None` could not be serialized to TOML
UnsupportedNone,
- /// Key was not convertable to `String` for serializing to TOML
+ /// Key was not convertible to `String` for serializing to TOML
KeyNotString,
/// A serialized date was invalid
DateInvalid,
@@ -84,6 +84,7 @@ impl std::error::Error for Error {}
/// 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: ?Sized>(value: &T) -> Result<Vec<u8>, Error>
where
T: serde::ser::Serialize,
@@ -127,6 +128,7 @@ where
/// let toml = toml_edit::ser::to_string(&config).unwrap();
/// println!("{}", toml)
/// ```
+#[cfg(feature = "display")]
pub fn to_string<T: ?Sized>(value: &T) -> Result<String, Error>
where
T: serde::ser::Serialize,
@@ -138,6 +140,7 @@ where
///
/// 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: ?Sized>(value: &T) -> Result<String, Error>
where
T: serde::ser::Serialize,
diff --git a/src/ser/value.rs b/src/ser/value.rs
index d29390a..47a17a3 100644
--- a/src/ser/value.rs
+++ b/src/ser/value.rs
@@ -60,10 +60,10 @@ impl serde::ser::Serializer for ValueSerializer {
type SerializeSeq = super::SerializeValueArray;
type SerializeTuple = super::SerializeValueArray;
type SerializeTupleStruct = super::SerializeValueArray;
- type SerializeTupleVariant = super::SerializeValueArray;
+ type SerializeTupleVariant = super::SerializeTupleVariant;
type SerializeMap = super::SerializeMap;
type SerializeStruct = super::SerializeMap;
- type SerializeStructVariant = serde::ser::Impossible<Self::Ok, Self::Error>;
+ type SerializeStructVariant = super::SerializeStructVariant;
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
Ok(v.into())
@@ -108,7 +108,17 @@ impl serde::ser::Serializer for ValueSerializer {
self.serialize_f64(v as f64)
}
- fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
+ 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())
}
@@ -205,10 +215,10 @@ impl serde::ser::Serializer for ValueSerializer {
self,
_name: &'static str,
_variant_index: u32,
- _variant: &'static str,
+ variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
- self.serialize_seq(Some(len))
+ Ok(super::SerializeTupleVariant::tuple(variant, len))
}
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
@@ -233,11 +243,11 @@ impl serde::ser::Serializer for ValueSerializer {
fn serialize_struct_variant(
self,
- name: &'static str,
+ _name: &'static str,
_variant_index: u32,
- _variant: &'static str,
- _len: usize,
+ variant: &'static str,
+ len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
- Err(Error::UnsupportedType(Some(name)))
+ Ok(super::SerializeStructVariant::struct_(variant, len))
}
}
diff --git a/src/table.rs b/src/table.rs
index 45d6d61..cc0d2b2 100644
--- a/src/table.rs
+++ b/src/table.rs
@@ -70,10 +70,10 @@ impl Table {
values
}
- fn append_values<'s, 'c>(
+ fn append_values<'s>(
&'s self,
parent: &[&'s Key],
- values: &'c mut Vec<(Vec<&'s Key>, &'s Value)>,
+ values: &mut Vec<(Vec<&'s Key>, &'s Value)>,
) {
for value in self.items.values() {
let mut path = parent.to_vec();
@@ -165,11 +165,15 @@ impl Table {
/// In the document above, tables `target` and `target."x86_64/windows.json"` are implicit.
///
/// ```
+ /// # #[cfg(feature = "parse")] {
+ /// # #[cfg(feature = "display")] {
/// use toml_edit::Document;
/// let mut doc = "[a]\n[a.b]\n".parse::<Document>().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;
@@ -214,14 +218,28 @@ impl Table {
&self.decor
}
+ /// Returns an accessor to a key's formatting
+ pub fn key(&self, key: &str) -> Option<&'_ Key> {
+ self.items.get(key).map(|kv| &kv.key)
+ }
+
+ /// Returns an accessor to a key's formatting
+ pub fn key_mut(&mut self, key: &str) -> Option<KeyMut<'_>> {
+ self.items.get_mut(key).map(|kv| kv.key.as_mut())
+ }
+
/// Returns the decor associated with a given key of the table.
+ #[deprecated(since = "0.21.1", note = "Replaced with `key_mut`")]
pub fn key_decor_mut(&mut self, key: &str) -> Option<&mut Decor> {
- self.items.get_mut(key).map(|kv| &mut kv.key.decor)
+ #![allow(deprecated)]
+ self.items.get_mut(key).map(|kv| kv.key.leaf_decor_mut())
}
/// Returns the decor associated with a given key of the table.
+ #[deprecated(since = "0.21.1", note = "Replaced with `key_mut`")]
pub fn key_decor(&self, key: &str) -> Option<&Decor> {
- self.items.get(key).map(|kv| &kv.key.decor)
+ #![allow(deprecated)]
+ self.items.get(key).map(|kv| kv.key.leaf_decor())
}
/// Returns the location within the original document
@@ -413,15 +431,15 @@ impl Table {
}
}
+#[cfg(feature = "display")]
impl std::fmt::Display for Table {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- use crate::encode::Encode;
let children = self.get_values();
// print table body
for (key_path, value) in children {
- key_path.as_slice().encode(f, None, DEFAULT_KEY_DECOR)?;
+ crate::encode::encode_key_path_ref(&key_path, f, None, DEFAULT_KEY_DECOR)?;
write!(f, "=")?;
- value.encode(f, None, DEFAULT_VALUE_DECOR)?;
+ crate::encode::encode_value(value, f, None, DEFAULT_VALUE_DECOR)?;
writeln!(f)?;
}
Ok(())
@@ -471,13 +489,14 @@ impl<'s> IntoIterator for &'s Table {
pub(crate) type KeyValuePairs = IndexMap<InternalString, TableKeyValue>;
fn decorate_table(table: &mut Table) {
- for (key_decor, value) in table
+ for (mut key, value) in table
.items
.iter_mut()
- .filter(|&(_, ref kv)| kv.value.is_value())
- .map(|(_, kv)| (&mut kv.key.decor, kv.value.as_value_mut().unwrap()))
+ .filter(|(_, kv)| kv.value.is_value())
+ .map(|(_, kv)| (kv.key.as_mut(), kv.value.as_value_mut().unwrap()))
{
- key_decor.clear();
+ key.leaf_decor_mut().clear();
+ key.dotted_decor_mut().clear();
value.decor_mut().clear();
}
}
@@ -557,9 +576,15 @@ pub trait TableLike: crate::private::Sealed {
/// 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<'_>>;
/// Returns the decor associated with a given key of the table.
+ #[deprecated(since = "0.21.1", note = "Replaced with `key_mut`")]
fn key_decor_mut(&mut self, key: &str) -> Option<&mut Decor>;
/// Returns the decor associated with a given key of the table.
+ #[deprecated(since = "0.21.1", note = "Replaced with `key_mut`")]
fn key_decor(&self, key: &str) -> Option<&Decor>;
}
@@ -617,10 +642,18 @@ impl TableLike for Table {
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)
+ }
fn key_decor_mut(&mut self, key: &str) -> Option<&mut Decor> {
+ #![allow(deprecated)]
self.key_decor_mut(key)
}
fn key_decor(&self, key: &str) -> Option<&Decor> {
+ #![allow(deprecated)]
self.key_decor(key)
}
}
diff --git a/src/value.rs b/src/value.rs
index f10da9a..f416434 100644
--- a/src/value.rs
+++ b/src/value.rs
@@ -4,7 +4,6 @@ use std::str::FromStr;
use toml_datetime::*;
use crate::key::Key;
-use crate::parser;
use crate::repr::{Decor, Formatted};
use crate::{Array, InlineTable, InternalString, RawString};
@@ -190,10 +189,12 @@ impl Value {
/// 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);
@@ -231,12 +232,13 @@ impl Value {
}
}
+#[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> {
- parser::parse_value(s)
+ crate::parser::parse_value(s)
}
}
@@ -284,6 +286,7 @@ impl From<i64> for Value {
impl From<f64> for Value {
fn from(f: f64) -> Self {
+ // Preserve sign of NaN. It may get written to TOML as `-nan`.
Value::Float(Formatted::new(f))
}
}
@@ -346,9 +349,10 @@ impl<K: Into<Key>, V: Into<Value>> FromIterator<(K, V)> for Value {
}
}
+#[cfg(feature = "display")]
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- crate::encode::Encode::encode(self, f, None, ("", ""))
+ crate::encode::encode_value(self, f, None, ("", ""))
}
}
@@ -360,6 +364,8 @@ pub(crate) const DEFAULT_TRAILING_VALUE_DECOR: (&str, &str) = (" ", " ");
pub(crate) const DEFAULT_LEADING_VALUE_DECOR: (&str, &str) = ("", "");
#[cfg(test)]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
mod tests {
use super::*;
diff --git a/src/visit.rs b/src/visit.rs
index 1bc640a..6d7be0b 100644
--- a/src/visit.rs
+++ b/src/visit.rs
@@ -43,6 +43,7 @@
//! This visitor stores every string in the document.
//!
//! ```
+//! # #[cfg(feature = "parse")] {
//! # use toml_edit::*;
//! use toml_edit::visit::*;
//!
@@ -67,10 +68,11 @@
//! 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/ordian/toml_edit/blob/master/examples/visit.rs).
+//! [on GitHub](https://github.com/toml-rs/toml/blob/main/crates/toml_edit/examples/visit.rs).
use crate::{
Array, ArrayOfTables, Datetime, Document, Formatted, InlineTable, Item, Table, TableLike, Value,
diff --git a/src/visit_mut.rs b/src/visit_mut.rs
index 2c2af97..c823cfb 100644
--- a/src/visit_mut.rs
+++ b/src/visit_mut.rs
@@ -45,6 +45,8 @@
//! 2 decimal points.
//!
//! ```
+//! # #[cfg(feature = "parse")] {
+//! # #[cfg(feature = "display")] {
//! # use toml_edit::*;
//! use toml_edit::visit_mut::*;
//!
@@ -80,10 +82,12 @@
//! "#;
//!
//! 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/ordian/toml_edit/blob/master/examples/visit.rs).
+//! [on GitHub](https://github.com/toml-rs/toml/blob/main/crates/toml_edit/examples/visit.rs).
use crate::{
Array, ArrayOfTables, Datetime, Document, Formatted, InlineTable, Item, KeyMut, Table,