helix_db/helix_engine/
macros.rs

1pub mod macros {
2    #[macro_export]
3    /// Creates array of pairs which each represent the property key and corresponding value.
4    /// If a value is None, it will be excluded from the final vector.
5    /// The vector is preallocated with capacity for all potential items.
6    ///
7    /// ## Example Use
8    /// ```rust
9    /// use helix_db::optional_props;
10    /// use helix_db::protocol::value::Value;
11    ///
12    /// let properties: Vec<(String, Value)> = optional_props! {
13    ///     "name" => Some("Will"),
14    ///     "age" => Some(21),
15    ///     "title" => None::<String>,
16    /// };
17    ///
18    /// assert_eq!(properties.len(), 2); // "title" is excluded
19    /// ```
20    macro_rules! optional_props {
21    () => {
22        vec![]
23    };
24    ($($key:expr => $value:expr),* $(,)?) => {{
25        let mut vec = Vec::with_capacity($crate::count!($($key),*));
26        $(
27            if let Some(value) = $value {
28                vec.push((String::from($key), value.into()));
29            }
30        )*
31        vec
32    }};
33}
34
35    // Helper macro to count the number of expressions
36    #[macro_export]
37    #[doc(hidden)]
38    macro_rules! count {
39    () => (0);
40    ($head:expr $(, $tail:expr)*) => (1 + $crate::count!($($tail),*));
41    }
42
43    #[macro_export]
44    /// Creates array of pairs which each represent the property key and corresponding value.
45    ///
46    /// ## Example Use
47    /// ```rust
48    /// use helix_db::props;
49    /// use helix_db::protocol::value::Value;
50    ///
51    /// let properties: Vec<(String, Value)> = props! {
52    ///     "name" => "Will",
53    ///     "age" => 21,
54    /// };
55    ///
56    /// assert_eq!(properties.len(), 2);
57    macro_rules! props {
58    () => {
59    vec![]
60    };
61    ($($key:expr => $value:expr),* $(,)?) => {
62        vec![
63            $(
64            (String::from($key), $value.into()),
65            )*
66        ]
67    };
68 }
69
70    #[macro_export]
71    /// Creates a closeure that takes a node and checks a property of the node against a value.
72    /// The closure returns true if the property matches the value, otherwise false.
73    ///
74    /// ## Example Use
75    ///
76    /// ```rust
77    /// use helix_db::node_matches;
78    /// use helix_db::protocol::value::Value;
79    /// use helix_db::protocol::items::Node;
80    /// use helix_db::protocol::filterable::Filterable;
81    /// let pred = node_matches!("name", "Will");
82    ///
83    /// let node = Node::new("person", vec![
84    ///    ("name".to_string(), Value::String("Will".to_string())),
85    ///   ("age".to_string(), Value::Integer(21)),
86    /// ]);
87    ///
88    ///
89    /// assert_eq!(pred(&node).unwrap(), true);
90    /// ```
91    macro_rules! node_matches {
92        ($key:expr, $value:expr) => {
93            |node: &helix_db::protocol::items::Node| {
94                if let Some(val) = node.check_property($key) {
95                    if let helix_db::protocol::value::Value::String(val) = &val {
96                        Ok(*val == $value)
97                    } else {
98                        Err(helix_db::helix_engine::types::GraphError::from(
99                            "Invalid node".to_string(),
100                        ))
101                    }
102                } else {
103                    Err(helix_db::helix_engine::types::GraphError::from(
104                        "Invalid node".to_string(),
105                    ))
106                }
107            }
108        };
109    }
110
111    #[macro_export]
112    macro_rules! edge_matches {
113        ($key:expr, $value:expr) => {
114            |edge: &helix_db::protocol::items::Edge| {
115                if let Some(val) = edge.check_property($key) {
116                    if let helix_db::protocol::value::Value::String(val) = &val {
117                        Ok(*val == $value)
118                    } else {
119                        Err(helix_db::helix_engine::types::GraphError::from(
120                            "Invalid edge".to_string(),
121                        ))
122                    }
123                } else {
124                    Err(helix_db::helix_engine::types::GraphError::from(
125                        "Invalid edge".to_string(),
126                    ))
127                }
128            }
129        };
130    }
131
132    #[macro_export]
133    macro_rules! field_remapping {
134        ($remapping_vals:expr, $var_name:expr, $old_name:expr => $new_name:expr) => {{
135            let old_value = match $var_name.check_property($old_name) {
136                Ok(val) => val,
137                Err(e) => {
138                    return Err(GraphError::ConversionError(format!(
139                        "Error Decoding: {:?}",
140                        "Invalid node".to_string()
141                    )))
142                }
143            };
144            let old_value_remapping =
145                Remapping::new(false, Some($new_name), Some(ReturnValue::from(old_value)));
146            $remapping_vals.insert(
147                $var_name.id(),
148                ResponseRemapping::new(
149                    HashMap::from([($old_name.to_string(), old_value_remapping)]),
150                    false,
151                ),
152            );
153            Ok::<TraversalVal, GraphError>($var_name) // Return the Ok value
154        }};
155    }
156
157    #[macro_export]
158    macro_rules! traversal_remapping {
159        ($remapping_vals:expr, $var_name:expr, $new_name:expr => $traversal:expr) => {{
160            // TODO: ref?
161            let new_remapping = Remapping::new(
162                false,
163                Some($new_name.to_string()),
164                Some(ReturnValue::from($traversal)),
165            );
166            $remapping_vals.insert(
167                $var_name.id(),
168                ResponseRemapping::new(
169                    HashMap::from([($new_name.to_string(), new_remapping)]),
170                    false,
171                ),
172            );
173            Ok::<TraversalVal, GraphError>($var_name)
174        }};
175    }
176
177    #[macro_export]
178    macro_rules! exclude_field {
179        ($remapping_vals:expr, $var_name:expr, $($field_to_exclude:expr),* $(,)?) => {{
180
181                    $(
182                    let field_to_exclude_remapping = Remapping::new(
183                        true,
184                        Some($field_to_exclude.to_string()),
185                        None,
186                    );
187                    $remapping_vals.insert(
188                        $var_name.id(),
189                        ResponseRemapping::new(
190                            HashMap::from([($field_to_exclude.to_string(), field_to_exclude_remapping)]),
191                            false,
192                        ),
193                    );
194                    )*
195                Ok::<TraversalVal, GraphError>($var_name)
196        }};
197    }
198
199    #[macro_export]
200    macro_rules! identifier_remapping {
201        ($remapping_vals:expr, $var_name:expr, $field_name:expr =>  $identifier_value:expr) => {{
202            let value = match $var_name.check_property($field_name) {
203                Ok(val) => val.clone(), // TODO: try and remove clone
204                Err(e) => {
205                    return Err(GraphError::ConversionError(format!(
206                        "Error Decoding: {:?}",
207                        "Invalid node".to_string()
208                    )))
209                }
210            };
211            let value_remapping = Remapping::new(
212                false,
213                Some($identifier_value.to_string()),
214                Some(ReturnValue::from(value)),
215            );
216            $remapping_vals.insert(
217                $var_name.id(),
218                ResponseRemapping::new(
219                    HashMap::from([($field_name.to_string(), value_remapping)]),
220                    false,
221                ),
222            );
223            Ok::<TraversalVal, GraphError>($var_name)
224        }};
225    }
226
227    #[macro_export]
228    macro_rules! value_remapping {
229        ($remapping_vals:expr, $var_name:expr, $field_name:expr =>  $value:expr) => {{
230            let value = match $var_name.check_property($field_name) {
231                Ok(val) => val.clone(),
232                Err(e) => {
233                    return Err(GraphError::ConversionError(format!(
234                        "Error Decoding: {:?}",
235                        "Invalid node".to_string()
236                    )))
237                }
238            };
239            let old_value_remapping = Remapping::new(
240                false,
241                Some($field_name.to_string()),
242                Some(ReturnValue::from(value)),
243            );
244            $remapping_vals.insert(
245                $var_name.id(),
246                ResponseRemapping::new(
247                    HashMap::from([($field_name.to_string(), old_value_remapping)]),
248                    false,
249                ),
250            );
251            Ok::<TraversalVal, GraphError>($var_name) // Return the Ok value
252        }};
253    }
254
255    #[macro_export]
256    /// simply just a debug logging function
257    macro_rules! debug_println {
258        ($($arg:tt)*) => {
259            #[cfg(feature = "debug-output")]
260            {
261                let caller = std::any::type_name_of_val(&|| {});
262                let caller = caller.strip_suffix("::{{closure}}").unwrap_or(caller);
263                println!("{}:{} =>\n\t{}", caller, line!(), format_args!($($arg)*));
264            }
265        };
266    }
267}
268