use proc_macro2::Span;
use std::ops::{Deref, DerefMut};
use syn::spanned::Spanned;
use crate::{
FromDeriveInput, FromField, FromGenericParam, FromGenerics, FromMeta, FromTypeParam,
FromVariant, Result,
};
#[derive(Debug, Clone, Copy)]
pub struct SpannedValue<T> {
value: T,
span: Span,
}
impl<T> SpannedValue<T> {
pub fn new(value: T, span: Span) -> Self {
SpannedValue { value, span }
}
pub fn span(&self) -> Span {
self.span
}
pub fn map_ref<U>(&self, map_fn: impl FnOnce(&T) -> U) -> SpannedValue<U> {
SpannedValue::new(map_fn(&self.value), self.span)
}
}
impl<T: Default> Default for SpannedValue<T> {
fn default() -> Self {
SpannedValue::new(Default::default(), Span::call_site())
}
}
impl<T> Deref for SpannedValue<T> {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
impl<T> DerefMut for SpannedValue<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.value
}
}
impl<T> AsRef<T> for SpannedValue<T> {
fn as_ref(&self) -> &T {
&self.value
}
}
macro_rules! spanned {
($trayt:ident, $method:ident, $syn:path) => {
impl<T: $trayt> $trayt for SpannedValue<T> {
fn $method(value: &$syn) -> Result<Self> {
Ok(SpannedValue::new(
$trayt::$method(value).map_err(|e| e.with_span(value))?,
value.span(),
))
}
}
};
}
impl<T: FromMeta> FromMeta for SpannedValue<T> {
fn from_meta(item: &syn::Meta) -> Result<Self> {
let value = T::from_meta(item).map_err(|e| e.with_span(item))?;
let span = match item {
syn::Meta::Path(path) => path.span(),
syn::Meta::List(list) => list.tokens.span(),
syn::Meta::NameValue(nv) => nv.value.span(),
};
Ok(Self::new(value, span))
}
}
spanned!(FromGenericParam, from_generic_param, syn::GenericParam);
spanned!(FromGenerics, from_generics, syn::Generics);
spanned!(FromTypeParam, from_type_param, syn::TypeParam);
spanned!(FromDeriveInput, from_derive_input, syn::DeriveInput);
spanned!(FromField, from_field, syn::Field);
spanned!(FromVariant, from_variant, syn::Variant);
impl<T: Spanned> From<T> for SpannedValue<T> {
fn from(value: T) -> Self {
let span = value.span();
SpannedValue::new(value, span)
}
}
#[cfg(test)]
mod tests {
use super::*;
use proc_macro2::Span;
#[test]
fn deref() {
let test = SpannedValue::new("hello", Span::call_site());
assert_eq!("hello", test.trim());
}
}