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}