use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::ops::Range;
#[derive(Debug, Clone)]
pub struct SpannedValue<T> {
value: T,
span: Option<Range<usize>>,
}
impl<T> SpannedValue<T> {
#[cfg(test)]
pub fn new(value: T, span: Range<usize>) -> Self {
Self {
value,
span: Some(span),
}
}
pub fn without_span(value: T) -> Self {
Self { value, span: None }
}
pub fn value(&self) -> &T {
&self.value
}
pub fn span(&self) -> Option<Range<usize>> {
self.span.clone()
}
}
impl<T> AsRef<T> for SpannedValue<T> {
fn as_ref(&self) -> &T {
&self.value
}
}
impl<T> From<T> for SpannedValue<T> {
fn from(value: T) -> Self {
Self::without_span(value)
}
}
impl<T: Serialize> Serialize for SpannedValue<T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.value.serialize(serializer)
}
}
impl<'de, T: Deserialize<'de>> Deserialize<'de> for SpannedValue<T> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let spanned = serde_spanned::Spanned::<T>::deserialize(deserializer)?;
let span = spanned.span();
let value = spanned.into_inner();
Ok(Self {
value,
span: Some(span),
})
}
}
impl<T: PartialEq> PartialEq for SpannedValue<T> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<T: Eq> Eq for SpannedValue<T> {}
impl<T: std::fmt::Display> std::fmt::Display for SpannedValue<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}
impl<T: schemars::JsonSchema> schemars::JsonSchema for SpannedValue<T> {
fn schema_name() -> std::borrow::Cow<'static, str> {
T::schema_name()
}
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
T::json_schema(generator)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spanned_value_creation() {
let sv = SpannedValue::new("hello".to_string(), 0..5);
assert_eq!(sv.value(), "hello");
assert_eq!(sv.span(), Some(0..5));
}
#[test]
fn test_spanned_value_without_span() {
let sv = SpannedValue::without_span("hello".to_string());
assert_eq!(sv.value(), "hello");
assert_eq!(sv.span(), None);
}
#[test]
fn test_spanned_value_from() {
let sv: SpannedValue<String> = "hello".to_string().into();
assert_eq!(sv.value(), "hello");
assert_eq!(sv.span(), None);
}
#[test]
fn test_spanned_value_serialize() {
let sv = SpannedValue::new("test".to_string(), 10..14);
let json = serde_json::to_string(&sv).unwrap();
assert_eq!(json, r#""test""#);
}
#[test]
fn test_spanned_value_deserialize_toml() {
#[derive(Deserialize)]
struct TestConfig {
name: SpannedValue<String>,
}
let toml = r#"name = "hello""#;
let config: TestConfig = toml_edit::de::from_str(toml).unwrap();
assert_eq!(config.name.value(), "hello");
assert!(config.name.span().is_some());
}
#[test]
fn test_spanned_value_equality() {
let sv1 = SpannedValue::new("hello".to_string(), 0..5);
let sv2 = SpannedValue::new("hello".to_string(), 10..15);
let sv3 = SpannedValue::new("world".to_string(), 0..5);
assert_eq!(sv1, sv2);
assert_ne!(sv1, sv3);
}
}