oso_cloud/
macros.rs

1/// Convenience macro to construct a `Fact` from
2///  a predicate and a list of arguments.
3///
4/// Arguments can either take "literal" syntax, i.e. `User{"123"}`
5/// or can be Rust literals, i.e. `123`.
6///
7/// ## Example
8///
9/// ```
10/// use oso_cloud::{fact, Fact, Value};
11///
12/// let fact: Fact = fact!("has_role", User { "1" }, "member", Repo { "1" });
13///
14/// assert_eq!(fact.args[0], Value::new("User", "1"));
15/// ```
16#[macro_export]
17macro_rules! fact {
18    ($predicate:expr, $($args:tt)*) => {{
19        let mut args = vec![];
20        $crate::push_values!(args; $($args)+);
21        $crate::Fact {
22            predicate: $predicate.to_owned(),
23            args,
24        }
25    }};
26}
27
28#[macro_export]
29macro_rules! value {
30    ($lit:literal) => {
31        $crate::Value::from($lit)
32    };
33    ($type_:ident { $id:expr }) => {
34        $crate::Value::new(stringify!($type_), $id)
35    };
36    ($val:expr) => {
37        $crate::Value::from($val)
38    };
39}
40
41#[macro_export]
42macro_rules! push_values {
43    ($args:expr; $str:literal) => {{
44        $args.push($crate::value!($str));
45    }};
46    ($args:expr; $type_:ident { $id:expr }) => {{
47        $args.push($crate::value!($type_ { $id }));
48    }};
49
50    ($args:expr; $str:literal, $($rest:tt)*) => {{
51        $args.push($crate::value!($str));
52        $crate::push_values!($args; $($rest)*);
53    }};
54    ($args:expr; $type_:ident { $id:expr }, $($rest:tt)*) => {{
55        $args.push($crate::value!($type_ { $id }));
56        $crate::push_values!($args; $($rest)*);
57    }};
58    ($args:expr; $val:expr) => {{
59        $args.push($crate::value!($val));
60    }};
61    ($args:expr; $val:expr, $($rest:tt)*) => {{
62        $args.push($crate::value!($val));
63        $crate::push_values!($args; $($rest)*);
64    }};
65}
66
67#[cfg(test)]
68mod test {
69    use crate::{
70        polar_ast::{PRIMITIVE_BOOLEAN_SYMBOL, PRIMITIVE_INTEGER_SYMBOL, PRIMITIVE_STRING_SYMBOL},
71        Fact, Value,
72    };
73
74    #[test]
75    fn test_macro_expansion() {
76        let bool_val = value!(true);
77        assert_eq!(bool_val, Value::new(PRIMITIVE_BOOLEAN_SYMBOL, "true"));
78
79        let string_val = value!("foo");
80        assert_eq!(string_val, Value::new(PRIMITIVE_STRING_SYMBOL, "foo"));
81
82        let int_val = value!(42);
83        assert_eq!(int_val, Value::new(PRIMITIVE_INTEGER_SYMBOL, "42"));
84
85        let literal_val = value!(Foo { "bar" });
86        assert_eq!(literal_val, Value::new("Foo", "bar"));
87
88        let fact = fact!("foo", "bar", 42, true, Foo { "bar" });
89        assert_eq!(
90            fact,
91            Fact {
92                predicate: "foo".to_string(),
93                args: vec![
94                    Value::new(PRIMITIVE_STRING_SYMBOL, "bar"),
95                    Value::new(PRIMITIVE_INTEGER_SYMBOL, "42"),
96                    Value::new(PRIMITIVE_BOOLEAN_SYMBOL, "true"),
97                    Value::new("Foo", "bar"),
98                ],
99            }
100        );
101    }
102}