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