更新libclamav库1.0.0版本

This commit is contained in:
2023-01-14 18:28:39 +08:00
parent b879ee0b2e
commit 45fe15f472
8531 changed files with 1222046 additions and 177272 deletions

View File

@@ -0,0 +1,544 @@
use std::error;
use std::fmt;
use std::str::{self, FromStr};
use serde::{de, ser};
/// A parsed TOML datetime value
///
/// This structure is intended to represent the datetime primitive type that can
/// be encoded into TOML documents. This type is a parsed version that contains
/// all metadata internally.
///
/// Currently this type is intentionally conservative and only supports
/// `to_string` as an accessor. Over time though it's intended that it'll grow
/// more support!
///
/// Note that if you're using `Deserialize` to deserialize a TOML document, you
/// can use this as a placeholder for where you're expecting a datetime to be
/// specified.
///
/// Also note though that while this type implements `Serialize` and
/// `Deserialize` it's only recommended to use this type with the TOML format,
/// otherwise encoded in other formats it may look a little odd.
///
/// Depending on how the option values are used, this struct will correspond
/// with one of the following four datetimes from the [TOML v1.0.0 spec]:
///
/// | `date` | `time` | `offset` | TOML type |
/// | --------- | --------- | --------- | ------------------ |
/// | `Some(_)` | `Some(_)` | `Some(_)` | [Offset Date-Time] |
/// | `Some(_)` | `Some(_)` | `None` | [Local Date-Time] |
/// | `Some(_)` | `None` | `None` | [Local Date] |
/// | `None` | `Some(_)` | `None` | [Local Time] |
///
/// **1. Offset Date-Time**: If all the optional values are used, `Datetime`
/// corresponds to an [Offset Date-Time]. From the TOML v1.0.0 spec:
///
/// > To unambiguously represent a specific instant in time, you may use an
/// > RFC 3339 formatted date-time with offset.
/// >
/// > ```toml
/// > odt1 = 1979-05-27T07:32:00Z
/// > odt2 = 1979-05-27T00:32:00-07:00
/// > odt3 = 1979-05-27T00:32:00.999999-07:00
/// > ```
/// >
/// > For the sake of readability, you may replace the T delimiter between date
/// > and time with a space character (as permitted by RFC 3339 section 5.6).
/// >
/// > ```toml
/// > odt4 = 1979-05-27 07:32:00Z
/// > ```
///
/// **2. Local Date-Time**: If `date` and `time` are given but `offset` is
/// `None`, `Datetime` corresponds to a [Local Date-Time]. From the spec:
///
/// > If you omit the offset from an RFC 3339 formatted date-time, it will
/// > represent the given date-time without any relation to an offset or
/// > timezone. It cannot be converted to an instant in time without additional
/// > information. Conversion to an instant, if required, is implementation-
/// > specific.
/// >
/// > ```toml
/// > ldt1 = 1979-05-27T07:32:00
/// > ldt2 = 1979-05-27T00:32:00.999999
/// > ```
///
/// **3. Local Date**: If only `date` is given, `Datetime` corresponds to a
/// [Local Date]; see the docs for [`Date`].
///
/// **4. Local Time**: If only `time` is given, `Datetime` corresponds to a
/// [Local Time]; see the docs for [`Time`].
///
/// [TOML v1.0.0 spec]: https://toml.io/en/v1.0.0
/// [Offset Date-Time]: https://toml.io/en/v1.0.0#offset-date-time
/// [Local Date-Time]: https://toml.io/en/v1.0.0#local-date-time
/// [Local Date]: https://toml.io/en/v1.0.0#local-date
/// [Local Time]: https://toml.io/en/v1.0.0#local-time
#[derive(PartialEq, Clone)]
pub struct Datetime {
/// Optional date.
/// Required for: *Offset Date-Time*, *Local Date-Time*, *Local Date*.
pub date: Option<Date>,
/// Optional time.
/// Required for: *Offset Date-Time*, *Local Date-Time*, *Local Time*.
pub time: Option<Time>,
/// Optional offset.
/// Required for: *Offset Date-Time*.
pub offset: Option<Offset>,
}
/// Error returned from parsing a `Datetime` in the `FromStr` implementation.
#[derive(Debug, Clone)]
pub struct DatetimeParseError {
_private: (),
}
// Currently serde itself doesn't have a datetime type, so we map our `Datetime`
// to a special valid in the serde data model. Namely one with these special
// fields/struct names.
//
// In general the TOML encoder/decoder will catch this and not literally emit
// these strings but rather emit datetimes as they're intended.
pub const FIELD: &str = "$__toml_private_datetime";
pub const NAME: &str = "$__toml_private_Datetime";
/// A parsed TOML date value
///
/// May be part of a [`Datetime`]. Alone, `Date` corresponds to a [Local Date].
/// From the TOML v1.0.0 spec:
///
/// > If you include only the date portion of an RFC 3339 formatted date-time,
/// > it will represent that entire day without any relation to an offset or
/// > timezone.
/// >
/// > ```toml
/// > ld1 = 1979-05-27
/// > ```
///
/// [Local Date]: https://toml.io/en/v1.0.0#local-date
#[derive(PartialEq, Clone)]
pub struct Date {
/// Year: four digits
pub year: u16,
/// Month: 1 to 12
pub month: u8,
/// Day: 1 to {28, 29, 30, 31} (based on month/year)
pub day: u8,
}
/// A parsed TOML time value
///
/// May be part of a [`Datetime`]. Alone, `Time` corresponds to a [Local Time].
/// From the TOML v1.0.0 spec:
///
/// > If you include only the time portion of an RFC 3339 formatted date-time,
/// > it will represent that time of day without any relation to a specific
/// > day or any offset or timezone.
/// >
/// > ```toml
/// > lt1 = 07:32:00
/// > lt2 = 00:32:00.999999
/// > ```
/// >
/// > Millisecond precision is required. Further precision of fractional
/// > seconds is implementation-specific. If the value contains greater
/// > precision than the implementation can support, the additional precision
/// > must be truncated, not rounded.
///
/// [Local Time]: https://toml.io/en/v1.0.0#local-time
#[derive(PartialEq, Clone)]
pub struct Time {
/// Hour: 0 to 23
pub hour: u8,
/// Minute: 0 to 59
pub minute: u8,
/// Second: 0 to {58, 59, 60} (based on leap second rules)
pub second: u8,
/// Nanosecond: 0 to 999_999_999
pub nanosecond: u32,
}
/// A parsed TOML time offset
///
#[derive(PartialEq, Clone)]
pub enum Offset {
/// > A suffix which, when applied to a time, denotes a UTC offset of 00:00;
/// > often spoken "Zulu" from the ICAO phonetic alphabet representation of
/// > the letter "Z". --- [RFC 3339 section 2]
///
/// [RFC 3339 section 2]: https://datatracker.ietf.org/doc/html/rfc3339#section-2
Z,
/// Offset between local time and UTC
Custom {
/// Hours: -12 to +12
hours: i8,
/// Minutes: 0 to 59
minutes: u8,
},
}
impl fmt::Debug for Datetime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for Datetime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref date) = self.date {
write!(f, "{}", date)?;
}
if let Some(ref time) = self.time {
if self.date.is_some() {
write!(f, "T")?;
}
write!(f, "{}", time)?;
}
if let Some(ref offset) = self.offset {
write!(f, "{}", offset)?;
}
Ok(())
}
}
impl fmt::Display for Date {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
}
}
impl fmt::Display for Time {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)?;
if self.nanosecond != 0 {
let s = format!("{:09}", self.nanosecond);
write!(f, ".{}", s.trim_end_matches('0'))?;
}
Ok(())
}
}
impl fmt::Display for Offset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Offset::Z => write!(f, "Z"),
Offset::Custom { hours, minutes } => write!(f, "{:+03}:{:02}", hours, minutes),
}
}
}
impl FromStr for Datetime {
type Err = DatetimeParseError;
fn from_str(date: &str) -> Result<Datetime, DatetimeParseError> {
// Accepted formats:
//
// 0000-00-00T00:00:00.00Z
// 0000-00-00T00:00:00.00
// 0000-00-00
// 00:00:00.00
if date.len() < 3 {
return Err(DatetimeParseError { _private: () });
}
let mut offset_allowed = true;
let mut chars = date.chars();
// First up, parse the full date if we can
let full_date = if chars.clone().nth(2) == Some(':') {
offset_allowed = false;
None
} else {
let y1 = u16::from(digit(&mut chars)?);
let y2 = u16::from(digit(&mut chars)?);
let y3 = u16::from(digit(&mut chars)?);
let y4 = u16::from(digit(&mut chars)?);
match chars.next() {
Some('-') => {}
_ => return Err(DatetimeParseError { _private: () }),
}
let m1 = digit(&mut chars)?;
let m2 = digit(&mut chars)?;
match chars.next() {
Some('-') => {}
_ => return Err(DatetimeParseError { _private: () }),
}
let d1 = digit(&mut chars)?;
let d2 = digit(&mut chars)?;
let date = Date {
year: y1 * 1000 + y2 * 100 + y3 * 10 + y4,
month: m1 * 10 + m2,
day: d1 * 10 + d2,
};
if date.month < 1 || date.month > 12 {
return Err(DatetimeParseError { _private: () });
}
if date.day < 1 || date.day > 31 {
return Err(DatetimeParseError { _private: () });
}
Some(date)
};
// Next parse the "partial-time" if available
let next = chars.clone().next();
let partial_time = if full_date.is_some()
&& (next == Some('T') || next == Some('t') || next == Some(' '))
{
chars.next();
true
} else {
full_date.is_none()
};
let time = if partial_time {
let h1 = digit(&mut chars)?;
let h2 = digit(&mut chars)?;
match chars.next() {
Some(':') => {}
_ => return Err(DatetimeParseError { _private: () }),
}
let m1 = digit(&mut chars)?;
let m2 = digit(&mut chars)?;
match chars.next() {
Some(':') => {}
_ => return Err(DatetimeParseError { _private: () }),
}
let s1 = digit(&mut chars)?;
let s2 = digit(&mut chars)?;
let mut nanosecond = 0;
if chars.clone().next() == Some('.') {
chars.next();
let whole = chars.as_str();
let mut end = whole.len();
for (i, byte) in whole.bytes().enumerate() {
match byte {
b'0'..=b'9' => {
if i < 9 {
let p = 10_u32.pow(8 - i as u32);
nanosecond += p * u32::from(byte - b'0');
}
}
_ => {
end = i;
break;
}
}
}
if end == 0 {
return Err(DatetimeParseError { _private: () });
}
chars = whole[end..].chars();
}
let time = Time {
hour: h1 * 10 + h2,
minute: m1 * 10 + m2,
second: s1 * 10 + s2,
nanosecond,
};
if time.hour > 24 {
return Err(DatetimeParseError { _private: () });
}
if time.minute > 59 {
return Err(DatetimeParseError { _private: () });
}
if time.second > 59 {
return Err(DatetimeParseError { _private: () });
}
if time.nanosecond > 999_999_999 {
return Err(DatetimeParseError { _private: () });
}
Some(time)
} else {
offset_allowed = false;
None
};
// And finally, parse the offset
let offset = if offset_allowed {
let next = chars.clone().next();
if next == Some('Z') || next == Some('z') {
chars.next();
Some(Offset::Z)
} else if next.is_none() {
None
} else {
let sign = match next {
Some('+') => 1,
Some('-') => -1,
_ => return Err(DatetimeParseError { _private: () }),
};
chars.next();
let h1 = digit(&mut chars)? as i8;
let h2 = digit(&mut chars)? as i8;
match chars.next() {
Some(':') => {}
_ => return Err(DatetimeParseError { _private: () }),
}
let m1 = digit(&mut chars)?;
let m2 = digit(&mut chars)?;
Some(Offset::Custom {
hours: sign * (h1 * 10 + h2),
minutes: m1 * 10 + m2,
})
}
} else {
None
};
// Return an error if we didn't hit eof, otherwise return our parsed
// date
if chars.next().is_some() {
return Err(DatetimeParseError { _private: () });
}
Ok(Datetime {
date: full_date,
time,
offset,
})
}
}
fn digit(chars: &mut str::Chars<'_>) -> Result<u8, DatetimeParseError> {
match chars.next() {
Some(c) if '0' <= c && c <= '9' => Ok(c as u8 - b'0'),
_ => Err(DatetimeParseError { _private: () }),
}
}
impl ser::Serialize for Datetime {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
use serde::ser::SerializeStruct;
let mut s = serializer.serialize_struct(NAME, 1)?;
s.serialize_field(FIELD, &self.to_string())?;
s.end()
}
}
impl<'de> de::Deserialize<'de> for Datetime {
fn deserialize<D>(deserializer: D) -> Result<Datetime, D::Error>
where
D: de::Deserializer<'de>,
{
struct DatetimeVisitor;
impl<'de> de::Visitor<'de> for DatetimeVisitor {
type Value = Datetime;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a TOML datetime")
}
fn visit_map<V>(self, mut visitor: V) -> Result<Datetime, V::Error>
where
V: de::MapAccess<'de>,
{
let value = visitor.next_key::<DatetimeKey>()?;
if value.is_none() {
return Err(de::Error::custom("datetime key not found"));
}
let v: DatetimeFromString = visitor.next_value()?;
Ok(v.value)
}
}
static FIELDS: [&str; 1] = [FIELD];
deserializer.deserialize_struct(NAME, &FIELDS, DatetimeVisitor)
}
}
struct DatetimeKey;
impl<'de> de::Deserialize<'de> for DatetimeKey {
fn deserialize<D>(deserializer: D) -> Result<DatetimeKey, D::Error>
where
D: de::Deserializer<'de>,
{
struct FieldVisitor;
impl<'de> de::Visitor<'de> for FieldVisitor {
type Value = ();
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a valid datetime field")
}
fn visit_str<E>(self, s: &str) -> Result<(), E>
where
E: de::Error,
{
if s == FIELD {
Ok(())
} else {
Err(de::Error::custom("expected field with custom name"))
}
}
}
deserializer.deserialize_identifier(FieldVisitor)?;
Ok(DatetimeKey)
}
}
pub struct DatetimeFromString {
pub value: Datetime,
}
impl<'de> de::Deserialize<'de> for DatetimeFromString {
fn deserialize<D>(deserializer: D) -> Result<DatetimeFromString, D::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = DatetimeFromString;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("string containing a datetime")
}
fn visit_str<E>(self, s: &str) -> Result<DatetimeFromString, E>
where
E: de::Error,
{
match s.parse() {
Ok(date) => Ok(DatetimeFromString { value: date }),
Err(e) => Err(de::Error::custom(e)),
}
}
}
deserializer.deserialize_str(Visitor)
}
}
impl fmt::Display for DatetimeParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"failed to parse datetime".fmt(f)
}
}
impl error::Error for DatetimeParseError {}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,180 @@
//! A [TOML]-parsing library
//!
//! This library implements a [TOML] v0.5.0 compatible parser,
//! primarily supporting the [`serde`] library for encoding/decoding
//! various types in Rust.
//!
//! TOML itself is a simple, ergonomic, and readable configuration format:
//!
//! ```toml
//! [package]
//! name = "toml"
//! version = "0.4.2"
//! authors = ["Alex Crichton <alex@alexcrichton.com>"]
//!
//! [dependencies]
//! serde = "1.0"
//! ```
//!
//! The TOML format tends to be relatively common throughout the Rust community
//! for configuration, notably being used by [Cargo], Rust's package manager.
//!
//! ## TOML values
//!
//! A value in TOML is represented with the [`Value`] enum in this crate:
//!
//! ```rust,ignore
//! pub enum Value {
//! String(String),
//! Integer(i64),
//! Float(f64),
//! Boolean(bool),
//! Datetime(Datetime),
//! Array(Array),
//! Table(Table),
//! }
//! ```
//!
//! TOML is similar to JSON with the notable addition of a [`Datetime`]
//! type. In general, TOML and JSON are interchangeable in terms of
//! formats.
//!
//! ## Parsing TOML
//!
//! The easiest way to parse a TOML document is via the [`Value`] type:
//!
//! ```rust
//! use toml::Value;
//!
//! let value = "foo = 'bar'".parse::<Value>().unwrap();
//!
//! assert_eq!(value["foo"].as_str(), Some("bar"));
//! ```
//!
//! The [`Value`] type implements a number of convenience methods and
//! traits; the example above uses [`FromStr`] to parse a [`str`] into a
//! [`Value`].
//!
//! ## Deserialization and Serialization
//!
//! This crate supports [`serde`] 1.0 with a number of
//! implementations of the `Deserialize`, `Serialize`, `Deserializer`, and
//! `Serializer` traits. Namely, you'll find:
//!
//! * `Deserialize for Value`
//! * `Serialize for Value`
//! * `Deserialize for Datetime`
//! * `Serialize for Datetime`
//! * `Deserializer for de::Deserializer`
//! * `Serializer for ser::Serializer`
//! * `Deserializer for Value`
//!
//! This means that you can use Serde to deserialize/serialize the
//! [`Value`] type as well as the [`Datetime`] type in this crate. You can also
//! use the [`Deserializer`], [`Serializer`], or [`Value`] type itself to act as
//! a deserializer/serializer for arbitrary types.
//!
//! An example of deserializing with TOML is:
//!
//! ```rust
//! use serde_derive::Deserialize;
//!
//! #[derive(Deserialize)]
//! struct Config {
//! ip: String,
//! port: Option<u16>,
//! keys: Keys,
//! }
//!
//! #[derive(Deserialize)]
//! struct Keys {
//! github: String,
//! travis: Option<String>,
//! }
//!
//! fn main() {
//! let config: Config = toml::from_str(r#"
//! ip = '127.0.0.1'
//!
//! [keys]
//! github = 'xxxxxxxxxxxxxxxxx'
//! travis = 'yyyyyyyyyyyyyyyyy'
//! "#).unwrap();
//!
//! assert_eq!(config.ip, "127.0.0.1");
//! assert_eq!(config.port, None);
//! assert_eq!(config.keys.github, "xxxxxxxxxxxxxxxxx");
//! assert_eq!(config.keys.travis.as_ref().unwrap(), "yyyyyyyyyyyyyyyyy");
//! }
//! ```
//!
//! You can serialize types in a similar fashion:
//!
//! ```rust
//! use serde_derive::Serialize;
//!
//! #[derive(Serialize)]
//! struct Config {
//! ip: String,
//! port: Option<u16>,
//! keys: Keys,
//! }
//!
//! #[derive(Serialize)]
//! struct Keys {
//! github: String,
//! travis: Option<String>,
//! }
//!
//! fn main() {
//! let config = Config {
//! ip: "127.0.0.1".to_string(),
//! port: None,
//! keys: Keys {
//! github: "xxxxxxxxxxxxxxxxx".to_string(),
//! travis: Some("yyyyyyyyyyyyyyyyy".to_string()),
//! },
//! };
//!
//! let toml = toml::to_string(&config).unwrap();
//! }
//! ```
//!
//! [TOML]: https://github.com/toml-lang/toml
//! [Cargo]: https://crates.io/
//! [`serde`]: https://serde.rs/
#![doc(html_root_url = "https://docs.rs/toml/0.5")]
#![deny(missing_docs)]
#![warn(rust_2018_idioms)]
// Makes rustc abort compilation if there are any unsafe blocks in the crate.
// Presence of this annotation is picked up by tools such as cargo-geiger
// and lets them ensure that there is indeed no unsafe code as opposed to
// something they couldn't detect (e.g. unsafe added via macro expansion, etc).
#![forbid(unsafe_code)]
pub mod map;
pub mod value;
#[doc(no_inline)]
pub use crate::value::Value;
mod datetime;
pub mod ser;
#[doc(no_inline)]
pub use crate::ser::{to_string, to_string_pretty, to_vec, Serializer};
pub mod de;
#[doc(no_inline)]
pub use crate::de::{from_slice, from_str, Deserializer};
mod tokens;
#[doc(hidden)]
pub mod macros;
mod spanned;
pub use crate::spanned::Spanned;
// Just for rustdoc
#[allow(unused_imports)]
use crate::datetime::Datetime;
#[allow(unused_imports)]
use core::str::FromStr;

