#![warn(clippy::unwrap_used)]

pub mod enc;
pub mod error;
pub mod fuid;
pub mod id64;
pub mod key_part;
pub mod key_type;
pub mod num_ops;
pub mod record_status;
pub mod zbase;

#[cfg(feature = "std")]
pub mod persist;

#[cfg(feature = "deserialize")]
pub mod deserialize;

#[cfg(feature = "dyn-value")]
pub mod dyn_value;

use arrayvec::ArrayString;

pub use armour_derive::*;
pub use armour_typ::{
    CollectionKind, CollectionNode, EnumType, FieldRef, Fields, GetRefs, GetType, IdBrand,
    OnMissing, PathSeg, RelationEdge, RelationKind, ScalarTyp, SchemaFields, SchemaGraph,
    SchemaTyp, SimpleEnumType, StructType, Typ,
};
pub use error::ArmourError;
pub use fuid::{Fuid, OptFuid};
pub use id64::{Id64, OptId64};
pub use key_type::{KeyScheme, KeyType};

/// Compatibility module for armour-derive generated code paths.
pub mod dyn_types {
    pub use armour_typ::get_type;
    pub use armour_typ::{EnumType, Fields, SimpleEnumType, StructType, Typ};
}

pub type IdStr = ArrayString<13>;

pub type Result<T, E = ArmourError> = core::result::Result<T, E>;

pub const ID_ENC_FLAG: u64 = 1u64 << 8;

/// key should be Base64Url decoded 56 bytes long (~ 75 chars)
///
/// `const_hasher!(module_name, "KEY_ENV_NAME");`
///
/// env name concatenated with "APP_ID_KEY_" will be used to get the key
#[macro_export]
macro_rules! const_hasher {
    ($m_name: ident, $key_name: literal) => {
        pub mod $m_name {
            use $crate::enc::{Cipher, IdHasher};

            #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
            pub struct Hasher;

            impl IdHasher for Hasher {
                const HASHER: Cipher = Cipher::new(env!(concat!("APP_ID_KEY_", $key_name)));
            }

            impl $crate::IdBrand for Hasher {
                const BRAND: &'static str = $key_name;
            }
        }
    };
}

/// Like `const_hasher!`, but in debug builds falls back to `$default` if the env var is not set.
///
/// `const_hasher_or!(module_name, "KEY_ENV_NAME", "default_base64url_key");`
///
/// env name concatenated with "APP_ID_KEY_" will be used to get the key
#[macro_export]
macro_rules! const_hasher_or {
    ($m_name:ident, $key_name:literal, $default:literal) => {
        pub mod $m_name {
            use $crate::enc::{Cipher, IdHasher};

            #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
            pub struct Hasher;

            impl IdHasher for Hasher {
                const HASHER: Cipher = Cipher::new({
                    #[cfg(debug_assertions)]
                    {
                        match option_env!(concat!("APP_ID_KEY_", $key_name)) {
                            Some(v) => v,
                            None => $default,
                        }
                    }
                    #[cfg(not(debug_assertions))]
                    {
                        env!(concat!("APP_ID_KEY_", $key_name))
                    }
                });
            }

            impl $crate::IdBrand for Hasher {
                const BRAND: &'static str = $key_name;
            }
        }
    };
}

/// key should be Base64Url decoded 56 bytes long (~ 75 chars)
///
/// `hasher!(module_name, "_mKbKGF2IrkGvIJvl97HuCgWjgt6QRZ7Ye8DHBQ2anvyi18BdMz8uN6Ej3YJApooY6qDu0obqq4");`
#[macro_export]
macro_rules! hasher {
    ($m_name: ident, $key: literal) => {
        pub mod $m_name {
            use $crate::enc::{Cipher, IdHasher};

            #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
            pub struct Hasher;

            impl IdHasher for Hasher {
                const HASHER: Cipher = Cipher::new($key);
            }

            impl $crate::IdBrand for Hasher {
                const BRAND: &'static str = stringify!($m_name);
            }
        }
    };
}
