mdnt_support/
error.rs

1//! Error type for the support crate.
2
3use std::{num::ParseIntError, str::ParseBoolError, sync::Arc};
4
5use num_bigint::{BigInt, ParseBigIntError, TryFromBigIntError};
6use thiserror::Error;
7
8/// Error type.
9#[derive(Error, Debug)]
10pub enum Error {
11    /// Parsing error while loading a field element from a string.
12    #[error("Failure while parsing field element")]
13    FieldParsingError,
14    /// The circuit requested more constants than provided.
15    #[error("Not enough constants")]
16    NotEnoughConstants,
17    /// The circuit did not declare enough cells for input or output.
18    #[error("IO cell iterator was exhausted")]
19    NotEnoughIOCells,
20    /// Integer parse error.
21    #[error("Parse failure")]
22    IntParse(#[from] ParseIntError),
23    /// Boolean parse error.
24    #[error("Parse failure")]
25    BoolParse(#[from] ParseBoolError),
26    /// BigUint parse error.
27    #[error("Parse failure")]
28    BigUintParse(#[from] ParseBigIntError),
29    /// Plonk synthesis error.
30    #[error("Synthesis error")]
31    Plonk(Arc<dyn std::error::Error>),
32    /// An error represented with an static string.
33    #[error("Error")]
34    StrError(&'static str),
35    /// Int cast error.
36    #[error(transparent)]
37    IntCast(#[from] std::num::TryFromIntError),
38    /// Big int cast error.
39    #[error(transparent)]
40    BigIntCast(#[from] TryFromBigIntError<BigInt>),
41    /// Error when an encountered an unexpected number of elements.
42    #[error("{header}Was expecting {expected} elements but got {actual}")]
43    UnexpectedElements {
44        /// Context header for the error
45        header: String,
46        /// The expected number of elements.
47        expected: usize,
48        /// The number of elements.
49        actual: usize,
50    },
51}
52
53impl From<&'static str> for Error {
54    fn from(value: &'static str) -> Self {
55        Self::StrError(value)
56    }
57}
58
59unsafe impl Send for Error {}
60unsafe impl Sync for Error {}
61
62/// Macro for creating [`Error::UnexpectedElements`] errors.
63///
64/// The macro accepts a comparison expression between two values (expected and actual) and an
65/// optional message. The message itself can accept formatting arguments.
66/// It will return an [`Err`] with an [`Error::UnexpectedElements`] error so the
67/// caller of the macro should have a return type of `Result<_, E>` s.t. `E` implements [`From`] for the [`Error`] type.
68///
69/// Because of how the macro is implemented, when passign a custom message is necessary to surround the comparison with parenthesis.
70///
71/// # Examples
72///
73/// ```no_run
74/// use mdnt_support::error::Error;
75/// use mdnt_support::expect_elements;
76///
77/// fn foo(c: usize) -> Result<(), Error> {
78///     let a = 6;
79///     let b = 7;
80///     // Default message
81///     expect_elements!(a == b);
82///     // With a custom message
83///     expect_elements!((a <= b), "During call to foo()");
84///     // With a custom formatted message
85///     expect_elements!((a > c), "During call to foo({c})");
86///     Ok(())
87/// }
88/// ```
89#[macro_export]
90macro_rules! expect_elements {
91    // With custom message
92    (($($tokens:tt)+) , $($fmt:tt)+) => {
93        $crate::__expect_elements_parse! { [] [$($tokens)+] format!($($fmt)+) }
94    };
95
96    // Without message → default
97    ($($tokens:tt)+) => {
98        $crate::__expect_elements_parse! { [] [$($tokens)+] String::new() }
99    };
100}
101
102//
103// TT MUNCHER: split into lhs / op / rhs
104//
105#[macro_export]
106#[doc(hidden)]
107macro_rules! __expect_elements_parse {
108    // ==
109    ([$($lhs:tt)+] [== $($rhs:tt)+] $msg:expr) => {
110        $crate::__expect_elements_finish! {
111            ($($lhs)*)
112            (==)
113            ($($rhs)*)
114            ($msg)
115        }
116    };
117
118    // !=
119    ([$($lhs:tt)+] [!= $($rhs:tt)+] $msg:expr) => {
120        $crate::__expect_elements_finish! {
121            ($($lhs)*)
122            (!=)
123            ($($rhs)*)
124            ($msg)
125        }
126    };
127
128    // <
129    ([$($lhs:tt)+] [< $($rhs:tt)+] $msg:expr) => {
130        $crate::__expect_elements_finish! {
131            ($($lhs)*)
132            (<)
133            ($($rhs)*)
134            ($msg)
135        }
136    };
137
138    // <=
139    ([$($lhs:tt)+] [<= $($rhs:tt)+] $msg:expr) => {
140        $crate::__expect_elements_finish! {
141            ($($lhs)*)
142            (<=)
143            ($($rhs)*)
144            ($msg)
145        }
146    };
147
148    // >
149    ([$($lhs:tt)+] [> $($rhs:tt)+] $msg:expr) => {
150        $crate::__expect_elements_finish! {
151            ($($lhs)*)
152            (>)
153            ($($rhs)*)
154            ($msg)
155        }
156    };
157
158    // >=
159    ([$($lhs:tt)+] [>= $($rhs:tt)+] $msg:expr) => {
160        $crate::__expect_elements_finish! {
161            ($($lhs)*)
162            (>=)
163            ($($rhs)*)
164            ($msg)
165        }
166    };
167
168    // Keep munching
169    ([$($lhs:tt)*] [$next:tt $($rest:tt)*] $msg:expr) => {
170        $crate::__expect_elements_parse! { [$($lhs)* $next] [$($rest)*] $msg }
171    };
172
173    // No operator -> error
174    ([$($lhs:tt)*] [] $msg:expr) => {
175        compile_error!("expected a comparison expression such as `lhs == rhs`");
176    };
177}
178
179//
180// Final step: evaluate lhs, rhs, and return Err(A::B)
181//
182#[doc(hidden)]
183#[macro_export]
184macro_rules! __expect_elements_finish {
185    // ==
186    (($($lhs:tt)+) (==) ($($rhs:tt)+) ($msg:expr)) => {{
187        let lhs_val: usize = $($lhs)*;
188        let rhs_val: usize = $($rhs)*;
189        $crate::error::__expect_elements_impl($msg, lhs_val, rhs_val, lhs_val == rhs_val)?;
190    }};
191
192    // !=
193    (($($lhs:tt)+) (!=) ($($rhs:tt)+) ($msg:expr)) => {{
194        let lhs_val: usize = $($lhs)*;
195        let rhs_val: usize = $($rhs)*;
196        $crate::error::__expect_elements_impl($msg, lhs_val, rhs_val, lhs_val != rhs_val)?;
197    }};
198
199    // <
200    (($($lhs:tt)+) (<) ($($rhs:tt)+) ($msg:expr)) => {{
201        let lhs_val: usize = $($lhs)*;
202        let rhs_val: usize = $($rhs)*;
203        $crate::error::__expect_elements_impl($msg, lhs_val, rhs_val, lhs_val < rhs_val)?;
204    }};
205
206    // <=
207    (($($lhs:tt)+) (<=) ($($rhs:tt)+) ($msg:expr)) => {{
208        let lhs_val: usize = $($lhs)*;
209        let rhs_val: usize = $($rhs)*;
210        $crate::error::__expect_elements_impl($msg, lhs_val, rhs_val, lhs_val <= rhs_val)?;
211    }};
212
213    // >
214    (($($lhs:tt)+) (>) ($($rhs:tt)+) ($msg:expr)) => {{
215        let lhs_val: usize = $($lhs)*;
216        let rhs_val: usize = $($rhs)*;
217        $crate::error::__expect_elements_impl($msg, lhs_val, rhs_val, lhs_val > rhs_val)?;
218    }};
219
220    // >=
221    (($($lhs:tt)+) (>=) ($($rhs:tt)+) ($msg:expr)) => {{
222        let lhs_val: usize = $($lhs)*;
223        let rhs_val: usize = $($rhs)*;
224        $crate::error::__expect_elements_impl($msg, lhs_val, rhs_val, lhs_val >= rhs_val)?;
225    }};
226}
227
228#[doc(hidden)]
229#[inline]
230pub fn __expect_elements_impl(
231    header: String,
232    expected: usize,
233    actual: usize,
234    passed: bool,
235) -> Result<(), Error> {
236    if passed {
237        Ok(())
238    } else {
239        Err(Error::UnexpectedElements {
240            header,
241            expected,
242            actual,
243        })
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250    use rstest::rstest;
251
252    #[derive(Debug)]
253    enum Cmp {
254        Eq,
255        Ne,
256        Lt,
257        Le,
258        Gt,
259        Ge,
260    }
261
262    #[rstest]
263    #[case(Cmp::Eq, 1, 1)]
264    #[case(Cmp::Ne, 1, 2)]
265    #[case(Cmp::Lt, 1, 2)]
266    #[case(Cmp::Le, 1, 1)]
267    #[case(Cmp::Gt, 2, 1)]
268    #[case(Cmp::Ge, 1, 1)]
269    #[should_panic(expected = "unexpected elements error")]
270    #[case(Cmp::Eq, 1, 2)]
271    #[should_panic(expected = "unexpected elements error")]
272    #[case(Cmp::Ne, 1, 1)]
273    #[should_panic(expected = "unexpected elements error")]
274    #[case(Cmp::Lt, 1, 0)]
275    #[should_panic(expected = "unexpected elements error")]
276    #[case(Cmp::Le, 1, 0)]
277    #[should_panic(expected = "unexpected elements error")]
278    #[case(Cmp::Gt, 2, 3)]
279    #[should_panic(expected = "unexpected elements error")]
280    #[case(Cmp::Ge, 1, 3)]
281    fn expect_elements_test(#[case] cmp: Cmp, #[case] expected: usize, #[case] actual: usize) {
282        fn do_test(cmp: Cmp, expected: usize, actual: usize) -> Result<(), Error> {
283            match cmp {
284                Cmp::Eq => expect_elements!((expected == actual), "unexpected elements error"),
285                Cmp::Ne => expect_elements!((expected != actual), "unexpected elements error"),
286                Cmp::Lt => expect_elements!((expected < actual), "unexpected elements error"),
287                Cmp::Le => expect_elements!((expected <= actual), "unexpected elements error"),
288                Cmp::Gt => expect_elements!((expected > actual), "unexpected elements error"),
289                Cmp::Ge => expect_elements!((expected >= actual), "unexpected elements error"),
290            }
291            Ok(())
292        }
293        eprintln!("cmp = {cmp:?}, expected = {expected}, actual = {actual}");
294        do_test(cmp, expected, actual).unwrap();
295    }
296
297    #[rstest]
298    #[should_panic(expected = "unexpected elements error")]
299    #[case(Cmp::Eq)]
300    #[should_panic(expected = "unexpected elements error")]
301    #[case(Cmp::Ne)]
302    #[should_panic(expected = "unexpected elements error")]
303    #[case(Cmp::Lt)]
304    #[should_panic(expected = "unexpected elements error")]
305    #[case(Cmp::Le)]
306    #[should_panic(expected = "unexpected elements error")]
307    #[case(Cmp::Gt)]
308    #[should_panic(expected = "unexpected elements error")]
309    #[case(Cmp::Ge)]
310    fn expect_elements_complex_expr_rhs(#[case] cmp: Cmp) {
311        fn do_test(cmp: Cmp) -> Result<(), Error> {
312            let v = vec![1, 2, 3];
313            match cmp {
314                Cmp::Eq => expect_elements!((2 == v.len()), "unexpected elements error"),
315                Cmp::Ne => expect_elements!((3 != v.len()), "unexpected elements error"),
316                Cmp::Lt => expect_elements!((4 < v.len()), "unexpected elements error"),
317                Cmp::Le => expect_elements!((4 <= v.len()), "unexpected elements error"),
318                Cmp::Gt => expect_elements!((2 > v.len()), "unexpected elements error"),
319                Cmp::Ge => expect_elements!((2 >= v.len()), "unexpected elements error"),
320            };
321            Ok(())
322        }
323        do_test(cmp).unwrap();
324    }
325
326    #[rstest]
327    #[should_panic(expected = "unexpected elements error")]
328    #[case(Cmp::Eq)]
329    #[should_panic(expected = "unexpected elements error")]
330    #[case(Cmp::Ne)]
331    #[should_panic(expected = "unexpected elements error")]
332    #[case(Cmp::Lt)]
333    #[should_panic(expected = "unexpected elements error")]
334    #[case(Cmp::Le)]
335    #[should_panic(expected = "unexpected elements error")]
336    #[case(Cmp::Gt)]
337    #[should_panic(expected = "unexpected elements error")]
338    #[case(Cmp::Ge)]
339    fn expect_elements_complex_expr_lhs(#[case] cmp: Cmp) {
340        fn do_test(cmp: Cmp) -> Result<(), Error> {
341            let v = vec![1, 2, 3];
342            match cmp {
343                Cmp::Eq => expect_elements!((v.len() == 2), "unexpected elements error"),
344                Cmp::Ne => expect_elements!((v.len() != 3), "unexpected elements error"),
345                Cmp::Lt => expect_elements!((v.len() < 2), "unexpected elements error"),
346                Cmp::Le => expect_elements!((v.len() <= 2), "unexpected elements error"),
347                Cmp::Gt => expect_elements!((v.len() > 4), "unexpected elements error"),
348                Cmp::Ge => expect_elements!((v.len() >= 4), "unexpected elements error"),
349            };
350            Ok(())
351        }
352        do_test(cmp).unwrap();
353    }
354}