View File

@@ -0,0 +1,462 @@
pub use serde::de::{Deserialize, IntoDeserializer};
use crate::value::{Array, Table, Value};
/// Construct a [`toml::Value`] from TOML syntax.
///
/// [`toml::Value`]: value/enum.Value.html
///
/// ```rust
/// fn main() {
/// let cargo_toml = toml::toml! {
/// [package]
/// name = "toml"
/// version = "0.4.5"
/// authors = ["Alex Crichton <alex@alexcrichton.com>"]
///
/// [badges]
/// travis-ci = { repository = "alexcrichton/toml-rs" }
///
/// [dependencies]
/// serde = "1.0"
///
/// [dev-dependencies]
/// serde_derive = "1.0"
/// serde_json = "1.0"
/// };
///
/// println!("{:#?}", cargo_toml);
/// }
/// ```
#[macro_export]
macro_rules! toml {
($($toml:tt)+) => {{
let table = $crate::value::Table::new();
let mut root = $crate::Value::Table(table);
$crate::toml_internal!(@toplevel root [] $($toml)+);
root
}};
}
// TT-muncher to parse TOML syntax into a toml::Value.
//
// @toplevel -- Parse tokens outside of an inline table or inline array. In
// this state, `[table headers]` and `[[array headers]]` are
// allowed and `key = value` pairs are not separated by commas.
//
// @topleveldatetime -- Helper to parse a Datetime from string and insert it
// into a table, continuing in the @toplevel state.
//
// @path -- Turn a path segment into a string. Segments that look like idents
// are stringified, while quoted segments like `"cfg(windows)"`
// are not.
//
// @value -- Parse the value part of a `key = value` pair, which may be a
// primitive or inline table or inline array.
//
// @table -- Parse the contents of an inline table, returning them as a
// toml::Value::Table.
//
// @tabledatetime -- Helper to parse a Datetime from string and insert it
// into a table, continuing in the @table state.
//
// @array -- Parse the contents of an inline array, returning them as a
// toml::Value::Array.
//
// @arraydatetime -- Helper to parse a Datetime from string and push it into
// an array, continuing in the @array state.
//
// @trailingcomma -- Helper to append a comma to a sequence of tokens if the
// sequence is non-empty and does not already end in a trailing
// comma.
//
#[macro_export]
#[doc(hidden)]
macro_rules! toml_internal {
// Base case, no elements remaining.
(@toplevel $root:ident [$($path:tt)*]) => {};
// Parse negative number `key = -value`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = - $v:tt $($rest:tt)*) => {
$crate::toml_internal!(@toplevel $root [$($path)*] $($($k)-+).+ = (-$v) $($rest)*);
};
// Parse positive number `key = +value`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = + $v:tt $($rest:tt)*) => {
$crate::toml_internal!(@toplevel $root [$($path)*] $($($k)-+).+ = ($v) $($rest)*);
};
// Parse offset datetime `key = 1979-05-27T00:32:00.999999-07:00`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
};
// Space instead of T.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
};
// Parse offset datetime `key = 1979-05-27T00:32:00-07:00`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
};
// Space instead of T.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec - $tzh : $tzm) $($rest)*);
};
// Parse local datetime `key = 1979-05-27T00:32:00.999999`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
};
// Space instead of T.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac) $($rest)*);
};
// Parse offset datetime `key = 1979-05-27T07:32:00Z` and local datetime `key = 1979-05-27T07:32:00`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec) $($rest)*);
};
// Space instead of T.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec) $($rest)*);
};
// Parse local date `key = 1979-05-27`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day) $($rest)*);
};
// Parse local time `key = 00:32:00.999999`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($hr : $min : $sec . $frac) $($rest)*);
};
// Parse local time `key = 07:32:00`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($hr : $min : $sec) $($rest)*);
};
// Parse any other `key = value` including string, inline array, inline
// table, number, and boolean.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $v:tt $($rest:tt)*) => {{
$crate::macros::insert_toml(
&mut $root,
&[$($path)* $(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
$crate::toml_internal!(@value $v));
$crate::toml_internal!(@toplevel $root [$($path)*] $($rest)*);
}};
// Parse array header `[[bin]]`.
(@toplevel $root:ident $oldpath:tt [[$($($path:tt)-+).+]] $($rest:tt)*) => {
$crate::macros::push_toml(
&mut $root,
&[$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+]);
$crate::toml_internal!(@toplevel $root [$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+] $($rest)*);
};
// Parse table header `[patch.crates-io]`.
(@toplevel $root:ident $oldpath:tt [$($($path:tt)-+).+] $($rest:tt)*) => {
$crate::macros::insert_toml(
&mut $root,
&[$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+],
$crate::Value::Table($crate::value::Table::new()));
$crate::toml_internal!(@toplevel $root [$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+] $($rest)*);
};
// Parse datetime from string and insert into table.
(@topleveldatetime $root:ident [$($path:tt)*] $($($k:tt)-+).+ = ($($datetime:tt)+) $($rest:tt)*) => {
$crate::macros::insert_toml(
&mut $root,
&[$($path)* $(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
$crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
$crate::toml_internal!(@toplevel $root [$($path)*] $($rest)*);
};
// Turn a path segment into a string.
(@path $ident:ident) => {
stringify!($ident)
};
// For a path segment that is not an ident, expect that it is already a
// quoted string, like in `[target."cfg(windows)".dependencies]`.
(@path $quoted:tt) => {
$quoted
};
// Construct a Value from an inline table.
(@value { $($inline:tt)* }) => {{
let mut table = $crate::Value::Table($crate::value::Table::new());
$crate::toml_internal!(@trailingcomma (@table table) $($inline)*);
table
}};
// Construct a Value from an inline array.
(@value [ $($inline:tt)* ]) => {{
let mut array = $crate::value::Array::new();
$crate::toml_internal!(@trailingcomma (@array array) $($inline)*);
$crate::Value::Array(array)
}};
(@value (-nan)) => {
$crate::Value::Float(-::std::f64::NAN)
};
(@value (nan)) => {
$crate::Value::Float(::std::f64::NAN)
};
(@value nan) => {
$crate::Value::Float(::std::f64::NAN)
};
(@value (-inf)) => {
$crate::Value::Float(::std::f64::NEG_INFINITY)
};
(@value (inf)) => {
$crate::Value::Float(::std::f64::INFINITY)
};
(@value inf) => {
$crate::Value::Float(::std::f64::INFINITY)
};
// Construct a Value from any other type, probably string or boolean or number.
(@value $v:tt) => {{
// TODO: Implement this with something like serde_json::to_value instead.
let de = $crate::macros::IntoDeserializer::<$crate::de::Error>::into_deserializer($v);
<$crate::Value as $crate::macros::Deserialize>::deserialize(de).unwrap()
}};
// Base case of inline table.
(@table $root:ident) => {};
// Parse negative number `key = -value`.
(@table $root:ident $($($k:tt)-+).+ = - $v:tt , $($rest:tt)*) => {
$crate::toml_internal!(@table $root $($($k)-+).+ = (-$v) , $($rest)*);
};
// Parse positive number `key = +value`.
(@table $root:ident $($($k:tt)-+).+ = + $v:tt , $($rest:tt)*) => {
$crate::toml_internal!(@table $root $($($k)-+).+ = ($v) , $($rest)*);
};
// Parse offset datetime `key = 1979-05-27T00:32:00.999999-07:00`.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
};
// Space instead of T.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
};
// Parse offset datetime `key = 1979-05-27T00:32:00-07:00`.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
};
// Space instead of T.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec - $tzh : $tzm) $($rest)*);
};
// Parse local datetime `key = 1979-05-27T00:32:00.999999`.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
};
// Space instead of T.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac) $($rest)*);
};
// Parse offset datetime `key = 1979-05-27T07:32:00Z` and local datetime `key = 1979-05-27T07:32:00`.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec) $($rest)*);
};
// Space instead of T.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec) $($rest)*);
};
// Parse local date `key = 1979-05-27`.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day) $($rest)*);
};
// Parse local time `key = 00:32:00.999999`.
(@table $root:ident $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($hr : $min : $sec . $frac) $($rest)*);
};
// Parse local time `key = 07:32:00`.
(@table $root:ident $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($hr : $min : $sec) $($rest)*);
};
// Parse any other type, probably string or boolean or number.
(@table $root:ident $($($k:tt)-+).+ = $v:tt , $($rest:tt)*) => {
$crate::macros::insert_toml(
&mut $root,
&[$(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
$crate::toml_internal!(@value $v));
$crate::toml_internal!(@table $root $($rest)*);
};
// Parse a Datetime from string and continue in @table state.
(@tabledatetime $root:ident $($($k:tt)-+).+ = ($($datetime:tt)*) $($rest:tt)*) => {
$crate::macros::insert_toml(
&mut $root,
&[$(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
$crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
$crate::toml_internal!(@table $root $($rest)*);
};
// Base case of inline array.
(@array $root:ident) => {};
// Parse negative number `-value`.
(@array $root:ident - $v:tt , $($rest:tt)*) => {
$crate::toml_internal!(@array $root (-$v) , $($rest)*);
};
// Parse positive number `+value`.
(@array $root:ident + $v:tt , $($rest:tt)*) => {
$crate::toml_internal!(@array $root ($v) , $($rest)*);
};
// Parse offset datetime `1979-05-27T00:32:00.999999-07:00`.
(@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
};
// Space instead of T.
(@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
};
// Parse offset datetime `1979-05-27T00:32:00-07:00`.
(@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
};
// Space instead of T.
(@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec - $tzh : $tzm) $($rest)*);
};
// Parse local datetime `1979-05-27T00:32:00.999999`.
(@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
};
// Space instead of T.
(@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec . $frac) $($rest)*);
};
// Parse offset datetime `1979-05-27T07:32:00Z` and local datetime `1979-05-27T07:32:00`.
(@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec) $($rest)*);
};
// Space instead of T.
(@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec) $($rest)*);
};
// Parse local date `1979-05-27`.
(@array $root:ident $yr:tt - $mo:tt - $day:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day) $($rest)*);
};
// Parse local time `00:32:00.999999`.
(@array $root:ident $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($hr : $min : $sec . $frac) $($rest)*);
};
// Parse local time `07:32:00`.
(@array $root:ident $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($hr : $min : $sec) $($rest)*);
};
// Parse any other type, probably string or boolean or number.
(@array $root:ident $v:tt , $($rest:tt)*) => {
$root.push($crate::toml_internal!(@value $v));
$crate::toml_internal!(@array $root $($rest)*);
};
// Parse a Datetime from string and continue in @array state.
(@arraydatetime $root:ident ($($datetime:tt)*) $($rest:tt)*) => {
$root.push($crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
$crate::toml_internal!(@array $root $($rest)*);
};
// No trailing comma required if the tokens are empty.
(@trailingcomma ($($args:tt)*)) => {
$crate::toml_internal!($($args)*);
};
// Tokens end with a trailing comma, do not append another one.
(@trailingcomma ($($args:tt)*) ,) => {
$crate::toml_internal!($($args)* ,);
};
// Tokens end with something other than comma, append a trailing comma.
(@trailingcomma ($($args:tt)*) $last:tt) => {
$crate::toml_internal!($($args)* $last ,);
};
// Not yet at the last token.
(@trailingcomma ($($args:tt)*) $first:tt $($rest:tt)+) => {
$crate::toml_internal!(@trailingcomma ($($args)* $first) $($rest)+);
};
}
// Called when parsing a `key = value` pair.
// Inserts an entry into the table at the given path.
pub fn insert_toml(root: &mut Value, path: &[&str], value: Value) {
*traverse(root, path) = value;
}
// Called when parsing an `[[array header]]`.
// Pushes an empty table onto the array at the given path.
pub fn push_toml(root: &mut Value, path: &[&str]) {
let target = traverse(root, path);
if !target.is_array() {
*target = Value::Array(Array::new());
}
target
.as_array_mut()
.unwrap()
.push(Value::Table(Table::new()));
}
fn traverse<'a>(root: &'a mut Value, path: &[&str]) -> &'a mut Value {
let mut cur = root;
for &key in path {
// Lexical lifetimes :D
let cur1 = cur;
let cur2;
// From the TOML spec:
//
// > Each double-bracketed sub-table will belong to the most recently
// > defined table element above it.
if cur1.is_array() {
cur2 = cur1.as_array_mut().unwrap().last_mut().unwrap();
} else {
cur2 = cur1;
};
// We are about to index into this value, so it better be a table.
if !cur2.is_table() {
*cur2 = Value::Table(Table::new());
}
if !cur2.as_table().unwrap().contains_key(key) {
// Insert an empty table for the next loop iteration to point to.
let empty = Value::Table(Table::new());
cur2.as_table_mut().unwrap().insert(key.to_owned(), empty);
}
// Step into the current table.
cur = cur2.as_table_mut().unwrap().get_mut(key).unwrap();
}
cur
}

