Proof of concept: reporting span information to a Spanned<T> type
This commit is contained in:
parent
24d0f25427
commit
6db43965f9
|
@ -20,7 +20,7 @@ travis-ci = { repository = "alexcrichton/toml-rs" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_derive = "1.0"
|
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
175
src/de.rs
175
src/de.rs
|
@ -13,8 +13,9 @@ use std::vec;
|
||||||
use serde::de;
|
use serde::de;
|
||||||
use serde::de::IntoDeserializer;
|
use serde::de::IntoDeserializer;
|
||||||
|
|
||||||
use tokens::{Tokenizer, Token, Error as TokenError};
|
use tokens::{Tokenizer, Token, Error as TokenError, Span};
|
||||||
use datetime::{SERDE_STRUCT_FIELD_NAME, SERDE_STRUCT_NAME};
|
use datetime::{SERDE_STRUCT_FIELD_NAME, SERDE_STRUCT_NAME};
|
||||||
|
use spanned;
|
||||||
|
|
||||||
/// Deserializes a byte slice into a type.
|
/// Deserializes a byte slice into a type.
|
||||||
///
|
///
|
||||||
|
@ -238,7 +239,7 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
|
||||||
{
|
{
|
||||||
if let Some(next) = self.next()? {
|
if let Some(next) = self.next()? {
|
||||||
match next {
|
match next {
|
||||||
Token::String { val, .. } => {
|
(_, Token::String { val, .. }) => {
|
||||||
visitor.visit_enum(val.into_deserializer())
|
visitor.visit_enum(val.into_deserializer())
|
||||||
},
|
},
|
||||||
_ => Err(Error::from_kind(ErrorKind::ExpectedString))
|
_ => Err(Error::from_kind(ErrorKind::ExpectedString))
|
||||||
|
@ -504,23 +505,23 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
|
||||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
|
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||||
where V: de::Visitor<'de>,
|
where V: de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
match self.value {
|
match self.value.e {
|
||||||
Value::Integer(i) => visitor.visit_i64(i),
|
E::Integer(i) => visitor.visit_i64(i),
|
||||||
Value::Boolean(b) => visitor.visit_bool(b),
|
E::Boolean(b) => visitor.visit_bool(b),
|
||||||
Value::Float(f) => visitor.visit_f64(f),
|
E::Float(f) => visitor.visit_f64(f),
|
||||||
Value::String(Cow::Borrowed(s)) => visitor.visit_borrowed_str(s),
|
E::String(Cow::Borrowed(s)) => visitor.visit_borrowed_str(s),
|
||||||
Value::String(Cow::Owned(s)) => visitor.visit_string(s),
|
E::String(Cow::Owned(s)) => visitor.visit_string(s),
|
||||||
Value::Datetime(s) => visitor.visit_map(DatetimeDeserializer {
|
E::Datetime(s) => visitor.visit_map(DatetimeDeserializer {
|
||||||
date: s,
|
date: s,
|
||||||
visited: false,
|
visited: false,
|
||||||
}),
|
}),
|
||||||
Value::Array(values) => {
|
E::Array(values) => {
|
||||||
let mut s = de::value::SeqDeserializer::new(values.into_iter());
|
let mut s = de::value::SeqDeserializer::new(values.into_iter());
|
||||||
let ret = visitor.visit_seq(&mut s)?;
|
let ret = visitor.visit_seq(&mut s)?;
|
||||||
s.end()?;
|
s.end()?;
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
Value::InlineTable(values) => {
|
E::InlineTable(values) => {
|
||||||
visitor.visit_map(InlineTableDeserializer {
|
visitor.visit_map(InlineTableDeserializer {
|
||||||
values: values.into_iter(),
|
values: values.into_iter(),
|
||||||
next_value: None,
|
next_value: None,
|
||||||
|
@ -536,7 +537,7 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
|
||||||
where V: de::Visitor<'de>,
|
where V: de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
if name == SERDE_STRUCT_NAME && fields == &[SERDE_STRUCT_FIELD_NAME] {
|
if name == SERDE_STRUCT_NAME && fields == &[SERDE_STRUCT_FIELD_NAME] {
|
||||||
if let Value::Datetime(s) = self.value {
|
if let E::Datetime(s) = self.value.e {
|
||||||
return visitor.visit_map(DatetimeDeserializer {
|
return visitor.visit_map(DatetimeDeserializer {
|
||||||
date: s,
|
date: s,
|
||||||
visited: false,
|
visited: false,
|
||||||
|
@ -544,6 +545,16 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if name == spanned::NAME && fields == spanned::FIELDS {
|
||||||
|
let start = self.value.start;
|
||||||
|
let end = self.value.end;
|
||||||
|
return visitor.visit_map(SpannedDeserializer {
|
||||||
|
start: Some(start),
|
||||||
|
value: Some(self.value),
|
||||||
|
end: Some(end),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
self.deserialize_any(visitor)
|
self.deserialize_any(visitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,8 +574,8 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
|
||||||
) -> Result<V::Value, Error>
|
) -> Result<V::Value, Error>
|
||||||
where V: de::Visitor<'de>
|
where V: de::Visitor<'de>
|
||||||
{
|
{
|
||||||
match self.value {
|
match self.value.e {
|
||||||
Value::String(val) => visitor.visit_enum(val.into_deserializer()),
|
E::String(val) => visitor.visit_enum(val.into_deserializer()),
|
||||||
_ => Err(Error::from_kind(ErrorKind::ExpectedString))
|
_ => Err(Error::from_kind(ErrorKind::ExpectedString))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -594,6 +605,46 @@ impl<'de> de::IntoDeserializer<'de, Error> for Value<'de> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SpannedDeserializer<'a> {
|
||||||
|
start: Option<usize>,
|
||||||
|
value: Option<Value<'a>>,
|
||||||
|
end: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> de::MapAccess<'de> for SpannedDeserializer<'de> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
|
||||||
|
where
|
||||||
|
K: de::DeserializeSeed<'de>,
|
||||||
|
{
|
||||||
|
if self.start.is_some() {
|
||||||
|
seed.deserialize("start".into_deserializer()).map(Some)
|
||||||
|
} else if self.value.is_some() {
|
||||||
|
seed.deserialize("value".into_deserializer()).map(Some)
|
||||||
|
} else if self.end.is_some() {
|
||||||
|
seed.deserialize("end".into_deserializer()).map(Some)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
|
||||||
|
where
|
||||||
|
V: de::DeserializeSeed<'de>,
|
||||||
|
{
|
||||||
|
if let Some(start) = self.start.take() {
|
||||||
|
seed.deserialize(start.into_deserializer())
|
||||||
|
} else if let Some(value) = self.value.take() {
|
||||||
|
seed.deserialize(value.into_deserializer())
|
||||||
|
} else if let Some(end) = self.end.take() {
|
||||||
|
seed.deserialize(end.into_deserializer())
|
||||||
|
} else {
|
||||||
|
panic!("next_value_seed called before next_key_seed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct DatetimeDeserializer<'a> {
|
struct DatetimeDeserializer<'a> {
|
||||||
visited: bool,
|
visited: bool,
|
||||||
date: &'a str,
|
date: &'a str,
|
||||||
|
@ -707,7 +758,7 @@ impl<'a> Deserializer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.peek()? {
|
match self.peek()? {
|
||||||
Some(Token::LeftBracket) => self.table_header().map(Some),
|
Some((_, Token::LeftBracket)) => self.table_header().map(Some),
|
||||||
Some(_) => self.key_value().map(Some),
|
Some(_) => self.key_value().map(Some),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
|
@ -725,13 +776,13 @@ impl<'a> Deserializer<'a> {
|
||||||
} else {
|
} else {
|
||||||
loop {
|
loop {
|
||||||
match self.next()? {
|
match self.next()? {
|
||||||
Some(Token::RightBracket) => {
|
Some((_, Token::RightBracket)) => {
|
||||||
if array {
|
if array {
|
||||||
self.eat(Token::RightBracket)?;
|
self.eat(Token::RightBracket)?;
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
Some(Token::Newline) |
|
Some((_, Token::Newline)) |
|
||||||
None => break,
|
None => break,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -759,17 +810,27 @@ impl<'a> Deserializer<'a> {
|
||||||
fn value(&mut self) -> Result<Value<'a>, Error> {
|
fn value(&mut self) -> Result<Value<'a>, Error> {
|
||||||
let at = self.tokens.current();
|
let at = self.tokens.current();
|
||||||
let value = match self.next()? {
|
let value = match self.next()? {
|
||||||
Some(Token::String { val, .. }) => Value::String(val),
|
Some((Span { start, end }, Token::String { val, .. })) => {
|
||||||
Some(Token::Keylike("true")) => Value::Boolean(true),
|
Value { e: E::String(val), start, end }
|
||||||
Some(Token::Keylike("false")) => Value::Boolean(false),
|
}
|
||||||
Some(Token::Keylike(key)) => self.number_or_date(key)?,
|
Some((Span { start, end }, Token::Keylike("true"))) => {
|
||||||
Some(Token::Plus) => self.number_leading_plus()?,
|
Value { e: E::Boolean(true), start, end }
|
||||||
Some(Token::LeftBrace) => self.inline_table().map(Value::InlineTable)?,
|
}
|
||||||
Some(Token::LeftBracket) => self.array().map(Value::Array)?,
|
Some((Span { start, end }, Token::Keylike("false"))) => {
|
||||||
|
Value { e: E::Boolean(false), start, end }
|
||||||
|
}
|
||||||
|
Some((_, Token::Keylike(key))) => self.number_or_date(key)?,
|
||||||
|
Some((_, Token::Plus)) => self.number_leading_plus()?,
|
||||||
|
Some((Span { start, end }, Token::LeftBrace)) => {
|
||||||
|
self.inline_table().map(|table| Value { e: E::InlineTable(table), start, end })?
|
||||||
|
}
|
||||||
|
Some((Span { start, end }, Token::LeftBracket)) => {
|
||||||
|
self.array().map(|array| Value { e: E::Array(array), start, end })?
|
||||||
|
}
|
||||||
Some(token) => {
|
Some(token) => {
|
||||||
return Err(self.error(at, ErrorKind::Wanted {
|
return Err(self.error(at, ErrorKind::Wanted {
|
||||||
expected: "a value",
|
expected: "a value",
|
||||||
found: token.describe(),
|
found: token.1.describe(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
None => return Err(self.eof()),
|
None => return Err(self.eof()),
|
||||||
|
@ -780,9 +841,11 @@ impl<'a> Deserializer<'a> {
|
||||||
fn number_or_date(&mut self, s: &'a str) -> Result<Value<'a>, Error> {
|
fn number_or_date(&mut self, s: &'a str) -> Result<Value<'a>, Error> {
|
||||||
if s.contains('T') || (s.len() > 1 && s[1..].contains('-')) &&
|
if s.contains('T') || (s.len() > 1 && s[1..].contains('-')) &&
|
||||||
!s.contains("e-") {
|
!s.contains("e-") {
|
||||||
self.datetime(s, false).map(Value::Datetime)
|
// FIXME needs span
|
||||||
|
self.datetime(s, false).map(|d| Value { e: E::Datetime(d), start: 0, end: 0 })
|
||||||
} else if self.eat(Token::Colon)? {
|
} else if self.eat(Token::Colon)? {
|
||||||
self.datetime(s, true).map(Value::Datetime)
|
// FIXME needs span
|
||||||
|
self.datetime(s, true).map(|d| Value { e: E::Datetime(d), start: 0, end: 0 })
|
||||||
} else {
|
} else {
|
||||||
self.number(s)
|
self.number(s)
|
||||||
}
|
}
|
||||||
|
@ -790,24 +853,27 @@ impl<'a> Deserializer<'a> {
|
||||||
|
|
||||||
fn number(&mut self, s: &'a str) -> Result<Value<'a>, Error> {
|
fn number(&mut self, s: &'a str) -> Result<Value<'a>, Error> {
|
||||||
if s.contains('e') || s.contains('E') {
|
if s.contains('e') || s.contains('E') {
|
||||||
self.float(s, None).map(Value::Float)
|
// FIXME needs span
|
||||||
|
self.float(s, None).map(|f| Value { e: E::Float(f), start: 0, end: 0 })
|
||||||
} else if self.eat(Token::Period)? {
|
} else if self.eat(Token::Period)? {
|
||||||
let at = self.tokens.current();
|
let at = self.tokens.current();
|
||||||
match self.next()? {
|
match self.next()? {
|
||||||
Some(Token::Keylike(after)) => {
|
Some((_, Token::Keylike(after))) => {
|
||||||
self.float(s, Some(after)).map(Value::Float)
|
// FIXME needs span
|
||||||
|
self.float(s, Some(after)).map(|f| Value { e: E::Float(f), start: 0, end: 0 })
|
||||||
}
|
}
|
||||||
_ => Err(self.error(at, ErrorKind::NumberInvalid)),
|
_ => Err(self.error(at, ErrorKind::NumberInvalid)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.integer(s).map(Value::Integer)
|
// FIXME needs span
|
||||||
|
self.integer(s).map(|f| Value { e: E::Integer(f), start: 0, end: 0 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn number_leading_plus(&mut self) -> Result<Value<'a>, Error> {
|
fn number_leading_plus(&mut self) -> Result<Value<'a>, Error> {
|
||||||
let start = self.tokens.current();
|
let start = self.tokens.current();
|
||||||
match self.next()? {
|
match self.next()? {
|
||||||
Some(Token::Keylike(s)) => self.number(s),
|
Some((_, Token::Keylike(s))) => self.number(s),
|
||||||
_ => Err(self.error(start, ErrorKind::NumberInvalid)),
|
_ => Err(self.error(start, ErrorKind::NumberInvalid)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -884,7 +950,7 @@ impl<'a> Deserializer<'a> {
|
||||||
let (a, b) = if suffix.len() == 1 {
|
let (a, b) = if suffix.len() == 1 {
|
||||||
self.eat(Token::Plus)?;
|
self.eat(Token::Plus)?;
|
||||||
match self.next()? {
|
match self.next()? {
|
||||||
Some(Token::Keylike(s)) => {
|
Some((_, Token::Keylike(s))) => {
|
||||||
self.parse_integer(s, false, false)?
|
self.parse_integer(s, false, false)?
|
||||||
}
|
}
|
||||||
_ => return Err(self.error(start, ErrorKind::NumberInvalid)),
|
_ => return Err(self.error(start, ErrorKind::NumberInvalid)),
|
||||||
|
@ -927,19 +993,19 @@ impl<'a> Deserializer<'a> {
|
||||||
if colon_eaten || self.eat(Token::Colon)? {
|
if colon_eaten || self.eat(Token::Colon)? {
|
||||||
// minutes
|
// minutes
|
||||||
match self.next()? {
|
match self.next()? {
|
||||||
Some(Token::Keylike(_)) => {}
|
Some((_, Token::Keylike(_))) => {}
|
||||||
_ => return Err(self.error(start, ErrorKind::DateInvalid)),
|
_ => return Err(self.error(start, ErrorKind::DateInvalid)),
|
||||||
}
|
}
|
||||||
// Seconds
|
// Seconds
|
||||||
self.expect(Token::Colon)?;
|
self.expect(Token::Colon)?;
|
||||||
match self.next()? {
|
match self.next()? {
|
||||||
Some(Token::Keylike(_)) => {}
|
Some((_, Token::Keylike(_))) => {}
|
||||||
_ => return Err(self.error(start, ErrorKind::DateInvalid)),
|
_ => return Err(self.error(start, ErrorKind::DateInvalid)),
|
||||||
}
|
}
|
||||||
// Fractional seconds
|
// Fractional seconds
|
||||||
if self.eat(Token::Period)? {
|
if self.eat(Token::Period)? {
|
||||||
match self.next()? {
|
match self.next()? {
|
||||||
Some(Token::Keylike(_)) => {}
|
Some((_, Token::Keylike(_))) => {}
|
||||||
_ => return Err(self.error(start, ErrorKind::DateInvalid)),
|
_ => return Err(self.error(start, ErrorKind::DateInvalid)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -947,13 +1013,13 @@ impl<'a> Deserializer<'a> {
|
||||||
// offset
|
// offset
|
||||||
if self.eat(Token::Plus)? {
|
if self.eat(Token::Plus)? {
|
||||||
match self.next()? {
|
match self.next()? {
|
||||||
Some(Token::Keylike(_)) => {}
|
Some((_, Token::Keylike(_))) => {}
|
||||||
_ => return Err(self.error(start, ErrorKind::DateInvalid)),
|
_ => return Err(self.error(start, ErrorKind::DateInvalid)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.eat(Token::Colon)? {
|
if self.eat(Token::Colon)? {
|
||||||
match self.next()? {
|
match self.next()? {
|
||||||
Some(Token::Keylike(_)) => {}
|
Some((_, Token::Keylike(_))) => {}
|
||||||
_ => return Err(self.error(start, ErrorKind::DateInvalid)),
|
_ => return Err(self.error(start, ErrorKind::DateInvalid)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1048,12 +1114,12 @@ impl<'a> Deserializer<'a> {
|
||||||
self.tokens.expect(expected).map_err(|e| self.token_error(e))
|
self.tokens.expect(expected).map_err(|e| self.token_error(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self) -> Result<Option<Token<'a>>, Error> {
|
fn next(&mut self) -> Result<Option<(Span, Token<'a>)>, Error> {
|
||||||
self.tokens.next().map(|t| t.map(|t| t.1)).map_err(|e| self.token_error(e))
|
self.tokens.next().map_err(|e| self.token_error(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek(&mut self) -> Result<Option<Token<'a>>, Error> {
|
fn peek(&mut self) -> Result<Option<(Span, Token<'a>)>, Error> {
|
||||||
self.tokens.peek().map(|t| t.map(|t| t.1)).map_err(|e| self.token_error(e))
|
self.tokens.peek().map_err(|e| self.token_error(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eof(&self) -> Error {
|
fn eof(&self) -> Error {
|
||||||
|
@ -1300,7 +1366,14 @@ impl<'a> Header<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Value<'a> {
|
struct Value<'a> {
|
||||||
|
e: E<'a>,
|
||||||
|
start: usize,
|
||||||
|
end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum E<'a> {
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
|
@ -1312,14 +1385,14 @@ enum Value<'a> {
|
||||||
|
|
||||||
impl<'a> Value<'a> {
|
impl<'a> Value<'a> {
|
||||||
fn same_type(&self, other: &Value<'a>) -> bool {
|
fn same_type(&self, other: &Value<'a>) -> bool {
|
||||||
match (self, other) {
|
match (&self.e, &other.e) {
|
||||||
(&Value::String(..), &Value::String(..)) |
|
(&E::String(..), &E::String(..)) |
|
||||||
(&Value::Integer(..), &Value::Integer(..)) |
|
(&E::Integer(..), &E::Integer(..)) |
|
||||||
(&Value::Float(..), &Value::Float(..)) |
|
(&E::Float(..), &E::Float(..)) |
|
||||||
(&Value::Boolean(..), &Value::Boolean(..)) |
|
(&E::Boolean(..), &E::Boolean(..)) |
|
||||||
(&Value::Datetime(..), &Value::Datetime(..)) |
|
(&E::Datetime(..), &E::Datetime(..)) |
|
||||||
(&Value::Array(..), &Value::Array(..)) |
|
(&E::Array(..), &E::Array(..)) |
|
||||||
(&Value::InlineTable(..), &Value::InlineTable(..)) => true,
|
(&E::InlineTable(..), &E::InlineTable(..)) => true,
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,6 +153,8 @@
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
pub mod value;
|
pub mod value;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
|
@ -169,3 +171,5 @@ mod tokens;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
|
|
||||||
|
pub mod spanned;
|
||||||
|
|
49
src/spanned.rs
Normal file
49
src/spanned.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
//! ```
|
||||||
|
//! #[macro_use]
|
||||||
|
//! extern crate serde_derive;
|
||||||
|
//!
|
||||||
|
//! extern crate toml;
|
||||||
|
//! use toml::spanned::Spanned;
|
||||||
|
//!
|
||||||
|
//! #[derive(Deserialize)]
|
||||||
|
//! struct Udoprog {
|
||||||
|
//! s: Spanned<String>,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn main() {
|
||||||
|
//! let t = "s = \"udoprog\"\n";
|
||||||
|
//!
|
||||||
|
//! let u: Udoprog = toml::from_str(t).unwrap();
|
||||||
|
//!
|
||||||
|
//! assert_eq!(u.s.start, 4);
|
||||||
|
//! assert_eq!(u.s.end, 13);
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use serde::{Serialize, Serializer};
|
||||||
|
|
||||||
|
// FIXME: use a more unique name like "toml::Spanned".
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub const NAME: &str = "Spanned";
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub const FIELDS: &[&str] = &["value", "start", "end"];
|
||||||
|
|
||||||
|
///
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Spanned<T> {
|
||||||
|
///
|
||||||
|
pub value: T,
|
||||||
|
///
|
||||||
|
pub start: usize,
|
||||||
|
///
|
||||||
|
pub end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Serialize> Serialize for Spanned<T> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
self.value.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue