Skip to main content

darling_core/util/
spanned_value.rs

1use proc_macro2::Span;
2use quote::{quote_spanned, ToTokens, TokenStreamExt};
3use std::ops::{Deref, DerefMut};
4use syn::spanned::Spanned;
5
6use crate::{
7    FromDeriveInput, FromField, FromGenericParam, FromGenerics, FromMeta, FromTypeParam,
8    FromVariant, Result,
9};
10
11/// A value and an associated position in source code. The main use case for this is
12/// to preserve position information to emit warnings from proc macros. You can use
13/// a `SpannedValue<T>` as a field in any struct that implements or derives any of
14/// `darling`'s core traits.
15///
16/// To access the underlying value, use the struct's `Deref` implementation.
17///
18/// # Defaulting
19/// This type is meant to be used in conjunction with attribute-extracted options,
20/// but the user may not always explicitly set those options in their source code.
21/// In this case, using `Default::default()` will create an instance which points
22/// to `Span::call_site()`.
23#[derive(Debug, Clone, Copy)]
24pub struct SpannedValue<T> {
25    value: T,
26    span: Span,
27}
28
29impl<T> SpannedValue<T> {
30    pub fn new(value: T, span: Span) -> Self {
31        SpannedValue { value, span }
32    }
33
34    /// Get the source code location referenced by this struct.
35    pub fn span(&self) -> Span {
36        self.span
37    }
38
39    /// Apply a mapping function to a reference to the spanned value.
40    pub fn map_ref<U>(&self, map_fn: impl FnOnce(&T) -> U) -> SpannedValue<U> {
41        SpannedValue::new(map_fn(&self.value), self.span)
42    }
43
44    /// Gets the inner value, consuming `self` in the process.
45    pub fn into_inner(self) -> T {
46        self.value
47    }
48}
49
50impl<T: Default> Default for SpannedValue<T> {
51    fn default() -> Self {
52        SpannedValue::new(Default::default(), Span::call_site())
53    }
54}
55
56impl<T> Deref for SpannedValue<T> {
57    type Target = T;
58
59    fn deref(&self) -> &T {
60        &self.value
61    }
62}
63
64impl<T> DerefMut for SpannedValue<T> {
65    fn deref_mut(&mut self) -> &mut T {
66        &mut self.value
67    }
68}
69
70impl<T> AsRef<T> for SpannedValue<T> {
71    fn as_ref(&self) -> &T {
72        &self.value
73    }
74}
75
76macro_rules! spanned {
77    ($trayt:ident, $method:ident, $syn:path) => {
78        impl<T: $trayt> $trayt for SpannedValue<T> {
79            fn $method(value: &$syn) -> Result<Self> {
80                Ok(SpannedValue::new(
81                    $trayt::$method(value).map_err(|e| e.with_span(value))?,
82                    value.span(),
83                ))
84            }
85        }
86    };
87}
88
89impl<T: FromMeta> FromMeta for SpannedValue<T> {
90    fn from_meta(item: &syn::Meta) -> Result<Self> {
91        let value = T::from_meta(item).map_err(|e| e.with_span(item))?;
92        let span = match item {
93            // Example: `#[darling(skip)]` as SpannedValue<bool>
94            // should have the span pointing to the word `skip`.
95            syn::Meta::Path(path) => path.span(),
96            // Example: `#[darling(attributes(Value))]` as a SpannedValue<Vec<String>>
97            // should have the span pointing to the list contents.
98            syn::Meta::List(list) => list.tokens.span(),
99            // Example: `#[darling(skip = true)]` as SpannedValue<bool>
100            // should have the span pointing to the word `true`.
101            syn::Meta::NameValue(nv) => nv.value.span(),
102        };
103
104        Ok(Self::new(value, span))
105    }
106
107    fn from_nested_meta(item: &crate::ast::NestedMeta) -> Result<Self> {
108        T::from_nested_meta(item)
109            .map(|value| Self::new(value, item.span()))
110            .map_err(|e| e.with_span(item))
111    }
112
113    fn from_value(literal: &syn::Lit) -> Result<Self> {
114        T::from_value(literal)
115            .map(|value| Self::new(value, literal.span()))
116            .map_err(|e| e.with_span(literal))
117    }
118
119    fn from_expr(expr: &syn::Expr) -> Result<Self> {
120        T::from_expr(expr)
121            .map(|value| Self::new(value, expr.span()))
122            .map_err(|e| e.with_span(expr))
123    }
124}
125
126spanned!(FromGenericParam, from_generic_param, syn::GenericParam);
127spanned!(FromGenerics, from_generics, syn::Generics);
128spanned!(FromTypeParam, from_type_param, syn::TypeParam);
129spanned!(FromDeriveInput, from_derive_input, syn::DeriveInput);
130spanned!(FromField, from_field, syn::Field);
131spanned!(FromVariant, from_variant, syn::Variant);
132
133impl<T: Spanned> From<T> for SpannedValue<T> {
134    fn from(value: T) -> Self {
135        let span = value.span();
136        SpannedValue::new(value, span)
137    }
138}
139
140impl<T: ToTokens> ToTokens for SpannedValue<T> {
141    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
142        let value = &self.value;
143        tokens.append_all(quote_spanned!(self.span()=> #value));
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150    use proc_macro2::Span;
151
152    /// Make sure that `SpannedValue` can be seamlessly used as its underlying type.
153    #[test]
154    fn deref() {
155        let test = SpannedValue::new("hello", Span::call_site());
156        assert_eq!("hello", test.trim());
157    }
158}