View File

@@ -0,0 +1,595 @@
// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A map of String to toml::Value.
//!
//! By default the map is backed by a [`BTreeMap`]. Enable the `preserve_order`
//! feature of toml-rs to use [`LinkedHashMap`] instead.
//!
//! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
//! [`LinkedHashMap`]: https://docs.rs/linked-hash-map/*/linked_hash_map/struct.LinkedHashMap.html
use crate::value::Value;
use serde::{de, ser};
use std::borrow::Borrow;
use std::fmt::{self, Debug};
use std::hash::Hash;
use std::iter::FromIterator;
use std::ops;
#[cfg(not(feature = "preserve_order"))]
use std::collections::{btree_map, BTreeMap};
#[cfg(feature = "preserve_order")]
use indexmap::{self, IndexMap};
/// Represents a TOML key/value type.
pub struct Map<K, V> {
map: MapImpl<K, V>,
}
#[cfg(not(feature = "preserve_order"))]
type MapImpl<K, V> = BTreeMap<K, V>;
#[cfg(feature = "preserve_order")]
type MapImpl<K, V> = IndexMap<K, V>;
impl Map<String, Value> {
/// Makes a new empty Map.
#[inline]
pub fn new() -> Self {
Map {
map: MapImpl::new(),
}
}
#[cfg(not(feature = "preserve_order"))]
/// Makes a new empty Map with the given initial capacity.
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
// does not support with_capacity
let _ = capacity;
Map {
map: BTreeMap::new(),
}
}
#[cfg(feature = "preserve_order")]
/// Makes a new empty Map with the given initial capacity.
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Map {
map: IndexMap::with_capacity(capacity),
}
}
/// Clears the map, removing all values.
#[inline]
pub fn clear(&mut self) {
self.map.clear()
}
/// Returns a reference to the value corresponding to the key.
///
/// The key may be any borrowed form of the map's key type, but the ordering
/// on the borrowed form *must* match the ordering on the key type.
#[inline]
pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&Value>
where
String: Borrow<Q>,
Q: Ord + Eq + Hash,
{
self.map.get(key)
}
/// Returns true if the map contains a value for the specified key.
///
/// The key may be any borrowed form of the map's key type, but the ordering
/// on the borrowed form *must* match the ordering on the key type.
#[inline]
pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
where
String: Borrow<Q>,
Q: Ord + Eq + Hash,
{
self.map.contains_key(key)
}
/// Returns a mutable reference to the value corresponding to the key.
///
/// The key may be any borrowed form of the map's key type, but the ordering
/// on the borrowed form *must* match the ordering on the key type.
#[inline]
pub fn get_mut<Q: ?Sized>(&mut self, key: &Q) -> Option<&mut Value>
where
String: Borrow<Q>,
Q: Ord + Eq + Hash,
{
self.map.get_mut(key)
}
/// Inserts a key-value pair into the map.
///
/// If the map did not have this key present, `None` is returned.
///
/// If the map did have this key present, the value is updated, and the old
/// value is returned. The key is not updated, though; this matters for
/// types that can be `==` without being identical.
#[inline]
pub fn insert(&mut self, k: String, v: Value) -> Option<Value> {
self.map.insert(k, v)
}
/// Removes a key from the map, returning the value at the key if the key
/// was previously in the map.
///
/// The key may be any borrowed form of the map's key type, but the ordering
/// on the borrowed form *must* match the ordering on the key type.
#[inline]
pub fn remove<Q: ?Sized>(&mut self, key: &Q) -> Option<Value>
where
String: Borrow<Q>,
Q: Ord + Eq + Hash,
{
self.map.remove(key)
}
/// Gets the given key's corresponding entry in the map for in-place
/// manipulation.
pub fn entry<S>(&mut self, key: S) -> Entry<'_>
where
S: Into<String>,
{
#[cfg(feature = "preserve_order")]
use indexmap::map::Entry as EntryImpl;
#[cfg(not(feature = "preserve_order"))]
use std::collections::btree_map::Entry as EntryImpl;
match self.map.entry(key.into()) {
EntryImpl::Vacant(vacant) => Entry::Vacant(VacantEntry { vacant }),
EntryImpl::Occupied(occupied) => Entry::Occupied(OccupiedEntry { occupied }),
}
}
/// Returns the number of elements in the map.
#[inline]
pub fn len(&self) -> usize {
self.map.len()
}
/// Returns true if the map contains no elements.
#[inline]
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
/// Gets an iterator over the entries of the map.
#[inline]
pub fn iter(&self) -> Iter<'_> {
Iter {
iter: self.map.iter(),
}
}
/// Gets a mutable iterator over the entries of the map.
#[inline]
pub fn iter_mut(&mut self) -> IterMut<'_> {
IterMut {
iter: self.map.iter_mut(),
}
}
/// Gets an iterator over the keys of the map.
#[inline]
pub fn keys(&self) -> Keys<'_> {
Keys {
iter: self.map.keys(),
}
}
/// Gets an iterator over the values of the map.
#[inline]
pub fn values(&self) -> Values<'_> {
Values {
iter: self.map.values(),
}
}
}
impl Default for Map<String, Value> {
#[inline]
fn default() -> Self {
Map {
map: MapImpl::new(),
}
}
}
impl Clone for Map<String, Value> {
#[inline]
fn clone(&self) -> Self {
Map {
map: self.map.clone(),
}
}
}
impl PartialEq for Map<String, Value> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.map.eq(&other.map)
}
}
/// Access an element of this map. Panics if the given key is not present in the
/// map.
impl<'a, Q: ?Sized> ops::Index<&'a Q> for Map<String, Value>
where
String: Borrow<Q>,
Q: Ord + Eq + Hash,
{
type Output = Value;
fn index(&self, index: &Q) -> &Value {
self.map.index(index)
}
}
/// Mutably access an element of this map. Panics if the given key is not
/// present in the map.
impl<'a, Q: ?Sized> ops::IndexMut<&'a Q> for Map<String, Value>
where
String: Borrow<Q>,
Q: Ord + Eq + Hash,
{
fn index_mut(&mut self, index: &Q) -> &mut Value {
self.map.get_mut(index).expect("no entry found for key")
}
}
impl Debug for Map<String, Value> {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.map.fmt(formatter)
}
}
impl ser::Serialize for Map<String, Value> {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(Some(self.len()))?;
for (k, v) in self {
map.serialize_key(k)?;
map.serialize_value(v)?;
}
map.end()
}
}
impl<'de> de::Deserialize<'de> for Map<String, Value> {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = Map<String, Value>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a map")
}
#[inline]
fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Map::new())
}
#[inline]
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
where
V: de::MapAccess<'de>,
{
let mut values = Map::new();
while let Some((key, value)) = visitor.next_entry()? {
values.insert(key, value);
}
Ok(values)
}
}
deserializer.deserialize_map(Visitor)
}
}
impl FromIterator<(String, Value)> for Map<String, Value> {
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = (String, Value)>,
{
Map {
map: FromIterator::from_iter(iter),
}
}
}
impl Extend<(String, Value)> for Map<String, Value> {
fn extend<T>(&mut self, iter: T)
where
T: IntoIterator<Item = (String, Value)>,
{
self.map.extend(iter);
}
}
macro_rules! delegate_iterator {
(($name:ident $($generics:tt)*) => $item:ty) => {
impl $($generics)* Iterator for $name $($generics)* {
type Item = $item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl $($generics)* DoubleEndedIterator for $name $($generics)* {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back()
}
}
impl $($generics)* ExactSizeIterator for $name $($generics)* {
#[inline]
fn len(&self) -> usize {
self.iter.len()
}
}
}
}
//////////////////////////////////////////////////////////////////////////////
/// A view into a single entry in a map, which may either be vacant or occupied.
/// This enum is constructed from the [`entry`] method on [`Map`].
///
/// [`entry`]: struct.Map.html#method.entry
/// [`Map`]: struct.Map.html
pub enum Entry<'a> {
/// A vacant Entry.
Vacant(VacantEntry<'a>),
/// An occupied Entry.
Occupied(OccupiedEntry<'a>),
}
/// A vacant Entry. It is part of the [`Entry`] enum.
///
/// [`Entry`]: enum.Entry.html
pub struct VacantEntry<'a> {
vacant: VacantEntryImpl<'a>,
}
/// An occupied Entry. It is part of the [`Entry`] enum.
///
/// [`Entry`]: enum.Entry.html
pub struct OccupiedEntry<'a> {
occupied: OccupiedEntryImpl<'a>,
}
#[cfg(not(feature = "preserve_order"))]
type VacantEntryImpl<'a> = btree_map::VacantEntry<'a, String, Value>;
#[cfg(feature = "preserve_order")]
type VacantEntryImpl<'a> = indexmap::map::VacantEntry<'a, String, Value>;
#[cfg(not(feature = "preserve_order"))]
type OccupiedEntryImpl<'a> = btree_map::OccupiedEntry<'a, String, Value>;
#[cfg(feature = "preserve_order")]
type OccupiedEntryImpl<'a> = indexmap::map::OccupiedEntry<'a, String, Value>;
impl<'a> Entry<'a> {
/// Returns a reference to this entry's key.
pub fn key(&self) -> &String {
match *self {
Entry::Vacant(ref e) => e.key(),
Entry::Occupied(ref 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 {
Entry::Vacant(entry) => entry.insert(default),
Entry::Occupied(entry) => entry.into_mut(),
}
}
/// 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>(self, default: F) -> &'a mut Value
where
F: FnOnce() -> Value,
{
match self {
Entry::Vacant(entry) => entry.insert(default()),
Entry::Occupied(entry) => entry.into_mut(),
}
}
}
impl<'a> VacantEntry<'a> {
/// Gets a reference to the key that would be used when inserting a value
/// through the VacantEntry.
#[inline]
pub fn key(&self) -> &String {
self.vacant.key()
}
/// Sets the value of the entry with the VacantEntry's key, and returns a
/// mutable reference to it.
#[inline]
pub fn insert(self, value: Value) -> &'a mut Value {
self.vacant.insert(value)
}
}
impl<'a> OccupiedEntry<'a> {
/// Gets a reference to the key in the entry.
#[inline]
pub fn key(&self) -> &String {
self.occupied.key()
}
/// Gets a reference to the value in the entry.
#[inline]
pub fn get(&self) -> &Value {
self.occupied.get()
}
/// Gets a mutable reference to the value in the entry.
#[inline]
pub fn get_mut(&mut self) -> &mut Value {
self.occupied.get_mut()
}
/// Converts the entry into a mutable reference to its value.
#[inline]
pub fn into_mut(self) -> &'a mut Value {
self.occupied.into_mut()
}
/// Sets the value of the entry with the `OccupiedEntry`'s key, and returns
/// the entry's old value.
#[inline]
pub fn insert(&mut self, value: Value) -> Value {
self.occupied.insert(value)
}
/// Takes the value of the entry out of the map, and returns it.
#[inline]
pub fn remove(self) -> Value {
self.occupied.remove()
}
}
//////////////////////////////////////////////////////////////////////////////
impl<'a> IntoIterator for &'a Map<String, Value> {
type Item = (&'a String, &'a Value);
type IntoIter = Iter<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
Iter {
iter: self.map.iter(),
}
}
}
/// An iterator over a toml::Map's entries.
pub struct Iter<'a> {
iter: IterImpl<'a>,
}
#[cfg(not(feature = "preserve_order"))]
type IterImpl<'a> = btree_map::Iter<'a, String, Value>;
#[cfg(feature = "preserve_order")]
type IterImpl<'a> = indexmap::map::Iter<'a, String, Value>;
delegate_iterator!((Iter<'a>) => (&'a String, &'a Value));
//////////////////////////////////////////////////////////////////////////////
impl<'a> IntoIterator for &'a mut Map<String, Value> {
type Item = (&'a String, &'a mut Value);
type IntoIter = IterMut<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IterMut {
iter: self.map.iter_mut(),
}
}
}
/// A mutable iterator over a toml::Map's entries.
pub struct IterMut<'a> {
iter: IterMutImpl<'a>,
}
#[cfg(not(feature = "preserve_order"))]
type IterMutImpl<'a> = btree_map::IterMut<'a, String, Value>;
#[cfg(feature = "preserve_order")]
type IterMutImpl<'a> = indexmap::map::IterMut<'a, String, Value>;
delegate_iterator!((IterMut<'a>) => (&'a String, &'a mut Value));
//////////////////////////////////////////////////////////////////////////////
impl IntoIterator for Map<String, Value> {
type Item = (String, Value);
type IntoIter = IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IntoIter {
iter: self.map.into_iter(),
}
}
}
/// An owning iterator over a toml::Map's entries.
pub struct IntoIter {
iter: IntoIterImpl,
}
#[cfg(not(feature = "preserve_order"))]
type IntoIterImpl = btree_map::IntoIter<String, Value>;
#[cfg(feature = "preserve_order")]
type IntoIterImpl = indexmap::map::IntoIter<String, Value>;
delegate_iterator!((IntoIter) => (String, Value));
//////////////////////////////////////////////////////////////////////////////
/// An iterator over a toml::Map's keys.
pub struct Keys<'a> {
iter: KeysImpl<'a>,
}
#[cfg(not(feature = "preserve_order"))]
type KeysImpl<'a> = btree_map::Keys<'a, String, Value>;
#[cfg(feature = "preserve_order")]
type KeysImpl<'a> = indexmap::map::Keys<'a, String, Value>;
delegate_iterator!((Keys<'a>) => &'a String);
//////////////////////////////////////////////////////////////////////////////
/// An iterator over a toml::Map's values.
pub struct Values<'a> {
iter: ValuesImpl<'a>,
}
#[cfg(not(feature = "preserve_order"))]
type ValuesImpl<'a> = btree_map::Values<'a, String, Value>;
#[cfg(feature = "preserve_order")]
type ValuesImpl<'a> = indexmap::map::Values<'a, String, Value>;
delegate_iterator!((Values<'a>) => &'a Value);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,168 @@
use serde::{de, ser};
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
pub(crate) const NAME: &str = "$__toml_private_Spanned";
pub(crate) const START: &str = "$__toml_private_start";
pub(crate) const END: &str = "$__toml_private_end";
pub(crate) const VALUE: &str = "$__toml_private_value";
/// A spanned value, indicating the range at which it is defined in the source.
///
/// ```
/// use serde_derive::Deserialize;
/// use toml::Spanned;
///
/// #[derive(Deserialize)]
/// struct Value {
/// s: Spanned<String>,
/// }
///
/// fn main() {
/// let t = "s = \"value\"\n";
///
/// let u: Value = toml::from_str(t).unwrap();
///
/// assert_eq!(u.s.start(), 4);
/// assert_eq!(u.s.end(), 11);
/// assert_eq!(u.s.get_ref(), "value");
/// assert_eq!(u.s.into_inner(), String::from("value"));
/// }
/// ```
#[derive(Clone, Debug)]
pub struct Spanned<T> {
/// The start range.
start: usize,
/// The end range (exclusive).
end: usize,
/// The spanned value.
value: T,
}
impl<T> Spanned<T> {
/// Access the start of the span of the contained value.
pub fn start(&self) -> usize {
self.start
}
/// Access the end of the span of the contained value.
pub fn end(&self) -> usize {
self.end
}
/// Get the span of the contained value.
pub fn span(&self) -> (usize, usize) {
(self.start, self.end)
}
/// Consumes the spanned value and returns the contained value.
pub fn into_inner(self) -> T {
self.value
}
/// Returns a reference to the contained value.
pub fn get_ref(&self) -> &T {
&self.value
}
/// Returns a mutable reference to the contained value.
pub fn get_mut(&mut self) -> &mut T {
&mut self.value
}
}
impl Borrow<str> for Spanned<String> {
fn borrow(&self) -> &str {
&self.get_ref()
}
}
impl<T: PartialEq> PartialEq for Spanned<T> {
fn eq(&self, other: &Self) -> bool {
self.value.eq(&other.value)
}
}
impl<T: Eq> Eq for Spanned<T> {}
impl<T: Hash> Hash for Spanned<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.value.hash(state);
}
}
impl<T: PartialOrd> PartialOrd for Spanned<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.value.partial_cmp(&other.value)
}
}
impl<T: Ord> Ord for Spanned<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.value.cmp(&other.value)
}
}
impl<'de, T> de::Deserialize<'de> for Spanned<T>
where
T: de::Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Spanned<T>, D::Error>
where
D: de::Deserializer<'de>,
{
struct SpannedVisitor<T>(::std::marker::PhantomData<T>);
impl<'de, T> de::Visitor<'de> for SpannedVisitor<T>
where
T: de::Deserialize<'de>,
{
type Value = Spanned<T>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a TOML spanned")
}
fn visit_map<V>(self, mut visitor: V) -> Result<Spanned<T>, V::Error>
where
V: de::MapAccess<'de>,
{
if visitor.next_key()? != Some(START) {
return Err(de::Error::custom("spanned start key not found"));
}
let start: usize = visitor.next_value()?;
if visitor.next_key()? != Some(END) {
return Err(de::Error::custom("spanned end key not found"));
}
let end: usize = visitor.next_value()?;
if visitor.next_key()? != Some(VALUE) {
return Err(de::Error::custom("spanned value key not found"));
}
let value: T = visitor.next_value()?;
Ok(Spanned { start, end, value })
}
}
let visitor = SpannedVisitor(::std::marker::PhantomData);
static FIELDS: [&str; 3] = [START, END, VALUE];
deserializer.deserialize_struct(NAME, &FIELDS, visitor)
}
}
impl<T: ser::Serialize> ser::Serialize for Spanned<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
self.value.serialize(serializer)
}
}

