use std::hash::Hash;
use std::str::FromStr;
use amplify::{Bytes32, RawArray, Wrapper};
use baid58::{Baid58ParseError, FromBaid58, ToBaid58};
use encoding::{FieldName, LibName};
use sha2::Digest;
use strict_encoding::{Sizing, TypeName, Variant, STRICT_TYPES_LIB};
use crate::ast::ty::{Field, UnionVariants, UnnamedFields};
use crate::ast::{EnumVariants, NamedFields, PrimitiveRef};
use crate::{Cls, Ty, TypeRef};
#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)]
#[wrapper(Deref, BorrowSlice, Hex, Index, RangeOps)]
#[display(Self::to_baid58)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = STRICT_TYPES_LIB)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct SemId(
#[from]
#[from([u8; 32])]
Bytes32,
);
impl Default for SemId {
fn default() -> Self { Ty::<SemId>::UNIT.id(None) }
}
impl ToBaid58<32> for SemId {
const HRI: &'static str = "sty";
fn to_baid58_payload(&self) -> [u8; 32] { self.to_raw_array() }
}
impl FromBaid58<32> for SemId {}
impl FromStr for SemId {
type Err = Baid58ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::from_baid58_str(s) }
}
pub const SEM_ID_TAG: [u8; 32] = *b"urn:ubideco:strict-types:typ:v01";
impl TypeRef for SemId {
fn is_unicode_char(&self) -> bool { Self::unicode_char() == *self }
fn is_byte(&self) -> bool { Self::byte() == *self || Ty::<Self>::U8.id(None) == *self }
}
impl PrimitiveRef for SemId {
fn byte() -> Self { Ty::<Self>::BYTE.id(None) }
fn unicode_char() -> Self { Ty::<Self>::UNICODE.id(None) }
}
impl<Ref: TypeRef> Ty<Ref> {
pub fn id(&self, name: Option<&TypeName>) -> SemId {
let tag = sha2::Sha256::new_with_prefix(&SEM_ID_TAG).finalize();
let mut hasher = sha2::Sha256::new();
hasher.update(tag);
hasher.update(tag);
if let Some(name) = name {
name.hash_id(&mut hasher);
}
self.hash_id(&mut hasher);
SemId::from_raw_array(hasher.finalize())
}
}
pub trait HashId {
fn hash_id(&self, hasher: &mut sha2::Sha256);
}
impl HashId for LibName {
fn hash_id(&self, hasher: &mut sha2::Sha256) {
hasher.update([self.len() as u8]);
hasher.update(self.as_bytes());
}
}
impl HashId for TypeName {
fn hash_id(&self, hasher: &mut sha2::Sha256) {
hasher.update([self.len() as u8]);
hasher.update(self.as_bytes());
}
}
impl HashId for FieldName {
fn hash_id(&self, hasher: &mut sha2::Sha256) {
hasher.update([self.len() as u8]);
hasher.update(self.as_bytes());
}
}
impl HashId for SemId {
fn hash_id(&self, hasher: &mut sha2::Sha256) { hasher.update(self.as_slice()); }
}
impl<Ref: TypeRef> HashId for Ty<Ref> {
fn hash_id(&self, hasher: &mut sha2::Sha256) {
self.cls().hash_id(hasher);
match self {
Ty::Primitive(prim) => {
hasher.update(&[prim.into_code()]);
}
Ty::Enum(vars) => vars.hash_id(hasher),
Ty::Union(fields) => fields.hash_id(hasher),
Ty::Tuple(fields) => fields.hash_id(hasher),
Ty::Struct(fields) => fields.hash_id(hasher),
Ty::Array(ty, len) => {
ty.hash_id(hasher);
hasher.update(&len.to_le_bytes());
}
Ty::UnicodeChar => {}
Ty::List(ty, sizing) => {
ty.hash_id(hasher);
sizing.hash_id(hasher);
}
Ty::Set(ty, sizing) => {
ty.hash_id(hasher);
sizing.hash_id(hasher);
}
Ty::Map(key, ty, sizing) => {
key.hash_id(hasher);
ty.hash_id(hasher);
sizing.hash_id(hasher);
}
};
}
}
impl HashId for Cls {
fn hash_id(&self, hasher: &mut sha2::Sha256) { hasher.update(&[*self as u8]); }
}
impl<Ref: TypeRef> HashId for Field<Ref> {
fn hash_id(&self, hasher: &mut sha2::Sha256) {
self.name.hash_id(hasher);
self.ty.hash_id(hasher);
}
}
impl HashId for EnumVariants {
fn hash_id(&self, hasher: &mut sha2::Sha256) {
for variant in self {
variant.hash_id(hasher);
}
}
}
impl<Ref: TypeRef> HashId for UnionVariants<Ref> {
fn hash_id(&self, hasher: &mut sha2::Sha256) {
for (variant, ty) in self {
variant.hash_id(hasher);
ty.hash_id(hasher);
}
}
}
impl<Ref: TypeRef> HashId for NamedFields<Ref> {
fn hash_id(&self, hasher: &mut sha2::Sha256) {
for field in self {
field.hash_id(hasher);
}
}
}
impl<Ref: TypeRef> HashId for UnnamedFields<Ref> {
fn hash_id(&self, hasher: &mut sha2::Sha256) {
for ty in self {
ty.hash_id(hasher);
}
}
}
impl HashId for Variant {
fn hash_id(&self, hasher: &mut sha2::Sha256) {
self.name.hash_id(hasher);
hasher.update(&[self.tag]);
}
}
impl HashId for Sizing {
fn hash_id(&self, hasher: &mut sha2::Sha256) {
let mut data = self.min.to_le_bytes().to_vec();
data.extend(self.max.to_le_bytes());
hasher.update(&data);
}
}