armour/
lib.rs

1#![warn(clippy::unwrap_used)]
2#![cfg_attr(not(feature = "std"), no_std)]
3#[cfg_attr(not(feature = "std"), macro_use)]
4#[cfg(not(feature = "std"))]
5extern crate alloc;
6
7pub use armour_derive::*;
8pub use async_broadcast::RecvError;
9pub use cid::{Cid, KeyScheme, KeyType, key_size};
10pub use database::{db::Database, raw_tree::RawTree, tree::CollectionTree};
11pub use dyn_types::{Typ, get_type::GetType, value::Value};
12pub use error::{DbError, DbResult};
13#[cfg(feature = "fjall")]
14pub use fjall::{self, Slice};
15pub use indexes::index::{CompositionIndex, HashIndexExtractor};
16pub use rapira;
17pub use record::{Migration, Record};
18#[cfg(feature = "sled")]
19pub use sled::{self, InlineArray};
20pub use types::{entry::Entry, fuid::Fuid, id64::Id64, record_status::RecordStatus};
21#[cfg(all(feature = "sled", feature = "id32"))]
22pub use types::{ident::ID, low_id::LowId, uid::Uid};
23
24#[cfg(feature = "bytemuck")]
25pub use crate::database::byte_as_slice::BytesAsValSlice;
26
27#[cfg(feature = "canopydb")]
28pub mod canopydb;
29pub mod cid;
30pub mod database;
31pub mod dyn_types;
32pub mod error;
33pub mod indexes;
34#[cfg(feature = "fjall")]
35pub mod logdb;
36pub mod record;
37pub mod types;
38pub mod utils;
39
40pub mod migrations {
41    pub use crate::record::{MigrationRes, MigrationType};
42}
43
44/// key should be Base64Url decoded 56 bytes long (~ 75 chars)
45///
46/// `const_hasher!(module_name, "ENV_KEY_NAME");`
47///
48/// env key concatenated with "APP_ID_KEY_" will be used to get the key
49#[macro_export]
50macro_rules! const_hasher {
51    ($m_name: ident, $key_name: literal) => {
52        pub mod $m_name {
53            use $crate::types::enc::{Cipher, IdHasher};
54
55            #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
56            pub struct Hasher;
57
58            impl IdHasher for Hasher {
59                const HASHER: Cipher = Cipher::new(env!(concat!("APP_ID_KEY_", $key_name)));
60            }
61        }
62    };
63}
64
65/// key should be Base64Url decoded 56 bytes long (~ 75 chars)
66///
67/// `hasher!(module_name, "hash_key");`
68#[macro_export]
69macro_rules! hasher {
70    ($m_name: ident, $key: literal) => {
71        pub mod $m_name {
72            use $crate::types::enc::{Cipher, IdHasher};
73
74            #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
75            pub struct Hasher;
76
77            impl IdHasher for Hasher {
78                const HASHER: Cipher = Cipher::new($key);
79            }
80        }
81    };
82}
83
84/// rapira_record!(User, UserID, sled::InlineArray, "users")
85#[macro_export]
86macro_rules! rapira_record {
87    ($ty: ty, $id_ty: ty, $value_ty: ty, $name: literal) => {
88        impl $crate::Record for $ty {
89            type SelfId = $id_ty;
90            type Value = $value_ty;
91            const NAME: &'static str = $name;
92            fn deser(bytes: &Self::Value) -> Self {
93                rapira::deserialize(bytes.as_ref()).unwrap()
94            }
95            fn ser(&self) -> $value_ty {
96                rapira::serialize(self).into()
97            }
98        }
99    };
100}
101
102/// zerocopy_record!(User, UserID, sled::InlineArray, "users")
103#[macro_export]
104macro_rules! zerocopy_record {
105    ($ty: ty, $id_ty: ty, $value_ty: ty, $name: literal) => {
106        const SIZE: usize = std::mem::size_of::<$ty>();
107        impl $crate::Record for $ty {
108            type SelfId = $id_ty;
109            type Value = $value_ty;
110            const NAME: &'static str = $name;
111            fn deser(bytes: &Self::Value) -> Self {
112                let bytes: &[u8] = bytes.as_ref();
113                let bytes: [u8; SIZE] = bytes.try_into().unwrap();
114                zerocopy::transmute!(bytes)
115            }
116            fn ser(&self) -> $value_ty {
117                let bytes: &[u8; SIZE] = zerocopy::transmute_ref!(self);
118                bytes.into()
119            }
120        }
121    };
122}
123
124/// bytemuck_record!(User, UserID, sled::InlineArray, "users")
125#[macro_export]
126macro_rules! bytemuck_slice_record {
127    ($ty: ty, $id_ty: ty, $value_ty: ty, $name: literal) => {
128        impl $crate::Record for $ty {
129            type SelfId = $id_ty;
130            type Value = $value_ty;
131            const NAME: &'static str = $name;
132            fn deser(bytes: &Self::Value) -> Self {
133                if bytes.is_empty() {
134                    return Default::default();
135                }
136                let vec = bytes.as_ref().to_vec();
137                let vec: Vec<ProfileId> = bytemuck::cast_vec(vec);
138                Self(vec)
139            }
140            fn ser(&self) -> $value_ty {
141                let bytes: &[u8] = bytemuck::cast_slice(self);
142                bytes.to_vec().into()
143            }
144        }
145    };
146}
147
148/// `zerocopy_cid!(Key, [KeyType::U64...], Type::GROUP_BITS, field_name)`
149#[macro_export]
150macro_rules! zerocopy_cid {
151    ($ty: ty, [$($key_type:expr),*], $group_bits:expr, $field:ident) => {
152        impl $crate::Cid for $ty {
153            type B = [u8; size_of::<Self>()];
154            const TY: $crate::KeyScheme = $crate::KeyScheme::Typed(&[$($key_type),*]);
155            const GROUP_BITS: u32 = $group_bits;
156            fn encode(&self) -> Self::B {
157                zerocopy::transmute!(*self)
158            }
159            fn decode(bytes: &Self::B) -> armour::types::Result<Self> {
160                Ok(zerocopy::transmute!(*bytes))
161            }
162            fn group_id(&self) -> u32 {
163                self.$field.group_id()
164            }
165        }
166    };
167}
168
169#[cfg(test)]
170mod tests {
171    use rapira::Rapira;
172
173    use super::*;
174    use crate::types::id64::Id64;
175
176    #[derive(Clone, Rapira)]
177    struct User {
178        name: String,
179    }
180
181    impl GetType for User {
182        const TYPE: Typ = Typ::Custom("unimplemented", &[]);
183    }
184
185    const_hasher!(test_key, "TEST");
186    type UserID = Id64<test_key::Hasher>;
187    rapira_record!(User, UserID, Vec<u8>, "users");
188
189    #[test]
190    fn test_rapira_record() {
191        let _user = User {
192            name: "John Doe".to_string(),
193        };
194    }
195}