codama_syn_helpers/extensions/
expr.rs

1use super::ToTokensExtension;
2use syn::{Expr, ExprLit, ExprPath};
3
4pub trait ExprExtension {
5    fn get_self(&self) -> &Expr;
6
7    /// Returns the integer value of the expression if it is a literal integer.
8    fn as_literal_integer<T>(&self) -> syn::Result<T>
9    where
10        T: std::str::FromStr,
11        T::Err: std::fmt::Display,
12    {
13        let this = self.get_self();
14        match this {
15            Expr::Lit(ExprLit {
16                lit: syn::Lit::Int(value),
17                ..
18            }) => value.base10_parse::<T>(),
19            _ => Err(this.error("expected a literal integer")),
20        }
21    }
22
23    /// Returns the string value of the expression if it is a literal string.
24    fn as_literal_string(&self) -> syn::Result<String> {
25        let this = self.get_self();
26        match this {
27            Expr::Lit(ExprLit {
28                lit: syn::Lit::Str(value),
29                ..
30            }) => Ok(value.value()),
31            _ => Err(this.error("expected a literal string")),
32        }
33    }
34
35    /// Returns the boolean value of the expression if it is a literal bool.
36    fn as_literal_bool(&self) -> syn::Result<bool> {
37        let this = self.get_self();
38        match this {
39            Expr::Lit(ExprLit {
40                lit: syn::Lit::Bool(value),
41                ..
42            }) => Ok(value.value()),
43            _ => Err(this.error("expected a literal boolean")),
44        }
45    }
46
47    /// Returns the path of the expression if it is a path.
48    fn as_path(&self) -> syn::Result<&syn::Path> {
49        let this = self.get_self();
50        match this {
51            Expr::Path(ExprPath { path, .. }) => Ok(path),
52            _ => Err(this.error("expected a path")),
53        }
54    }
55}
56
57impl ExprExtension for Expr {
58    fn get_self(&self) -> &Expr {
59        self
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66    use crate::extensions::*;
67
68    #[test]
69    fn as_literal_integer_ok() {
70        let expr: Expr = syn::parse_quote! { 42 };
71        let result = expr.as_literal_integer::<usize>();
72        assert!(matches!(result, Ok(42usize)));
73    }
74
75    #[test]
76    fn as_literal_integer_err() {
77        let expr: Expr = syn::parse_quote! { 40 + 2 };
78        let error = expr.as_literal_integer::<usize>().unwrap_err();
79        assert_eq!(error.to_string(), "expected a literal integer");
80    }
81
82    #[test]
83    fn as_literal_string_ok() {
84        let expr: Expr = syn::parse_quote! { "hello" };
85        let result = expr.as_literal_string().unwrap();
86        assert_eq!(result, "hello");
87    }
88
89    #[test]
90    fn as_literal_string_err() {
91        let expr: Expr = syn::parse_quote! { 40 + 2 };
92        let error = expr.as_literal_string().unwrap_err();
93        assert_eq!(error.to_string(), "expected a literal string");
94    }
95
96    #[test]
97    fn as_path_ok() {
98        let expr: Expr = syn::parse_quote! { hello::world };
99        let result = expr.as_path().unwrap().to_string();
100        assert_eq!(result, "hello::world");
101    }
102
103    #[test]
104    fn as_path_err() {
105        let expr: Expr = syn::parse_quote! { 40 + 2 };
106        let error = expr.as_path().unwrap_err();
107        assert_eq!(error.to_string(), "expected a path");
108    }
109}