Skip to main content

Spanned

Struct Spanned 

Source
pub struct Spanned<T> {
    pub value: T,
    pub referenced: Location,
    pub defined: Location,
}
Expand description

A value paired with source locations describing where it came from. Spanned locations are specified in character positions and, when possible, in byte offsets as well. Byte offsets are available for string sources but not reader sources.

§Example

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Cfg {
    timeout: serde_saphyr::Spanned<u64>,
}

let cfg: Cfg = serde_saphyr::from_str("timeout: 5\n").unwrap();
assert_eq!(cfg.timeout.value, 5);
assert_eq!(cfg.timeout.referenced.line(), 1);
assert_eq!(cfg.timeout.referenced.column(), 10);

§Location semantics for YAML aliases and merges

Spanned<T> exposes two locations:

  • referenced: where the value is referenced/used in the YAML.
    • For aliases (*a): this is the location of the alias token.
    • For merge-derived values (<<): this is the location of the merge entry (typically the <<: *a site).
  • defined: where the value is defined in YAML.
    • For plain values: equals referenced.
    • For aliases: points to the anchored definition.
    • For merge-derived values: points to the originating scalar in the merged mapping.

§Limitation with #[serde(flatten)], #[serde(untagged)], and #[serde(tag = "...")]

When Spanned<T> is used inside a struct with #[serde(flatten)], or inside variants of #[serde(untagged)] or #[serde(tag = "...")] enums, deserialization succeeds but location information is lost: both referenced and defined will be Location::UNKNOWN (line 0, column 0).

This is because serde buffers values through a generic ContentDeserializer in these cases, which discards the YAML deserializer context needed to capture spans.

§Workaround for untagged/internally-tagged enums: Wrap the entire enum

Instead of putting Spanned<T> inside each variant, wrap the whole enum:

use serde::Deserialize;
use serde_saphyr::Spanned;

#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum Payload {
    StringVariant { message: String },
    IntVariant { count: u32 },
}

// Use Spanned<Payload> instead of Spanned<T> inside variants
let yaml = "message: hello";
let result: Spanned<Payload> = serde_saphyr::from_str(yaml).unwrap();
assert_eq!(result.referenced.line(), 1);

§Alternative: Use externally tagged enums (serde default)

Externally tagged enums (the default) work with Spanned<T> inside variants:

use serde::Deserialize;
use serde_saphyr::Spanned;

#[derive(Debug, Deserialize)]
pub enum Payload {
    StringVariant { message: Spanned<String> },
    IntVariant { count: Spanned<u32> },
}

let yaml = "StringVariant:\n  message: hello";
let result: Payload = serde_saphyr::from_str(yaml).unwrap();
match result {
    Payload::StringVariant { message } => {
        assert_eq!(&message.value, "hello");
        assert_eq!(message.referenced.line(), 2);
    }
    _ => panic!("Expected StringVariant"),
}

§Alternative: Use adjacently tagged enums

Adjacently tagged enums also work with Spanned<T> inside variants:

use serde::Deserialize;
use serde_saphyr::Spanned;

#[derive(Debug, Deserialize)]
#[serde(tag = "type", content = "data")]
pub enum Payload {
    StringVariant { message: Spanned<String> },
    IntVariant { count: Spanned<u32> },
}

let yaml = "type: StringVariant\ndata:\n  message: hello";
let result: Payload = serde_saphyr::from_str(yaml).unwrap();
match result {
    Payload::StringVariant { message } => {
        assert_eq!(&message.value, "hello");
        assert_eq!(message.referenced.line(), 3);
    }
    _ => panic!("Expected StringVariant"),
}

Fields§

§value: T§referenced: Location§defined: Location

Implementations§

Source§

impl<T> Spanned<T>

Source

pub const fn new(value: T, referenced: Location, defined: Location) -> Self

Trait Implementations§

Source§

impl<T: Clone> Clone for Spanned<T>

Source§

fn clone(&self) -> Spanned<T>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T: Debug> Debug for Spanned<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de, T> Deserialize<'de> for Spanned<T>
where T: Deserialize<'de>,

Source§

fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl<T: Eq> Eq for Spanned<T>

Source§

impl<T: PartialEq> PartialEq for Spanned<T>

Source§

fn eq(&self, other: &Spanned<T>) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl<T> Serialize for Spanned<T>
where T: Serialize,

Source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl<T: PartialEq> StructuralPartialEq for Spanned<T>

Auto Trait Implementations§

§

impl<T> Freeze for Spanned<T>
where T: Freeze,

§

impl<T> RefUnwindSafe for Spanned<T>
where T: RefUnwindSafe,

§

impl<T> Send for Spanned<T>
where T: Send,

§

impl<T> Sync for Spanned<T>
where T: Sync,

§

impl<T> Unpin for Spanned<T>
where T: Unpin,

§

impl<T> UnsafeUnpin for Spanned<T>
where T: UnsafeUnpin,

§

impl<T> UnwindSafe for Spanned<T>
where T: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.