View File

@@ -0,0 +1,740 @@
use std::borrow::Cow;
use std::char;
use std::str;
use std::string;
use std::string::String as StdString;
use self::Token::*;
/// A span, designating a range of bytes where a token is located.
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
pub struct Span {
/// The start of the range.
pub start: usize,
/// The end of the range (exclusive).
pub end: usize,
}
impl From<Span> for (usize, usize) {
fn from(Span { start, end }: Span) -> (usize, usize) {
(start, end)
}
}
#[derive(Eq, PartialEq, Debug)]
pub enum Token<'a> {
Whitespace(&'a str),
Newline,
Comment(&'a str),
Equals,
Period,
Comma,
Colon,
Plus,
LeftBrace,
RightBrace,
LeftBracket,
RightBracket,
Keylike(&'a str),
String {
src: &'a str,
val: Cow<'a, str>,
multiline: bool,
},
}
#[derive(Eq, PartialEq, Debug)]
pub enum Error {
InvalidCharInString(usize, char),
InvalidEscape(usize, char),
InvalidHexEscape(usize, char),
InvalidEscapeValue(usize, u32),
NewlineInString(usize),
Unexpected(usize, char),
UnterminatedString(usize),
NewlineInTableKey(usize),
MultilineStringKey(usize),
Wanted {
at: usize,
expected: &'static str,
found: &'static str,
},
}
#[derive(Clone)]
pub struct Tokenizer<'a> {
input: &'a str,
chars: CrlfFold<'a>,
}
#[derive(Clone)]
struct CrlfFold<'a> {
chars: str::CharIndices<'a>,
}
#[derive(Debug)]
enum MaybeString {
NotEscaped(usize),
Owned(string::String),
}
impl<'a> Tokenizer<'a> {
pub fn new(input: &'a str) -> Tokenizer<'a> {
let mut t = Tokenizer {
input,
chars: CrlfFold {
chars: input.char_indices(),
},
};
// Eat utf-8 BOM
t.eatc('\u{feff}');
t
}
pub fn next(&mut self) -> Result<Option<(Span, Token<'a>)>, Error> {
let (start, token) = match self.one() {
Some((start, '\n')) => (start, Newline),
Some((start, ' ')) => (start, self.whitespace_token(start)),
Some((start, '\t')) => (start, self.whitespace_token(start)),
Some((start, '#')) => (start, self.comment_token(start)),
Some((start, '=')) => (start, Equals),
Some((start, '.')) => (start, Period),
Some((start, ',')) => (start, Comma),
Some((start, ':')) => (start, Colon),
Some((start, '+')) => (start, Plus),
Some((start, '{')) => (start, LeftBrace),
Some((start, '}')) => (start, RightBrace),
Some((start, '[')) => (start, LeftBracket),
Some((start, ']')) => (start, RightBracket),
Some((start, '\'')) => {
return self
.literal_string(start)
.map(|t| Some((self.step_span(start), t)))
}
Some((start, '"')) => {
return self
.basic_string(start)
.map(|t| Some((self.step_span(start), t)))
}
Some((start, ch)) if is_keylike(ch) => (start, self.keylike(start)),
Some((start, ch)) => return Err(Error::Unexpected(start, ch)),
None => return Ok(None),
};
let span = self.step_span(start);
Ok(Some((span, token)))
}
pub fn peek(&mut self) -> Result<Option<(Span, Token<'a>)>, Error> {
self.clone().next()
}
pub fn eat(&mut self, expected: Token<'a>) -> Result<bool, Error> {
self.eat_spanned(expected).map(|s| s.is_some())
}
/// Eat a value, returning it's span if it was consumed.
pub fn eat_spanned(&mut self, expected: Token<'a>) -> Result<Option<Span>, Error> {
let span = match self.peek()? {
Some((span, ref found)) if expected == *found => span,
Some(_) => return Ok(None),
None => return Ok(None),
};
drop(self.next());
Ok(Some(span))
}
pub fn expect(&mut self, expected: Token<'a>) -> Result<(), Error> {
// ignore span
let _ = self.expect_spanned(expected)?;
Ok(())
}
/// Expect the given token returning its span.
pub fn expect_spanned(&mut self, expected: Token<'a>) -> Result<Span, Error> {
let current = self.current();
match self.next()? {
Some((span, found)) => {
if expected == found {
Ok(span)
} else {
Err(Error::Wanted {
at: current,
expected: expected.describe(),
found: found.describe(),
})
}
}
None => Err(Error::Wanted {
at: self.input.len(),
expected: expected.describe(),
found: "eof",
}),
}
}
pub fn table_key(&mut self) -> Result<(Span, Cow<'a, str>), Error> {
let current = self.current();
match self.next()? {
Some((span, Token::Keylike(k))) => Ok((span, k.into())),
Some((
span,
Token::String {
src,
val,
multiline,
},
)) => {
let offset = self.substr_offset(src);
if multiline {
return Err(Error::MultilineStringKey(offset));
}
match src.find('\n') {
None => Ok((span, val)),
Some(i) => Err(Error::NewlineInTableKey(offset + i)),
}
}
Some((_, other)) => Err(Error::Wanted {
at: current,
expected: "a table key",
found: other.describe(),
}),
None => Err(Error::Wanted {
at: self.input.len(),
expected: "a table key",
found: "eof",
}),
}
}
pub fn eat_whitespace(&mut self) -> Result<(), Error> {
while self.eatc(' ') || self.eatc('\t') {
// ...
}
Ok(())
}
pub fn eat_comment(&mut self) -> Result<bool, Error> {
if !self.eatc('#') {
return Ok(false);
}
drop(self.comment_token(0));
self.eat_newline_or_eof().map(|()| true)
}
pub fn eat_newline_or_eof(&mut self) -> Result<(), Error> {
let current = self.current();
match self.next()? {
None | Some((_, Token::Newline)) => Ok(()),
Some((_, other)) => Err(Error::Wanted {
at: current,
expected: "newline",
found: other.describe(),
}),
}
}
pub fn skip_to_newline(&mut self) {
loop {
match self.one() {
Some((_, '\n')) | None => break,
_ => {}
}
}
}
fn eatc(&mut self, ch: char) -> bool {
match self.chars.clone().next() {
Some((_, ch2)) if ch == ch2 => {
self.one();
true
}
_ => false,
}
}
pub fn current(&mut self) -> usize {
self.chars
.clone()
.next()
.map(|i| i.0)
.unwrap_or_else(|| self.input.len())
}
pub fn input(&self) -> &'a str {
self.input
}
fn whitespace_token(&mut self, start: usize) -> Token<'a> {
while self.eatc(' ') || self.eatc('\t') {
// ...
}
Whitespace(&self.input[start..self.current()])
}
fn comment_token(&mut self, start: usize) -> Token<'a> {
while let Some((_, ch)) = self.chars.clone().next() {
if ch != '\t' && (ch < '\u{20}' || ch > '\u{10ffff}') {
break;
}
self.one();
}
Comment(&self.input[start..self.current()])
}
fn read_string(
&mut self,
delim: char,
start: usize,
new_ch: &mut dyn FnMut(
&mut Tokenizer<'_>,
&mut MaybeString,
bool,
usize,
char,
) -> Result<(), Error>,
) -> Result<Token<'a>, Error> {
let mut multiline = false;
if self.eatc(delim) {
if self.eatc(delim) {
multiline = true;
} else {
return Ok(String {
src: &self.input[start..start + 2],
val: Cow::Borrowed(""),
multiline: false,
});
}
}
let mut val = MaybeString::NotEscaped(self.current());
let mut n = 0;
'outer: loop {
n += 1;
match self.one() {
Some((i, '\n')) => {
if multiline {
if self.input.as_bytes()[i] == b'\r' {
val.to_owned(&self.input[..i]);
}
if n == 1 {
val = MaybeString::NotEscaped(self.current());
} else {
val.push('\n');
}
continue;
} else {
return Err(Error::NewlineInString(i));
}
}
Some((mut i, ch)) if ch == delim => {
if multiline {
if !self.eatc(delim) {
val.push(delim);
continue 'outer;
}
if !self.eatc(delim) {
val.push(delim);
val.push(delim);
continue 'outer;
}
if self.eatc(delim) {
val.push(delim);
i += 1;
}
if self.eatc(delim) {
val.push(delim);
i += 1;
}
}
return Ok(String {
src: &self.input[start..self.current()],
val: val.into_cow(&self.input[..i]),
multiline,
});
}
Some((i, c)) => new_ch(self, &mut val, multiline, i, c)?,
None => return Err(Error::UnterminatedString(start)),
}
}
}
fn literal_string(&mut self, start: usize) -> Result<Token<'a>, Error> {
self.read_string('\'', start, &mut |_me, val, _multi, i, ch| {
if ch == '\u{09}' || ('\u{20}' <= ch && ch <= '\u{10ffff}' && ch != '\u{7f}') {
val.push(ch);
Ok(())
} else {
Err(Error::InvalidCharInString(i, ch))
}
})
}
fn basic_string(&mut self, start: usize) -> Result<Token<'a>, Error> {
self.read_string('"', start, &mut |me, val, multi, i, ch| match ch {
'\\' => {
val.to_owned(&me.input[..i]);
match me.chars.next() {
Some((_, '"')) => val.push('"'),
Some((_, '\\')) => val.push('\\'),
Some((_, 'b')) => val.push('\u{8}'),
Some((_, 'f')) => val.push('\u{c}'),
Some((_, 'n')) => val.push('\n'),
Some((_, 'r')) => val.push('\r'),
Some((_, 't')) => val.push('\t'),
Some((i, c @ 'u')) | Some((i, c @ 'U')) => {
let len = if c == 'u' { 4 } else { 8 };
val.push(me.hex(start, i, len)?);
}
Some((i, c @ ' ')) | Some((i, c @ '\t')) | Some((i, c @ '\n')) if multi => {
if c != '\n' {
while let Some((_, ch)) = me.chars.clone().next() {
match ch {
' ' | '\t' => {
me.chars.next();
continue;
}
'\n' => {
me.chars.next();
break;
}
_ => return Err(Error::InvalidEscape(i, c)),
}
}
}
while let Some((_, ch)) = me.chars.clone().next() {
match ch {
' ' | '\t' | '\n' => {
me.chars.next();
}
_ => break,
}
}
}
Some((i, c)) => return Err(Error::InvalidEscape(i, c)),
None => return Err(Error::UnterminatedString(start)),
}
Ok(())
}
ch if ch == '\u{09}' || ('\u{20}' <= ch && ch <= '\u{10ffff}' && ch != '\u{7f}') => {
val.push(ch);
Ok(())
}
_ => Err(Error::InvalidCharInString(i, ch)),
})
}
fn hex(&mut self, start: usize, i: usize, len: usize) -> Result<char, Error> {
let mut buf = StdString::with_capacity(len);
for _ in 0..len {
match self.one() {
Some((_, ch)) if ch as u32 <= 0x7F && ch.is_digit(16) => buf.push(ch),
Some((i, ch)) => return Err(Error::InvalidHexEscape(i, ch)),
None => return Err(Error::UnterminatedString(start)),
}
}
let val = u32::from_str_radix(&buf, 16).unwrap();
match char::from_u32(val) {
Some(ch) => Ok(ch),
None => Err(Error::InvalidEscapeValue(i, val)),
}
}
fn keylike(&mut self, start: usize) -> Token<'a> {
while let Some((_, ch)) = self.peek_one() {
if !is_keylike(ch) {
break;
}
self.one();
}
Keylike(&self.input[start..self.current()])
}
pub fn substr_offset(&self, s: &'a str) -> usize {
assert!(s.len() <= self.input.len());
let a = self.input.as_ptr() as usize;
let b = s.as_ptr() as usize;
assert!(a <= b);
b - a
}
/// Calculate the span of a single character.
fn step_span(&mut self, start: usize) -> Span {
let end = self
.peek_one()
.map(|t| t.0)
.unwrap_or_else(|| self.input.len());
Span { start, end }
}
/// Peek one char without consuming it.
fn peek_one(&mut self) -> Option<(usize, char)> {
self.chars.clone().next()
}
/// Take one char.
pub fn one(&mut self) -> Option<(usize, char)> {
self.chars.next()
}
}
impl<'a> Iterator for CrlfFold<'a> {
type Item = (usize, char);
fn next(&mut self) -> Option<(usize, char)> {
self.chars.next().map(|(i, c)| {
if c == '\r' {
let mut attempt = self.chars.clone();
if let Some((_, '\n')) = attempt.next() {
self.chars = attempt;
return (i, '\n');
}
}
(i, c)
})
}
}
impl MaybeString {
fn push(&mut self, ch: char) {
match *self {
MaybeString::NotEscaped(..) => {}
MaybeString::Owned(ref mut s) => s.push(ch),
}
}
fn to_owned(&mut self, input: &str) {
match *self {
MaybeString::NotEscaped(start) => {
*self = MaybeString::Owned(input[start..].to_owned());
}
MaybeString::Owned(..) => {}
}
}
fn into_cow(self, input: &str) -> Cow<'_, str> {
match self {
MaybeString::NotEscaped(start) => Cow::Borrowed(&input[start..]),
MaybeString::Owned(s) => Cow::Owned(s),
}
}
}
fn is_keylike(ch: char) -> bool {
('A' <= ch && ch <= 'Z')
|| ('a' <= ch && ch <= 'z')
|| ('0' <= ch && ch <= '9')
|| ch == '-'
|| ch == '_'
}
impl<'a> Token<'a> {
pub fn describe(&self) -> &'static str {
match *self {
Token::Keylike(_) => "an identifier",
Token::Equals => "an equals",
Token::Period => "a period",
Token::Comment(_) => "a comment",
Token::Newline => "a newline",
Token::Whitespace(_) => "whitespace",
Token::Comma => "a comma",
Token::RightBrace => "a right brace",
Token::LeftBrace => "a left brace",
Token::RightBracket => "a right bracket",
Token::LeftBracket => "a left bracket",
Token::String { multiline, .. } => {
if multiline {
"a multiline string"
} else {
"a string"
}
}
Token::Colon => "a colon",
Token::Plus => "a plus",
}
}
}
#[cfg(test)]
mod tests {
use super::{Error, Token, Tokenizer};
use std::borrow::Cow;
fn err(input: &str, err: Error) {
let mut t = Tokenizer::new(input);
let token = t.next().unwrap_err();
assert_eq!(token, err);
assert!(t.next().unwrap().is_none());
}
#[test]
fn literal_strings() {
fn t(input: &str, val: &str, multiline: bool) {
let mut t = Tokenizer::new(input);
let (_, token) = t.next().unwrap().unwrap();
assert_eq!(
token,
Token::String {
src: input,
val: Cow::Borrowed(val),
multiline: multiline,
}
);
assert!(t.next().unwrap().is_none());
}
t("''", "", false);
t("''''''", "", true);
t("'''\n'''", "", true);
t("'a'", "a", false);
t("'\"a'", "\"a", false);
t("''''a'''", "'a", true);
t("'''\n'a\n'''", "'a\n", true);
t("'''a\n'a\r\n'''", "a\n'a\n", true);
}
#[test]
fn basic_strings() {
fn t(input: &str, val: &str, multiline: bool) {
let mut t = Tokenizer::new(input);
let (_, token) = t.next().unwrap().unwrap();
assert_eq!(
token,
Token::String {
src: input,
val: Cow::Borrowed(val),
multiline: multiline,
}
);
assert!(t.next().unwrap().is_none());
}
t(r#""""#, "", false);
t(r#""""""""#, "", true);
t(r#""a""#, "a", false);
t(r#""""a""""#, "a", true);
t(r#""\t""#, "\t", false);
t(r#""\u0000""#, "\0", false);
t(r#""\U00000000""#, "\0", false);
t(r#""\U000A0000""#, "\u{A0000}", false);
t(r#""\\t""#, "\\t", false);
t("\"\t\"", "\t", false);
t("\"\"\"\n\t\"\"\"", "\t", true);
t("\"\"\"\\\n\"\"\"", "", true);
t(
"\"\"\"\\\n \t \t \\\r\n \t \n \t \r\n\"\"\"",
"",
true,
);
t(r#""\r""#, "\r", false);
t(r#""\n""#, "\n", false);
t(r#""\b""#, "\u{8}", false);
t(r#""a\fa""#, "a\u{c}a", false);
t(r#""\"a""#, "\"a", false);
t("\"\"\"\na\"\"\"", "a", true);
t("\"\"\"\n\"\"\"", "", true);
t(r#""""a\"""b""""#, "a\"\"\"b", true);
err(r#""\a"#, Error::InvalidEscape(2, 'a'));
err("\"\\\n", Error::InvalidEscape(2, '\n'));
err("\"\\\r\n", Error::InvalidEscape(2, '\n'));
err("\"\\", Error::UnterminatedString(0));
err("\"\u{0}", Error::InvalidCharInString(1, '\u{0}'));
err(r#""\U00""#, Error::InvalidHexEscape(5, '"'));
err(r#""\U00"#, Error::UnterminatedString(0));
err(r#""\uD800"#, Error::InvalidEscapeValue(2, 0xd800));
err(r#""\UFFFFFFFF"#, Error::InvalidEscapeValue(2, 0xffff_ffff));
}
#[test]
fn keylike() {
fn t(input: &str) {
let mut t = Tokenizer::new(input);
let (_, token) = t.next().unwrap().unwrap();
assert_eq!(token, Token::Keylike(input));
assert!(t.next().unwrap().is_none());
}
t("foo");
t("0bar");
t("bar0");
t("1234");
t("a-b");
t("a_B");
t("-_-");
t("___");
}
#[test]
fn all() {
fn t(input: &str, expected: &[((usize, usize), Token<'_>, &str)]) {
let mut tokens = Tokenizer::new(input);
let mut actual: Vec<((usize, usize), Token<'_>, &str)> = Vec::new();
while let Some((span, token)) = tokens.next().unwrap() {
actual.push((span.into(), token, &input[span.start..span.end]));
}
for (a, b) in actual.iter().zip(expected) {
assert_eq!(a, b);
}
assert_eq!(actual.len(), expected.len());
}
t(
" a ",
&[
((0, 1), Token::Whitespace(" "), " "),
((1, 2), Token::Keylike("a"), "a"),
((2, 3), Token::Whitespace(" "), " "),
],
);
t(
" a\t [[]] \t [] {} , . =\n# foo \r\n#foo \n ",
&[
((0, 1), Token::Whitespace(" "), " "),
((1, 2), Token::Keylike("a"), "a"),
((2, 4), Token::Whitespace("\t "), "\t "),
((4, 5), Token::LeftBracket, "["),
((5, 6), Token::LeftBracket, "["),
((6, 7), Token::RightBracket, "]"),
((7, 8), Token::RightBracket, "]"),
((8, 11), Token::Whitespace(" \t "), " \t "),
((11, 12), Token::LeftBracket, "["),
((12, 13), Token::RightBracket, "]"),
((13, 14), Token::Whitespace(" "), " "),
((14, 15), Token::LeftBrace, "{"),
((15, 16), Token::RightBrace, "}"),
((16, 17), Token::Whitespace(" "), " "),
((17, 18), Token::Comma, ","),
((18, 19), Token::Whitespace(" "), " "),
((19, 20), Token::Period, "."),
((20, 21), Token::Whitespace(" "), " "),
((21, 22), Token::Equals, "="),
((22, 23), Token::Newline, "\n"),
((23, 29), Token::Comment("# foo "), "# foo "),
((29, 31), Token::Newline, "\r\n"),
((31, 36), Token::Comment("#foo "), "#foo "),
((36, 37), Token::Newline, "\n"),
((37, 38), Token::Whitespace(" "), " "),
],
);
}
#[test]
fn bare_cr_bad() {
err("\r", Error::Unexpected(0, '\r'));
err("'\n", Error::NewlineInString(1));
err("'\u{0}", Error::InvalidCharInString(1, '\u{0}'));
err("'", Error::UnterminatedString(0));
err("\u{0}", Error::Unexpected(0, '\u{0}'));
}
#[test]
fn bad_comment() {
let mut t = Tokenizer::new("#\u{0}");
t.next().unwrap().unwrap();
assert_eq!(t.next(), Err(Error::Unexpected(1, '\u{0}')));
assert!(t.next().unwrap().is_none());
}
}

File diff suppressed because it is too large Load Diff