Repository: toml-rs/toml (rust)

Issue:
Support semantic error messages by supporting deserializing spans
See [toml::Spanned](https://docs.rs/toml/latest/toml/struct.Spanned.html) for inspiration

Pull request description:
feat(edit): Spanned serialization support
Fixes #302

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 5add1f0c..7b4a40e0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -958,6 +958,7 @@ dependencies = [
  "nom8",
  "serde",
  "serde_json",
+ "serde_spanned",
  "snapbox",
  "toml 0.5.10",
  "toml-test-data",
diff --git a/crates/serde_spanned/src/spanned.rs b/crates/serde_spanned/src/spanned.rs
index db705959..f2cfbeae 100644
--- a/crates/serde_spanned/src/spanned.rs
+++ b/crates/serde_spanned/src/spanned.rs
@@ -123,19 +123,16 @@ where
                 if visitor.next_key()? != Some(START_FIELD) {
                     return Err(serde::de::Error::custom("spanned start key not found"));
                 }
-
                 let start: usize = visitor.next_value()?;
 
                 if visitor.next_key()? != Some(END_FIELD) {
                     return Err(serde::de::Error::custom("spanned end key not found"));
                 }
-
                 let end: usize = visitor.next_value()?;
 
                 if visitor.next_key()? != Some(VALUE_FIELD) {
                     return Err(serde::de::Error::custom("spanned value key not found"));
                 }
-
                 let value: T = visitor.next_value()?;
 
                 Ok(Spanned {
diff --git a/crates/toml_edit/Cargo.toml b/crates/toml_edit/Cargo.toml
index e15d5211..515ee0e9 100644
--- a/crates/toml_edit/Cargo.toml
+++ b/crates/toml_edit/Cargo.toml
@@ -38,7 +38,7 @@ pre-release-replacements = [
 default = []
 easy = ["serde"]
 perf = ["dep:kstring"]
-serde = ["dep:serde", "toml_datetime/serde"]
+serde = ["dep:serde", "toml_datetime/serde", "serde_spanned"]
 # Provide a method disable_recursion_limit to parse arbitrarily deep structures
 # without any consideration for overflowing the stack. Additionally you will
 # need to be careful around other recursive operations on the parsed result
@@ -53,6 +53,7 @@ itertools = "0.10.5"
 serde = { version = "1.0.145", features = ["derive"], optional = true }
 kstring = { version = "2.0.0", features = ["max_inline"], optional = true }
 toml_datetime = { version = "0.5.0", path = "../toml_datetime" }
+serde_spanned = { version = "0.5.0", path = "../serde_spanned", features = ["serde"], optional = true }
 
 [dev-dependencies]
 serde_json = "1.0.91"
diff --git a/crates/toml_edit/src/de/array.rs b/crates/toml_edit/src/de/array.rs
index 4aaacdad..bc398148 100644
--- a/crates/toml_edit/src/de/array.rs
+++ b/crates/toml_edit/src/de/array.rs
@@ -2,11 +2,12 @@ use crate::de::Error;
 
 pub(crate) struct ArrayDeserializer {
     input: Vec<crate::Item>,
+    span: Option<std::ops::Range<usize>>,
 }
 
 impl ArrayDeserializer {
-    pub(crate) fn new(input: Vec<crate::Item>) -> Self {
-        Self { input }
+    pub(crate) fn new(input: Vec<crate::Item>, span: Option<std::ops::Range<usize>>) -> Self {
+        Self { input, span }
     }
 }
 
@@ -20,10 +21,36 @@ impl<'de> serde::Deserializer<'de> for ArrayDeserializer {
         visitor.visit_seq(ArraySeqAccess::new(self.input))
     }
 
+    fn deserialize_struct<V>(
+        self,
+        name: &'static str,
+        fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Error>
+    where
+        V: serde::de::Visitor<'de>,
+    {
+        if super::is_spanned(name, fields) {
+            if let Some(span) = self.span.clone() {
+                return visitor.visit_map(super::SpannedDeserializer::new(self, span));
+            }
+        }
+
+        self.deserialize_any(visitor)
+    }
+
     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
-        ignored_any unit_struct tuple_struct tuple enum identifier struct
+        ignored_any unit_struct tuple_struct tuple enum identifier
+    }
+}
+
+impl<'de> serde::de::IntoDeserializer<'de, crate::de::Error> for ArrayDeserializer {
+    type Deserializer = Self;
+
+    fn into_deserializer(self) -> Self {
+        self
     }
 }
 
@@ -37,10 +64,28 @@ impl<'de> serde::Deserializer<'de> for crate::Array {
         visitor.visit_seq(ArraySeqAccess::with_array(self))
     }
 
+    fn deserialize_struct<V>(
+        self,
+        name: &'static str,
+        fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Error>
+    where
+        V: serde::de::Visitor<'de>,
+    {
+        if super::is_spanned(name, fields) {
+            if let Some(span) = self.span() {
+                return visitor.visit_map(super::SpannedDeserializer::new(self, span));
+            }
+        }
+
+        self.deserialize_any(visitor)
+    }
+
     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
-        ignored_any unit_struct tuple_struct tuple enum identifier struct
+        ignored_any unit_struct tuple_struct tuple enum identifier
     }
 }
 
@@ -62,10 +107,28 @@ impl<'de> serde::Deserializer<'de> for crate::ArrayOfTables {
         visitor.visit_seq(ArraySeqAccess::with_array_of_tables(self))
     }
 
+    fn deserialize_struct<V>(
+        self,
+        name: &'static str,
+        fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Error>
+    where
+        V: serde::de::Visitor<'de>,
+    {
+        if super::is_spanned(name, fields) {
+            if let Some(span) = self.span() {
+                return visitor.visit_map(super::SpannedDeserializer::new(self, span));
+            }
+        }
+
+        self.deserialize_any(visitor)
+    }
+
     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
-        ignored_any unit_struct tuple_struct tuple enum identifier struct
+        ignored_any unit_struct tuple_struct tuple enum identifier
     }
 }
 
diff --git a/crates/toml_edit/src/de/inline_table.rs b/crates/toml_edit/src/de/inline_table.rs
index becb3ea6..2155772d 100644
--- a/crates/toml_edit/src/de/inline_table.rs
+++ b/crates/toml_edit/src/de/inline_table.rs
@@ -34,13 +34,19 @@ impl<'de> serde::Deserializer<'de> for crate::InlineTable {
 
     fn deserialize_struct<V>(
         self,
-        _name: &'static str,
-        _fields: &'static [&'static str],
+        name: &'static str,
+        fields: &'static [&'static str],
         visitor: V,
     ) -> Result<V::Value, Error>
     where
         V: serde::de::Visitor<'de>,
     {
+        if super::is_spanned(name, fields) {
+            if let Some(span) = self.span() {
+                return visitor.visit_map(super::SpannedDeserializer::new(self, span));
+            }
+        }
+
         self.deserialize_any(visitor)
     }
 
@@ -87,7 +93,7 @@ impl<'de> serde::de::IntoDeserializer<'de, crate::de::Error> for crate::InlineTa
 pub(crate) struct InlineTableMapAccess {
     iter: indexmap::map::IntoIter<crate::InternalString, crate::table::TableKeyValue>,
     span: Option<std::ops::Range<usize>>,
-    value: Option<(crate::InternalString, crate::Item)>,
+    value: Option<crate::Item>,
 }
 
 impl InlineTableMapAccess {
@@ -110,15 +116,16 @@ impl<'de> serde::de::MapAccess<'de> for InlineTableMapAccess {
     {
         match self.iter.next() {
             Some((k, v)) => {
-                let ret = seed.deserialize(k.into_deserializer()).map(Some).map_err(
-                    |mut e: Self::Error| {
+                let ret = seed
+                    .deserialize(super::KeyDeserializer::new(k, v.key.span()))
+                    .map(Some)
+                    .map_err(|mut e: Self::Error| {
                         if e.span().is_none() {
                             e.set_span(v.key.span());
                         }
                         e
-                    },
-                );
-                self.value = Some((k, v.value));
+                    });
+                self.value = Some(v.value);
                 ret
             }
             None => Ok(None),
@@ -130,7 +137,7 @@ impl<'de> serde::de::MapAccess<'de> for InlineTableMapAccess {
         V: serde::de::DeserializeSeed<'de>,
     {
         match self.value.take() {
-            Some((_k, v)) => {
+            Some(v) => {
                 let span = v.span();
                 seed.deserialize(crate::de::ItemDeserializer::new(v))
                     .map_err(|mut err| {
diff --git a/crates/toml_edit/src/de/item.rs b/crates/toml_edit/src/de/item.rs
index 238e59b1..ab8b60fd 100644
--- a/crates/toml_edit/src/de/item.rs
+++ b/crates/toml_edit/src/de/item.rs
@@ -66,6 +66,12 @@ impl<'de> serde::Deserializer<'de> for ItemDeserializer {
     where
         V: serde::de::Visitor<'de>,
     {
+        if super::is_spanned(name, fields) {
+            if let Some(span) = self.input.span() {
+                return visitor.visit_map(super::SpannedDeserializer::new(self.input, span));
+            }
+        }
+
         if self.validate_struct_keys {
             match &self.input {
                 crate::Item::Table(values) => super::validate_struct_keys(&values.items, fields)?,
@@ -99,6 +105,14 @@ impl<'de> serde::Deserializer<'de> for ItemDeserializer {
     }
 }
 
+impl<'de> serde::de::IntoDeserializer<'de, crate::de::Error> for ItemDeserializer {
+    type Deserializer = Self;
+
+    fn into_deserializer(self) -> Self {
+        self
+    }
+}
+
 impl<'de> serde::Deserializer<'de> for crate::Item {
     type Error = Error;
 
@@ -168,6 +182,24 @@ impl<'de> serde::Deserializer<'de> for crate::Item {
             })
     }
 
+    fn deserialize_struct<V>(
+        self,
+        name: &'static str,
+        fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Error>
+    where
+        V: serde::de::Visitor<'de>,
+    {
+        if super::is_spanned(name, fields) {
+            if let Some(span) = self.span() {
+                return visitor.visit_map(super::SpannedDeserializer::new(self, span));
+            }
+        }
+
+        self.deserialize_any(visitor)
+    }
+
     // Called when the type to deserialize is an enum, as opposed to a field in the type.
     fn deserialize_enum<V>(
         self,
@@ -217,7 +249,7 @@ impl<'de> serde::Deserializer<'de> for crate::Item {
 
     serde::forward_to_deserialize_any! {
         bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
-        bytes byte_buf map unit struct
+        bytes byte_buf map unit
         ignored_any unit_struct tuple_struct tuple identifier
     }
 }
diff --git a/crates/toml_edit/src/de/key.rs b/crates/toml_edit/src/de/key.rs
new file mode 100644
index 00000000..1816ce76
--- /dev/null
+++ b/crates/toml_edit/src/de/key.rs
@@ -0,0 +1,140 @@
+use serde::de::IntoDeserializer;
+
+use super::Error;
+
+pub(crate) struct KeyDeserializer {
+    span: Option<std::ops::Range<usize>>,
+    key: crate::InternalString,
+}
+
+impl KeyDeserializer {
+    pub(crate) fn new(key: crate::InternalString, span: Option<std::ops::Range<usize>>) -> Self {
+        KeyDeserializer { span, key }
+    }
+}
+
+impl<'de> serde::de::IntoDeserializer<'de, Error> for KeyDeserializer {
+    type Deserializer = Self;
+
+    fn into_deserializer(self) -> Self::Deserializer {
+        self
+    }
+}
+
+impl<'de> serde::de::Deserializer<'de> for KeyDeserializer {
+    type Error = Error;
+
+    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
+    where
+        V: serde::de::Visitor<'de>,
+    {
+        self.key.into_deserializer().deserialize_any(visitor)
+    }
+
+    fn deserialize_enum<V>(
+        self,
+        name: &str,
+        variants: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Self::Error>
+    where
+        V: serde::de::Visitor<'de>,
+    {
+        let _ = name;
+        let _ = variants;
+        visitor.visit_enum(self)
+    }
+
+    fn deserialize_struct<V>(
+        self,
+        name: &'static str,
+        fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Error>
+    where
+        V: serde::de::Visitor<'de>,
+    {
+        if super::is_spanned(name, fields) {
+            if let Some(span) = self.span.clone() {
+                return visitor.visit_map(super::SpannedDeserializer::new(self.key.as_str(), span));
+            }
+        }
+        self.deserialize_any(visitor)
+    }
+
+    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
+        ignored_any unit_struct tuple_struct tuple identifier
+    }
+}
+
+impl<'de> serde::de::EnumAccess<'de> for KeyDeserializer {
+    type Error = super::Error;
+    type Variant = UnitOnly<Self::Error>;
+
+    fn variant_seed<T>(self, seed: T) -> Result<(T::Value, Self::Variant), Self::Error>
+    where
+        T: serde::de::DeserializeSeed<'de>,
+    {
+        seed.deserialize(self).map(unit_only)
+    }
+}
+
+pub(crate) struct UnitOnly<E> {
+    marker: std::marker::PhantomData<E>,
+}
+
+fn unit_only<T, E>(t: T) -> (T, UnitOnly<E>) {
+    (
+        t,
+        UnitOnly {
+            marker: std::marker::PhantomData,
+        },
+    )
+}
+
+impl<'de, E> serde::de::VariantAccess<'de> for UnitOnly<E>
+where
+    E: serde::de::Error,
+{
+    type Error = E;
+
+    fn unit_variant(self) -> Result<(), Self::Error> {
+        Ok(())
+    }
+
+    fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value, Self::Error>
+    where
+        T: serde::de::DeserializeSeed<'de>,
+    {
+        Err(serde::de::Error::invalid_type(
+            serde::de::Unexpected::UnitVariant,
+            &"newtype variant",
+        ))
+    }
+
+    fn tuple_variant<V>(self, _len: usize, _visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: serde::de::Visitor<'de>,
+    {
+        Err(serde::de::Error::invalid_type(
+            serde::de::Unexpected::UnitVariant,
+            &"tuple variant",
+        ))
+    }
+
+    fn struct_variant<V>(
+        self,
+        _fields: &'static [&'static str],
+        _visitor: V,
+    ) -> Result<V::Value, Self::Error>
+    where
+        V: serde::de::Visitor<'de>,
+    {
+        Err(serde::de::Error::invalid_type(
+            serde::de::Unexpected::UnitVariant,
+            &"struct variant",
+        ))
+    }
+}
diff --git a/crates/toml_edit/src/de/mod.rs b/crates/toml_edit/src/de/mod.rs
index e45d8c1b..cfa81470 100644
--- a/crates/toml_edit/src/de/mod.rs
+++ b/crates/toml_edit/src/de/mod.rs
@@ -8,6 +8,8 @@ use serde::de::DeserializeOwned;
 mod array;
 mod inline_table;
 mod item;
+mod key;
+mod spanned;
 mod table;
 mod table_enum;
 mod value;
@@ -15,6 +17,8 @@ mod value;
 use array::*;
 use inline_table::*;
 use item::*;
+use key::*;
+use spanned::*;
 use table::*;
 use table_enum::*;
 
@@ -148,6 +152,18 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
         self.input.deserialize_option(visitor)
     }
 
+    fn deserialize_struct<V>(
+        self,
+        name: &'static str,
+        fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Error>
+    where
+        V: serde::de::Visitor<'de>,
+    {
+        self.input.deserialize_struct(name, fields, visitor)
+    }
+
     // Called when the type to deserialize is an enum, as opposed to a field in the type.
     fn deserialize_enum<V>(
         self,
@@ -164,7 +180,7 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
     serde::forward_to_deserialize_any! {
         bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
         bytes byte_buf map unit newtype_struct
-        ignored_any unit_struct tuple_struct tuple identifier struct
+        ignored_any unit_struct tuple_struct tuple identifier
     }
 }
 
@@ -199,6 +215,24 @@ impl<'de> serde::Deserializer<'de> for crate::Document {
             })
     }
 
+    fn deserialize_struct<V>(
+        self,
+        name: &'static str,
+        fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Error>
+    where
+        V: serde::de::Visitor<'de>,
+    {
+        if is_spanned(name, fields) {
+            if let Some(span) = self.span() {
+                return visitor.visit_map(SpannedDeserializer::new(self, span));
+            }
+        }
+
+        self.deserialize_any(visitor)
+    }
+
     // Called when the type to deserialize is an enum, as opposed to a field in the type.
     fn deserialize_enum<V>(
         self,
@@ -221,7 +255,7 @@ impl<'de> serde::Deserializer<'de> for crate::Document {
     serde::forward_to_deserialize_any! {
         bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
         bytes byte_buf map unit newtype_struct
-        ignored_any unit_struct tuple_struct tuple identifier struct
+        ignored_any unit_struct tuple_struct tuple identifier
     }
 }
 
diff --git a/crates/toml_edit/src/de/spanned.rs b/crates/toml_edit/src/de/spanned.rs
new file mode 100644
index 00000000..0ebfb7fd
--- /dev/null
+++ b/crates/toml_edit/src/de/spanned.rs
@@ -0,0 +1,74 @@
+use serde::de::value::BorrowedStrDeserializer;
+use serde::de::IntoDeserializer as _;
+
+use super::Error;
+
+pub(crate) struct SpannedDeserializer<'de, T: serde::de::IntoDeserializer<'de, Error>> {
+    phantom_data: std::marker::PhantomData<&'de ()>,
+    start: Option<usize>,
+    end: Option<usize>,
+    value: Option<T>,
+}
+
+impl<'de, T> SpannedDeserializer<'de, T>
+where
+    T: serde::de::IntoDeserializer<'de, Error>,
+{
+    pub(crate) fn new(value: T, span: std::ops::Range<usize>) -> Self {
+        Self {
+            phantom_data: Default::default(),
+            start: Some(span.start),
+            end: Some(span.end),
+            value: Some(value),
+        }
+    }
+}
+
+impl<'de, T> serde::de::MapAccess<'de> for SpannedDeserializer<'de, T>
+where
+    T: serde::de::IntoDeserializer<'de, Error>,
+{
+    type Error = Error;
+    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
+    where
+        K: serde::de::DeserializeSeed<'de>,
+    {
+        if self.start.is_some() {
+            seed.deserialize(BorrowedStrDeserializer::new(serde_spanned::START_FIELD))
+                .map(Some)
+        } else if self.end.is_some() {
+            seed.deserialize(BorrowedStrDeserializer::new(serde_spanned::END_FIELD))
+                .map(Some)
+        } else if self.value.is_some() {
+            seed.deserialize(BorrowedStrDeserializer::new(serde_spanned::VALUE_FIELD))
+                .map(Some)
+        } else {
+            Ok(None)
+        }
+    }
+
+    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
+    where
+        V: serde::de::DeserializeSeed<'de>,
+    {
+        if let Some(start) = self.start.take() {
+            seed.deserialize(start.into_deserializer())
+        } else if let Some(end) = self.end.take() {
+            seed.deserialize(end.into_deserializer())
+        } else if let Some(value) = self.value.take() {
+            seed.deserialize(value.into_deserializer())
+        } else {
+            panic!("next_value_seed called before next_key_seed")
+        }
+    }
+}
+
+pub(crate) fn is_spanned(name: &'static str, fields: &'static [&'static str]) -> bool {
+    name == serde_spanned::NAME
+        && fields
+            == [
+                serde_spanned::START_FIELD,
+                serde_spanned::END_FIELD,
+                serde_spanned::VALUE_FIELD,
+            ]
+}
diff --git a/crates/toml_edit/src/de/table.rs b/crates/toml_edit/src/de/table.rs
index fd5aca32..ba19796e 100644
--- a/crates/toml_edit/src/de/table.rs
+++ b/crates/toml_edit/src/de/table.rs
@@ -34,13 +34,19 @@ impl<'de> serde::Deserializer<'de> for crate::Table {
 
     fn deserialize_struct<V>(
         self,
-        _name: &'static str,
-        _fields: &'static [&'static str],
+        name: &'static str,
+        fields: &'static [&'static str],
         visitor: V,
     ) -> Result<V::Value, Error>
     where
         V: serde::de::Visitor<'de>,
     {
+        if super::is_spanned(name, fields) {
+            if let Some(span) = self.span() {
+                return visitor.visit_map(super::SpannedDeserializer::new(self, span));
+            }
+        }
+
         self.deserialize_any(visitor)
     }
 
@@ -87,7 +93,7 @@ impl<'de> serde::de::IntoDeserializer<'de, crate::de::Error> for crate::Table {
 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::Item>,
 }
 
 impl TableMapAccess {
@@ -110,15 +116,16 @@ impl<'de> serde::de::MapAccess<'de> for TableMapAccess {
     {
         match self.iter.next() {
             Some((k, v)) => {
-                let ret = seed.deserialize(k.into_deserializer()).map(Some).map_err(
-                    |mut e: Self::Error| {
+                let ret = seed
+                    .deserialize(super::KeyDeserializer::new(k, v.key.span()))
+                    .map(Some)
+                    .map_err(|mut e: Self::Error| {
                         if e.span().is_none() {
                             e.set_span(v.key.span());
                         }
                         e
-                    },
-                );
-                self.value = Some((k, v.value));
+                    });
+                self.value = Some(v.value);
                 ret
             }
             None => Ok(None),
@@ -130,7 +137,7 @@ impl<'de> serde::de::MapAccess<'de> for TableMapAccess {
         V: serde::de::DeserializeSeed<'de>,
     {
         match self.value.take() {
-            Some((_k, v)) => seed.deserialize(crate::de::ItemDeserializer::new(v)),
+            Some(v) => seed.deserialize(crate::de::ItemDeserializer::new(v)),
             None => {
                 panic!("no more values in next_value_seed, internal error in ItemDeserializer")
             }
diff --git a/crates/toml_edit/src/de/table_enum.rs b/crates/toml_edit/src/de/table_enum.rs
index bea79d41..40cfe62a 100644
--- a/crates/toml_edit/src/de/table_enum.rs
+++ b/crates/toml_edit/src/de/table_enum.rs
@@ -82,7 +82,7 @@ impl<'de> serde::de::VariantAccess<'de> for TableEnumDeserializer {
 
                 if tuple_values.len() == len {
                     serde::de::Deserializer::deserialize_seq(
-                        super::ArrayDeserializer::new(tuple_values),
+                        super::ArrayDeserializer::new(tuple_values, values_span),
                         visitor,
                     )
                 } else {
@@ -125,7 +125,7 @@ impl<'de> serde::de::VariantAccess<'de> for TableEnumDeserializer {
 
                 if tuple_values.len() == len {
                     serde::de::Deserializer::deserialize_seq(
-                        super::ArrayDeserializer::new(tuple_values),
+                        super::ArrayDeserializer::new(tuple_values, values_span),
                         visitor,
                     )
                 } else {
diff --git a/crates/toml_edit/src/de/value.rs b/crates/toml_edit/src/de/value.rs
index f24bc54c..1ffa2a0f 100644
--- a/crates/toml_edit/src/de/value.rs
+++ b/crates/toml_edit/src/de/value.rs
@@ -43,6 +43,12 @@ impl<'de> serde::Deserializer<'de> for crate::Value {
             }
         }
 
+        if super::is_spanned(name, fields) {
+            if let Some(span) = self.span() {
+                return visitor.visit_map(super::SpannedDeserializer::new(self, span));
+            }
+        }
+
         self.deserialize_any(visitor)
     }
 
diff --git a/crates/toml_edit/src/easy/mod.rs b/crates/toml_edit/src/easy/mod.rs
index 78c09e81..fbf081c1 100644
--- a/crates/toml_edit/src/easy/mod.rs
+++ b/crates/toml_edit/src/easy/mod.rs
@@ -147,5 +147,6 @@ pub use crate::toml;
 pub use de::{from_document, from_slice, from_str, Deserializer};
 #[doc(no_inline)]
 pub use ser::{to_document, to_string, to_string_pretty, to_vec, Serializer};
+pub use serde_spanned::Spanned;
 #[doc(no_inline)]
 pub use value::Value;


Test patch:
diff --git a/crates/toml_edit/tests/testsuite/main.rs b/crates/toml_edit/tests/testsuite/main.rs
index afb1f328..16943b15 100644
--- a/crates/toml_edit/tests/testsuite/main.rs
+++ b/crates/toml_edit/tests/testsuite/main.rs
@@ -8,4 +8,6 @@ mod macros;
 mod parse;
 mod pretty;
 mod serde;
+mod spanned;
+mod spanned_impls;
 mod stackoverflow;
diff --git a/crates/toml_edit/tests/testsuite/spanned.rs b/crates/toml_edit/tests/testsuite/spanned.rs
new file mode 100644
index 00000000..1359f4bf
--- /dev/null
+++ b/crates/toml_edit/tests/testsuite/spanned.rs
@@ -0,0 +1,237 @@
+#![cfg(feature = "easy")]
+#![allow(renamed_and_removed_lints)]
+#![allow(clippy::blacklisted_name)]
+
+use std::collections::HashMap;
+use std::fmt::Debug;
+
+use toml_edit::easy as toml;
+
+use serde::Deserialize;
+use toml_edit::easy::value::Datetime;
+use toml_edit::easy::Spanned;
+
+/// A set of good datetimes.
+pub fn good_datetimes() -> Vec<&'static str> {
+    vec![
+        "1997-09-09T09:09:09Z",
+        "1997-09-09T09:09:09+09:09",
+        "1997-09-09T09:09:09-09:09",
+        "1997-09-09T09:09:09",
+        "1997-09-09",
+        "09:09:09",
+        "1997-09-09T09:09:09.09Z",
+        "1997-09-09T09:09:09.09+09:09",
+        "1997-09-09T09:09:09.09-09:09",
+        "1997-09-09T09:09:09.09",
+        "09:09:09.09",
+    ]
+}
+
+#[test]
+fn test_spanned_field() {
+    #[derive(Deserialize)]
+    struct Foo<T> {
+        foo: Spanned<T>,
+    }
+
+    #[derive(Deserialize)]
+    struct BareFoo<T> {
+        foo: T,
+    }
+
+    fn good<T>(s: &str, expected: &str, end: Option<usize>)
+    where
+        T: serde::de::DeserializeOwned + Debug + PartialEq,
+    {
+        let foo: Foo<T> = toml::from_str(s).unwrap();
+
+        assert_eq!(6, foo.foo.span().start);
+        if let Some(end) = end {
+            assert_eq!(end, foo.foo.span().end);
+        } else {
+            assert_eq!(s.len(), foo.foo.span().end);
+        }
+        assert_eq!(expected, &s[foo.foo.span()]);
+
+        // Test for Spanned<> at the top level
+        let foo_outer: Spanned<BareFoo<T>> = toml::from_str(s).unwrap();
+
+        assert_eq!(0, foo_outer.span().start);
+        assert_eq!(s.len(), foo_outer.span().end);
+        assert_eq!(foo.foo.into_inner(), foo_outer.into_inner().foo);
+    }
+
+    good::<String>("foo = \"foo\"", "\"foo\"", None);
+    good::<u32>("foo = 42", "42", None);
+    // leading plus
+    good::<u32>("foo = +42", "+42", None);
+    // table
+    good::<HashMap<String, u32>>(
+        "foo = {\"foo\" = 42, \"bar\" = 42}",
+        "{\"foo\" = 42, \"bar\" = 42}",
+        None,
+    );
+    // array
+    good::<Vec<u32>>("foo = [0, 1, 2, 3, 4]", "[0, 1, 2, 3, 4]", None);
+    // datetime
+    good::<String>(
+        "foo = \"1997-09-09T09:09:09Z\"",
+        "\"1997-09-09T09:09:09Z\"",
+        None,
+    );
+
+    for expected in good_datetimes() {
+        let s = format!("foo = {}", expected);
+        good::<Datetime>(&s, expected, None);
+    }
+    // ending at something other than the absolute end
+    good::<u32>("foo = 42\nnoise = true", "42", Some(8));
+}
+
+#[test]
+fn test_inner_spanned_table() {
+    #[derive(Deserialize)]
+    struct Foo {
+        foo: Spanned<HashMap<Spanned<String>, Spanned<String>>>,
+    }
+
+    fn good(s: &str, zero: bool) {
+        let foo: Foo = toml::from_str(s).unwrap();
+
+        if zero {
+            assert_eq!(foo.foo.span().start, 0);
+            assert_eq!(foo.foo.span().end, 73);
+        } else {
+            assert_eq!(foo.foo.span().start, s.find('{').unwrap());
+            assert_eq!(foo.foo.span().end, s.find('}').unwrap() + 1);
+        }
+        for (k, v) in foo.foo.as_ref().iter() {
+            assert_eq!(&s[k.span().start..k.span().end], k.as_ref());
+            assert_eq!(&s[(v.span().start + 1)..(v.span().end - 1)], v.as_ref());
+        }
+    }
+
+    good(
+        "\
+        [foo]
+        a = 'b'
+        bar = 'baz'
+        c = 'd'
+        e = \"f\"
+    ",
+        true,
+    );
+
+    good(
+        "
+        foo = { a = 'b', bar = 'baz', c = 'd', e = \"f\" }",
+        false,
+    );
+}
+
+#[test]
+fn test_outer_spanned_table() {
+    #[derive(Deserialize)]
+    struct Foo {
+        foo: HashMap<Spanned<String>, Spanned<String>>,
+    }
+
+    fn good(s: &str) {
+        let foo: Foo = toml::from_str(s).unwrap();
+
+        for (k, v) in foo.foo.iter() {
+            assert_eq!(&s[k.span().start..k.span().end], k.as_ref());
+            assert_eq!(&s[(v.span().start + 1)..(v.span().end - 1)], v.as_ref());
+        }
+    }
+
+    good(
+        "
+        [foo]
+        a = 'b'
+        bar = 'baz'
+        c = 'd'
+        e = \"f\"
+    ",
+    );
+
+    good(
+        "
+        foo = { a = 'b', bar = 'baz', c = 'd', e = \"f\" }
+    ",
+    );
+}
+
+#[test]
+fn test_spanned_nested() {
+    #[derive(Deserialize)]
+    struct Foo {
+        foo: HashMap<Spanned<String>, HashMap<Spanned<String>, Spanned<String>>>,
+    }
+
+    fn good(s: &str) {
+        let foo: Foo = toml::from_str(s).unwrap();
+
+        for (k, v) in foo.foo.iter() {
+            assert_eq!(&s[k.span().start..k.span().end], k.as_ref());
+            for (n_k, n_v) in v.iter() {
+                assert_eq!(&s[n_k.span().start..n_k.span().end], n_k.as_ref());
+                assert_eq!(
+                    &s[(n_v.span().start + 1)..(n_v.span().end - 1)],
+                    n_v.as_ref()
+                );
+            }
+        }
+    }
+
+    good(
+        "
+        [foo.a]
+        a = 'b'
+        c = 'd'
+        e = \"f\"
+        [foo.bar]
+        baz = 'true'
+    ",
+    );
+
+    good(
+        "
+        [foo]
+        foo = { a = 'b', bar = 'baz', c = 'd', e = \"f\" }
+        bazz = {}
+        g = { h = 'i' }
+    ",
+    );
+}
+
+#[test]
+fn test_spanned_array() {
+    #[derive(Deserialize)]
+    struct Foo {
+        foo: Vec<Spanned<HashMap<Spanned<String>, Spanned<String>>>>,
+    }
+
+    let toml = "\
+        [[foo]]
+        a = 'b'
+        bar = 'baz'
+        c = 'd'
+        e = \"f\"
+        [[foo]]
+        a = 'c'
+        bar = 'baz'
+        c = 'g'
+        e = \"h\"
+    ";
+    let foo_list: Foo = toml::from_str(toml).unwrap();
+
+    for (foo, expected) in foo_list.foo.iter().zip([0..75, 84..159]) {
+        assert_eq!(foo.span(), expected);
+        for (k, v) in foo.as_ref().iter() {
+            assert_eq!(&toml[k.span().start..k.span().end], k.as_ref());
+            assert_eq!(&toml[(v.span().start + 1)..(v.span().end - 1)], v.as_ref());
+        }
+    }
+}
diff --git a/crates/toml_edit/tests/testsuite/spanned_impls.rs b/crates/toml_edit/tests/testsuite/spanned_impls.rs
new file mode 100644
index 00000000..ceb21991
--- /dev/null
+++ b/crates/toml_edit/tests/testsuite/spanned_impls.rs
@@ -0,0 +1,43 @@
+#![cfg(feature = "easy")]
+
+use std::cmp::{Ord, Ordering, PartialOrd};
+
+use serde::Deserialize;
+use toml_edit::easy::{from_str, Spanned};
+
+#[test]
+fn test_spans_impls() {
+    #[derive(Deserialize)]
+    struct Foo {
+        bar: Spanned<bool>,
+        baz: Spanned<String>,
+    }
+    let f: Foo = from_str(
+        "
+    bar = true
+    baz = \"yes\"
+    ",
+    )
+    .unwrap();
+    let g: Foo = from_str(
+        "
+    baz = \"yes\"
+    bar = true
+    ",
+    )
+    .unwrap();
+    assert!(f.bar.span() != g.bar.span());
+    assert!(f.baz.span() != g.baz.span());
+
+    // test that eq still holds
+    assert_eq!(f.bar, g.bar);
+    assert_eq!(f.baz, g.baz);
+
+    // test that Ord returns equal order
+    assert_eq!(f.bar.cmp(&g.bar), Ordering::Equal);
+    assert_eq!(f.baz.cmp(&g.baz), Ordering::Equal);
+
+    // test that PartialOrd returns equal order
+    assert_eq!(f.bar.partial_cmp(&g.bar), Some(Ordering::Equal));
+    assert_eq!(f.baz.partial_cmp(&g.baz), Some(Ordering::Equal));
+}