Skip to main content

amaru_kernel/
macros.rs

1// Copyright 2025 PRAGMA
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15/// Generate a roundtrip property to assert that Cbor encoder and decoder for a given type can
16/// safely be called in sequence and yield the original input.
17///
18/// Requires:
19/// - proptest
20///
21/// Usage:
22///
23/// # ```
24/// # prop_cbor_roundtrip!(MyType, my_strategy())
25/// #
26/// # // Or with an explicit test title in case a module contains multiple calls to the macro:
27/// # prop_cbor_roundtrip!(prop_cbor_roundtrip_MyType, MyType, my_strategy())
28/// # ```
29#[macro_export]
30macro_rules! prop_cbor_roundtrip {
31    ($title:ident, $ty:ty, $strategy:expr) => {
32        proptest::proptest! {
33            #[test]
34            fn $title(val in $strategy) {
35                let bytes = $crate::to_cbor(&val);
36                let decoded = $crate::from_cbor_no_leftovers::<$ty>(&bytes).map_err(|e| e.to_string());
37                proptest::prop_assert_eq!(Ok(val), decoded, "bytes: {}", hex::encode(&bytes));
38            }
39        }
40    };
41
42    ($ty:ty, $strategy:expr) => {
43        prop_cbor_roundtrip!(prop_cbor_roundtrip, $ty, $strategy);
44    };
45}
46
47/// Easily create a hash from a hex-encoded literal string. Useful for testing.
48///
49/// Requires:
50/// - hex
51///
52/// Usage:
53///
54/// # ```
55/// # let my_hash32: Hash<32> = hash!("a7c4477e9fcfd519bf7dcba0d4ffe35a399125534bc8c60fa89ff6b50a060a7a"),
56/// # let my_hash28: Hash<28> = hash!("a7c4477e9fcfd519bf7dcba0d4ffe35a399125534bc8c60fa89ff6b5"),
57/// # ```
58#[macro_export]
59macro_rules! hash {
60    ($str:literal $(,)?) => {{
61        // Raise a compile-time error if the literal string is looking dubious
62        const _ASSERT_IS_HEX: () = {
63            let bytes = $str.as_bytes();
64            let mut i = 0;
65            while i < bytes.len() {
66                match bytes[i] {
67                    b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F' => {}
68                    _ => panic!("not a valid hex literal"),
69                }
70                i += 1;
71            }
72            if i != 64 && i != 56 {
73                panic!("invalid hash literal length");
74            }
75        };
76        $crate::Hash::from(hex::decode($str).unwrap().as_slice())
77    }};
78}
79
80/// Like 'include_cbor!', but do not panic when failing to decode. Return a Result instead.
81#[macro_export]
82macro_rules! try_include_cbor {
83    ($filepath:expr) => {
84        $crate::cbor::decode(include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/data/", $filepath,)))
85    };
86}
87
88/// Includes and deserialize a CBOR-encoded test data file. The file must located under the
89/// project's tests/data folder, relative to the project's Cargo.toml.
90#[macro_export]
91macro_rules! include_cbor {
92    ($filepath:expr) => {
93        $crate::try_include_cbor!($filepath).expect(concat!("invalid cbor file: ", $filepath))
94    };
95}
96
97/// Includes and deserialize a JSON-encoded test data file. The file must located under the
98/// project's tests/data folder, relative to the project's Cargo.toml.
99#[macro_export]
100macro_rules! include_json {
101    ($filepath:expr) => {{
102        $crate::json::from_str(include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/data/", $filepath,)))
103            .expect(concat!("invalid json file: ", $filepath))
104    }};
105}