1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use crate::rust::prelude::*;
use crate::*;

/// This is the struct used in the Schema
#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
pub struct NovelTypeMetadata {
    pub type_hash: TypeHash,
    pub type_metadata: TypeMetadata,
}

/// This enables the type to be represented as eg JSON
/// Also used to facilitate type reconstruction
#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
pub struct TypeMetadata {
    pub type_name: Option<Cow<'static, str>>,
    pub child_names: Option<ChildNames>,
}

#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
pub enum ChildNames {
    NamedFields(Vec<Cow<'static, str>>),
    EnumVariants(IndexMap<u8, TypeMetadata>),
}

impl TypeMetadata {
    pub fn unnamed() -> Self {
        Self {
            type_name: None,
            child_names: None,
        }
    }

    pub fn no_child_names(name: &'static str) -> Self {
        Self {
            type_name: Some(Cow::Borrowed(name)),
            child_names: None,
        }
    }

    pub fn struct_fields(name: &'static str, field_names: &[&'static str]) -> Self {
        let field_names = field_names
            .iter()
            .map(|field_name| Cow::Borrowed(*field_name))
            .collect();
        Self {
            type_name: Some(Cow::Borrowed(name)),
            child_names: Some(ChildNames::NamedFields(field_names)),
        }
    }

    pub fn enum_variants(name: &'static str, variant_naming: IndexMap<u8, TypeMetadata>) -> Self {
        Self {
            type_name: Some(Cow::Borrowed(name)),
            child_names: Some(ChildNames::EnumVariants(variant_naming)),
        }
    }

    pub fn with_name(mut self, name: Option<Cow<'static, str>>) -> Self {
        self.type_name = name;
        self
    }

    pub fn with_type_hash(self, type_hash: TypeHash) -> NovelTypeMetadata {
        NovelTypeMetadata {
            type_hash,
            type_metadata: self,
        }
    }

    pub fn get_name(&self) -> Option<&str> {
        self.type_name.as_ref().map(|c| c.as_ref())
    }

    pub fn get_name_string(&self) -> Option<String> {
        self.type_name.as_ref().map(|c| c.to_string())
    }

    pub fn get_field_names<'a>(&'a self) -> Option<&'a [Cow<'static, str>]> {
        match &self.child_names {
            Some(ChildNames::NamedFields(field_names)) => Some(field_names.as_slice()),
            _ => None,
        }
    }

    pub fn get_matching_tuple_data(&self, fields_length: usize) -> TupleData {
        TupleData {
            name: self.get_name(),
            field_names: self
                .get_field_names()
                .filter(|field_names| field_names.len() == fields_length),
        }
    }

    pub fn get_matching_enum_variant_data(
        &self,
        variant_id: u8,
        fields_length: usize,
    ) -> EnumVariantData {
        let enum_name = self.get_name();
        let Some(ChildNames::EnumVariants(variants)) = &self.child_names else {
            return EnumVariantData {
                enum_name,
                variant_name: None,
                field_names: None,
            };
        };
        let Some(variant_metadata) = variants.get(&variant_id) else {
            return EnumVariantData {
                enum_name,
                variant_name: None,
                field_names: None,
            };
        };
        EnumVariantData {
            enum_name,
            variant_name: variant_metadata.get_name(),
            field_names: variant_metadata
                .get_field_names()
                .filter(|field_names| field_names.len() == fields_length),
        }
    }
}

#[derive(Debug, Default)]
pub struct TupleData<'s> {
    pub name: Option<&'s str>,
    pub field_names: Option<&'s [Cow<'static, str>]>,
}

#[derive(Debug, Default)]
pub struct EnumVariantData<'s> {
    pub enum_name: Option<&'s str>,
    pub variant_name: Option<&'s str>,
    pub field_names: Option<&'s [Cow<'static, str>]>,
}