darling_core/util/
callable.rs

1use quote::ToTokens;
2use syn::{Expr, ExprClosure, ExprLit, ExprPath, Lit, Path};
3
4use crate::{Error, FromMeta, Result};
5
6/// Either a path or a closure.
7///
8/// This type is useful for options that historically took a path,
9/// e.g. `#[darling(with = ...)]` or `#[serde(skip_serializing_if = ...)]`
10/// and now want to also allow using a closure to avoid needing a separate
11/// function declaration.
12///
13/// In `darling`, this value is wrapped in [`core::convert::identity`] before usage;
14/// this allows treatment of the closure and path cases as equivalent, and prevents
15/// a closure from accessing locals in the generated code.
16#[derive(Debug, Clone)]
17pub struct Callable {
18    /// The callable
19    call: Expr,
20}
21
22impl AsRef<Expr> for Callable {
23    fn as_ref(&self) -> &Expr {
24        &self.call
25    }
26}
27
28impl From<Path> for Callable {
29    fn from(path: Path) -> Self {
30        Self::from(ExprPath {
31            attrs: vec![],
32            qself: None,
33            path,
34        })
35    }
36}
37
38impl From<ExprPath> for Callable {
39    fn from(value: ExprPath) -> Self {
40        Self {
41            call: Expr::Path(value),
42        }
43    }
44}
45
46impl From<ExprClosure> for Callable {
47    fn from(value: ExprClosure) -> Self {
48        Self {
49            call: Expr::Closure(value),
50        }
51    }
52}
53
54impl From<Callable> for Expr {
55    fn from(value: Callable) -> Self {
56        value.call
57    }
58}
59
60impl FromMeta for Callable {
61    fn from_expr(expr: &Expr) -> Result<Self> {
62        match expr {
63            Expr::Path(_) | Expr::Closure(_) => Ok(Self { call: expr.clone() }),
64            Expr::Lit(ExprLit {
65                lit: Lit::Str(s), ..
66            }) => syn::parse_str::<Path>(&s.value())
67                .map_err(|e| {
68                    Error::custom(format!("`with` must be a path if it's a string: {}", e))
69                        .with_span(s)
70                })
71                .map(Self::from),
72            _ => Err(Error::unexpected_expr_type(expr)),
73        }
74    }
75}
76
77impl ToTokens for Callable {
78    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
79        self.call.to_tokens(tokens);
80    }
81}