liquid_derive/
helpers.rs

1//! Utility items for this crate.
2
3use std::str::FromStr;
4use syn::spanned::Spanned as _;
5use syn::{meta, Attribute, Error, LitStr, Path, Result};
6
7/// A wrapper around a type that only allows its value to be assigned once.
8#[derive(Debug, Default)]
9pub(crate) enum AssignOnce<T> {
10    Set(T),
11    #[default]
12    Unset,
13}
14
15impl<T> AssignOnce<T> {
16    /// Assigns `value` to `self`, however calls `err` instead if `self` is already assigned.
17    pub(crate) fn set<E, F>(&mut self, value: T, err: F) -> std::result::Result<(), E>
18    where
19        F: FnOnce() -> E,
20    {
21        match self {
22            AssignOnce::Set(_) => Err(err()),
23            AssignOnce::Unset => {
24                *self = AssignOnce::Set(value);
25                Ok(())
26            }
27        }
28    }
29
30    /// Unwraps `self`, returning `default` if `self` is not set.
31    pub(crate) fn default_to(self, default: T) -> T {
32        match self {
33            AssignOnce::Set(value) => value,
34            AssignOnce::Unset => default,
35        }
36    }
37
38    /// Converts this type to `Option`.
39    pub(crate) fn into_option(self) -> Option<T> {
40        match self {
41            AssignOnce::Set(value) => Some(value),
42            AssignOnce::Unset => None,
43        }
44    }
45
46    /// Unwraps `self` or calls `err` if `self` is not set.
47    pub(crate) fn unwrap_or_err<E, F>(self, err: F) -> std::result::Result<T, E>
48    where
49        F: FnOnce() -> E,
50    {
51        match self {
52            AssignOnce::Set(value) => Ok(value),
53            AssignOnce::Unset => Err(err()),
54        }
55    }
56}
57
58/// Utility function to parse `Meta::NameValue` elements that assigns a String.
59pub(crate) fn assign_str_value(
60    to: &mut AssignOnce<String>,
61    attr: &Attribute,
62    key: &str,
63    meta: &meta::ParseNestedMeta<'_>,
64) -> Result<()> {
65    let value = meta.value()?;
66    let value: LitStr = value.parse()?;
67    to.set(value.value(), || {
68        Error::new(
69            attr.span(),
70            format!("parameter `{key}` was already specified."),
71        )
72    })
73}
74
75/// Utility function to parse `Meta::NameValue` elements that assigns a value parsed from a String.
76pub(crate) fn parse_str_value<T>(
77    to: &mut AssignOnce<T>,
78    attr: &Attribute,
79    key: &str,
80    meta: &meta::ParseNestedMeta<'_>,
81) -> Result<()>
82where
83    T: FromStr<Err = String>,
84{
85    let value = meta.value()?;
86    let value: LitStr = value.parse::<LitStr>()?;
87    let value = value
88        .value()
89        .parse()
90        .map_err(|err| Error::new(attr.span(), err))?;
91    to.set(value, || {
92        Error::new(
93            attr.span(),
94            format!("parameter `{key}` was already specified."),
95        )
96    })
97}
98
99/// Utility function to parse `Meta::Word` elements.
100pub(crate) fn assign_path(
101    to: &mut AssignOnce<Path>,
102    attr: &Attribute,
103    key: &str,
104    meta: &meta::ParseNestedMeta<'_>,
105) -> Result<()> {
106    meta.parse_nested_meta(|meta| {
107        to.set(meta.path, || {
108            Error::new(
109                attr.span(),
110                format!("attribute `{key}` was already specified."),
111            )
112        })?;
113        Ok(())
114    })
115}