1//! Functions to use with `#[darling(with = "...")]` that control how quoted values
2//! in [`Meta`] instances are parsed into [`Expr`] fields.
3//!
4//! Version 1 of syn did not permit expressions on the right-hand side of the `=` in a
5//! [`MetaNameValue`](syn::MetaNameValue), so darling accepted string literals and then
6//! parsed their contents as expressions.
7//! Passing a string literal in this version would have required the use of a raw string
8//! to add quotation marks inside the literal.
9//!
10//! Version 2 of syn removes the requirement that the right-hand side be a literal.
11//! For most types, such as [`Path`](syn::Path), the [`FromMeta`] impl can accept the
12//! version without quotation marks without causing ambiguity; a path cannot start and
13//! end with quotation marks, so removal is automatic.
14//!
15//! [`Expr`] is the one type where this ambiguity is new and unavoidable. To address this,
16//! this module provides different functions for different expected behaviors.
17
18use syn::{Expr, Meta};
19
20use crate::{Error, FromMeta};
21
22/// Parse a [`Meta`] to an [`Expr`]; if the value is a string literal, the emitted
23/// expression will be a string literal.
24pub fn preserve_str_literal(meta: &Meta) -> crate::Result<Expr> {
25 match meta {
26 Meta::Path(_) => Err(Error::unsupported_format("path").with_span(node:meta)),
27 Meta::List(_) => Err(Error::unsupported_format("list").with_span(node:meta)),
28 Meta::NameValue(nv: &MetaNameValue) => Ok(nv.value.clone()),
29 }
30}
31
32/// Parse a [`Meta`] to an [`Expr`]; if the value is a string literal, the string's
33/// contents will be parsed as an expression and emitted.
34pub fn parse_str_literal(meta: &Meta) -> crate::Result<Expr> {
35 match meta {
36 Meta::Path(_) => Err(Error::unsupported_format("path").with_span(node:meta)),
37 Meta::List(_) => Err(Error::unsupported_format("list").with_span(node:meta)),
38 Meta::NameValue(nv: &MetaNameValue) => {
39 if let Expr::Lit(expr_lit: &ExprLit) = &nv.value {
40 Expr::from_value(&expr_lit.lit)
41 } else {
42 Ok(nv.value.clone())
43 }
44 }
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use syn::parse_quote;
51
52 use super::*;
53
54 macro_rules! meta {
55 ($body:expr) => {
56 {
57 let attr: ::syn::Attribute = ::syn::parse_quote!(#[ignore = $body]);
58 attr.meta
59 }
60 };
61 }
62
63 #[test]
64 fn preserve_str() {
65 assert_eq!(
66 preserve_str_literal(&meta!("World")).unwrap(),
67 parse_quote!("World")
68 );
69 }
70
71 #[test]
72 fn preserve_binary_exp() {
73 assert_eq!(
74 preserve_str_literal(&meta!("World" + 5)).unwrap(),
75 parse_quote!("World" + 5)
76 )
77 }
78
79 #[test]
80 fn parse_ident() {
81 assert_eq!(
82 parse_str_literal(&meta!("world")).unwrap(),
83 parse_quote!(world)
84 )
85 }
86}
87