Skip to main content

darling_core/util/
callable.rs

1use quote::ToTokens;
2use syn::{parse::Parse, 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 Parse for Callable {
55    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
56        Ok(Self {
57            call: input.parse()?,
58        })
59    }
60}
61
62impl From<Callable> for Expr {
63    fn from(value: Callable) -> Self {
64        value.call
65    }
66}
67
68impl FromMeta for Callable {
69    fn from_expr(expr: &Expr) -> Result<Self> {
70        match expr {
71            Expr::Path(_) | Expr::Closure(_) => Ok(Self { call: expr.clone() }),
72            Expr::Lit(ExprLit {
73                lit: Lit::Str(s), ..
74            }) => s
75                .parse::<Path>()
76                .map_err(|e| {
77                    Error::custom(format!("must be a path if it's a string: {}", e)).with_span(s)
78                })
79                .map(Self::from),
80            _ => Err(Error::unexpected_expr_type(expr)),
81        }
82    }
83}
84
85impl ToTokens for Callable {
86    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
87        self.call.to_tokens(tokens);
88    }
89}