hpl_toolkit/schema/
schema.rs

1use super::{schema_value::SchemaValue, traits::ToSchema};
2use anchor_lang::prelude::*;
3
4#[cfg(feature = "compression")]
5use crate::compression::ToNode;
6use crate::{errors::HplToolkitError, utils::VecMap};
7#[cfg(feature = "compression")]
8use anchor_lang::solana_program::keccak;
9
10#[cfg_attr(feature = "debug", derive(Debug))]
11#[cfg_attr(feature = "compression", derive(ToNode))]
12#[cfg_attr(feature = "idl-build", derive(hpl_toolkit_derive_gen_idl::GenIdl))]
13#[derive(Clone, PartialEq)]
14pub enum Schema {
15    Null,
16    Bool,
17    Number,
18    String,
19    Array(Box<Self>),
20    Object(VecMap<String, Self>),
21    Pubkey,
22    Option(Box<Self>),
23    VecMap(Box<Self>, Box<Self>),
24    Enum(VecMap<String, Self>),
25    Any,
26}
27
28#[cfg_attr(feature = "debug", derive(Debug))]
29#[cfg_attr(feature = "compression", derive(ToNode))]
30#[derive(Clone, PartialEq)]
31pub struct SchemaContainer {
32    parsed: Option<Schema>,
33    raw: Option<Vec<u8>>,
34}
35
36#[cfg(feature = "idl-build")]
37use anchor_lang::idl::types::*;
38#[cfg(feature = "idl-build")]
39impl anchor_lang::idl::build::IdlBuild for SchemaContainer {
40    fn get_full_path() -> String {
41        format!("{0}::{1}", "hpl_toolkit::schema", "SchemaContainer",)
42    }
43
44    fn create_type() -> Option<IdlTypeDef> {
45        Some(IdlTypeDef {
46            name: "SchemaContainer".to_string(),
47            docs: vec![],
48            serialization: IdlSerialization::Borsh,
49            repr: None,
50            generics: vec![],
51            ty: IdlTypeDefTy::Struct {
52                fields: Some(IdlDefinedFields::Named(vec![IdlField {
53                    name: "raw".to_string(),
54                    ty: IdlType::Vec(Box::new(IdlType::U8)),
55                    docs: vec![],
56                }])),
57            },
58        })
59    }
60
61    fn insert_types(_types: &mut std::collections::BTreeMap<String, IdlTypeDef>) {
62        if !_types.contains_key("Schema") {
63            _types.insert("Schema".to_string(), Schema::create_type().unwrap());
64        }
65        if !_types.contains_key("SchemaValue") {
66            _types.insert("SchemaValue".to_string(), SchemaValue::create_type().unwrap());
67        }
68        if !_types.contains_key("Number") {
69            _types.insert("Number".to_string(), super::Number::create_type().unwrap());
70        }
71        // _types.insert(
72        //     "SchemaValue".to_string(),
73        //     SchemaValue::create_type().unwrap(),
74        // );
75        _types.insert("Number".to_string(), super::Number::create_type().unwrap());
76    }
77}
78impl ToSchema for SchemaContainer {
79    fn schema() -> Schema {
80        Schema::schema()
81    }
82    fn schema_value(&self) -> SchemaValue {
83        if let Some(schema) = &self.parsed {
84            return SchemaValue::Schema(schema.to_owned());
85        } else if let Some(bytes) = &self.raw {
86            if let Ok(schema) = Schema::deserialize(&mut &bytes[..]) {
87                return SchemaValue::Schema(schema);
88            }
89        }
90        SchemaValue::Null
91    }
92}
93impl AnchorDeserialize for SchemaContainer {
94    fn deserialize_reader<R: std::io::prelude::Read>(reader: &mut R) -> std::io::Result<Self> {
95        let buffer = Vec::<u8>::deserialize_reader(reader)?;
96        Ok(Self {
97            parsed: None,
98            raw: Some(buffer),
99        })
100    }
101}
102impl AnchorSerialize for SchemaContainer {
103    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
104        if let Some(bytes) = &self.raw {
105            bytes.serialize(writer)?;
106        } else if let Some(schema) = &self.parsed {
107            let bytes = schema.try_to_vec()?;
108            bytes.serialize(writer)?;
109        } else {
110            return Err(std::io::Error::from(std::io::ErrorKind::InvalidData));
111        };
112        Ok(())
113    }
114}
115
116impl SchemaContainer {
117    pub fn size(&self) -> usize {
118        if let Some(bytes) = &self.raw {
119            return bytes.len() + 4;
120        } else if let Some(schema) = &self.parsed {
121            return schema.size() + 4;
122        }
123
124        0
125    }
126    pub fn new(schema: Schema) -> Self {
127        Self {
128            raw: None,
129            parsed: Some(schema),
130        }
131    }
132
133    pub fn new_from_bytes(bytes: Vec<u8>) -> Self {
134        Self {
135            raw: Some(bytes),
136            parsed: None,
137        }
138    }
139
140    pub fn parse(&mut self) -> Result<()> {
141        if self.parsed.is_none() {
142            if self.raw.is_none() {
143                return Err(HplToolkitError::SchemaContainerEmpty.into());
144            }
145            self.parsed = Some(Schema::deserialize(&mut &self.raw.take().unwrap()[..])?)
146        }
147        Ok(())
148    }
149    pub fn get_schema(&mut self) -> Result<&Schema> {
150        self.parse()?;
151        Ok(self.parsed.as_ref().unwrap())
152    }
153
154    pub fn get_schema_mut(&mut self) -> Result<&mut Schema> {
155        self.parse()?;
156        Ok(self.parsed.as_mut().unwrap())
157    }
158}
159
160impl ToString for Schema {
161    fn to_string(&self) -> String {
162        let mut schema_str = String::new();
163        match self {
164            Self::Null => schema_str.push_str("null"),
165            Self::Bool => schema_str.push_str("bool"),
166            Self::Number => schema_str.push_str("number"),
167            Self::String => schema_str.push_str("string"),
168            Self::Array(schema) => {
169                schema_str.push_str(format!("Array<{}>", schema.to_string()).as_str())
170            }
171            Self::Object(schema) => {
172                schema_str.push_str("{ ");
173                schema.iter().for_each(|(key, value)| {
174                    schema_str.push_str(format!("{}: {}, ", key, value.to_string()).as_str());
175                });
176                schema_str.push_str("}");
177            }
178            Self::Pubkey => schema_str.push_str("pubkey"),
179            Self::Option(schema) => {
180                schema_str.push_str(format!("Option<{}>", schema.to_string()).as_str())
181            }
182            Self::VecMap(key, schema) => schema_str
183                .push_str(format!("Map<{}, {}>", key.to_string(), schema.to_string()).as_str()),
184            Self::Enum(variants) => {
185                schema_str.push_str("Enum {");
186                variants.iter().for_each(|variant| {
187                    schema_str.push_str(&format!("{}: {}, ", variant.0, variant.1.to_string()));
188                });
189                schema_str.push_str("}");
190            }
191            Schema::Any => schema_str.push_str("Any"),
192        };
193        schema_str
194    }
195}
196
197impl ToSchema for Schema {
198    fn schema() -> Self {
199        // unknown schema
200        Schema::Any
201    }
202
203    fn schema_value(&self) -> SchemaValue {
204        SchemaValue::Schema(self.clone())
205    }
206}
207
208impl Into<serde_json::Value> for Schema {
209    fn into(self) -> serde_json::Value {
210        let mut schema_json = serde_json::Map::<String, serde_json::Value>::new();
211        match self {
212            Schema::Null => {
213                schema_json.insert(
214                    String::from("__kind"),
215                    serde_json::Value::String(String::from("null")),
216                );
217            }
218            Schema::Bool => {
219                schema_json.insert(
220                    String::from("__kind"),
221                    serde_json::Value::String(String::from("bool")),
222                );
223            }
224            Schema::Number => {
225                schema_json.insert(
226                    String::from("__kind"),
227                    serde_json::Value::String(String::from("number")),
228                );
229            }
230            Schema::String => {
231                schema_json.insert(
232                    String::from("__kind"),
233                    serde_json::Value::String(String::from("string")),
234                );
235            }
236            Schema::Array(value) => {
237                schema_json.insert(
238                    String::from("__kind"),
239                    serde_json::Value::String(String::from("array")),
240                );
241                schema_json.insert(String::from("type"), (*value).into());
242            }
243            Schema::Object(value) => {
244                schema_json.insert(
245                    String::from("__kind"),
246                    serde_json::Value::String(String::from("object")),
247                );
248
249                let mut params = serde_json::Map::<String, serde_json::Value>::new();
250                for (key, value) in value {
251                    params.insert(key, value.into());
252                }
253
254                schema_json.insert(String::from("params"), serde_json::Value::Object(params));
255            }
256            Schema::Pubkey => {
257                schema_json.insert(
258                    String::from("__kind"),
259                    serde_json::Value::String(String::from("pubkey")),
260                );
261            }
262            Schema::Option(value) => {
263                schema_json.insert(
264                    String::from("__kind"),
265                    serde_json::Value::String(String::from("option")),
266                );
267                schema_json.insert(String::from("type"), (*value).into());
268            }
269            Schema::VecMap(key, value) => {
270                schema_json.insert(
271                    String::from("__kind"),
272                    serde_json::Value::String(String::from("map")),
273                );
274                schema_json.insert(String::from("key_type"), (*key).into());
275                schema_json.insert(String::from("value_type"), (*value).into());
276            }
277            Schema::Enum(variants) => {
278                schema_json.insert(
279                    String::from("__kind"),
280                    serde_json::Value::String(String::from("enum")),
281                );
282                let mut enum_variants = serde_json::Map::<String, serde_json::Value>::new();
283                for (kind, value) in variants {
284                    enum_variants.insert(kind, value.into());
285                }
286                schema_json.insert(
287                    String::from("variants"),
288                    serde_json::Value::Object(enum_variants),
289                );
290            }
291            Schema::Any => {
292                schema_json.insert(
293                    String::from("__kind"),
294                    serde_json::Value::String(String::from("any")),
295                );
296            }
297        };
298        serde_json::Value::Object(schema_json)
299    }
300}
301
302impl From<serde_json::Value> for Schema {
303    fn from(value: serde_json::Value) -> Self {
304        if let Some(kind) = value.get("__kind") {
305            match kind.as_str().unwrap() {
306                "null" => return Schema::Null,
307                "bool" => return Schema::Bool,
308                "number" => return Schema::Number,
309                "string" => return Schema::String,
310                "array" => {
311                    if let Some(value) = value.get("type") {
312                        return Schema::Array(Box::new(Self::from(value.to_owned())));
313                    }
314                }
315                "object" => {
316                    if let Some(params) = value.get("params") {
317                        if let serde_json::Value::Object(params) = params {
318                            let mut obj = VecMap::new();
319                            for (key, value) in params {
320                                obj.insert(key.to_owned(), Schema::from(value.to_owned()));
321                            }
322                            return Schema::Object(obj);
323                        }
324                    }
325                }
326                "pubkey" => return Schema::Pubkey,
327                "option" => {
328                    if let Some(value) = value.get("type") {
329                        return Schema::Option(Box::new(Schema::from(value.to_owned())));
330                    }
331                }
332                "map" => {
333                    if let Some(key) = value.get("key") {
334                        if let Some(value) = value.get("value") {
335                            return Schema::VecMap(
336                                Box::new(Schema::from(key.to_owned())),
337                                Box::new(Schema::from(value.to_owned())),
338                            );
339                        }
340                    }
341                }
342                "enum" => {
343                    if let Some(variants) = value.get("variants") {
344                        if let serde_json::Value::Object(variants) = variants {
345                            let mut obj = VecMap::new();
346                            for (kind, value) in variants {
347                                obj.insert(kind.to_owned(), Schema::from(value.to_owned()));
348                            }
349                            return Schema::Enum(obj);
350                        }
351                    }
352                }
353                "any" => {
354                    return Schema::Any;
355                }
356                _ => {
357                    return Self::Null;
358                }
359            }
360        }
361        Self::Null
362    }
363}
364
365impl Schema {
366    pub fn pack(self) -> serde_json::Value {
367        let mut obj = serde_json::Map::<String, serde_json::Value>::new();
368        obj.insert(String::from("__schema"), self.into());
369        serde_json::Value::Object(obj)
370    }
371
372    pub fn try_unpack(value: &serde_json::Value) -> Option<Self> {
373        if let Some(schema) = value.get("__schema") {
374            return Some(Schema::from(schema.to_owned()));
375        }
376        None
377    }
378}
379
380impl Schema {
381    pub fn validate(&self, value: &mut SchemaValue) -> bool {
382        value.validate(self)
383    }
384
385    pub fn size(&self) -> usize {
386        match self {
387            Self::Array(schema) => 1 + schema.size(),
388            Self::Object(map) => {
389                let mut size = 1;
390                for (key, value) in map.iter() {
391                    size += key.as_bytes().len();
392                    size += value.size();
393                }
394                size
395            }
396            Self::Option(schema) => 2 + schema.size(),
397            Self::VecMap(key_schema, value_schema) => 1 + key_schema.size() + value_schema.size(),
398            Self::Enum(variants) => {
399                1 + variants.iter().fold(0, |acc, variant| {
400                    acc + variant.0.as_bytes().len() + variant.1.size()
401                })
402            }
403            _ => 1,
404        }
405    }
406
407    pub fn size_for_borsh(&self) -> usize {
408        match self.try_to_vec() {
409            Ok(vec) => vec.len(),
410            Err(_) => 0,
411        }
412    }
413}
414
415impl AnchorSerialize for Schema {
416    #[inline]
417    fn serialize<W: std::io::prelude::Write>(&self, writer: &mut W) -> std::io::Result<()> {
418        match self {
419            Self::Null => 0u8.serialize(writer),
420            Self::Bool => 1u8.serialize(writer),
421            Self::Number => 2u8.serialize(writer),
422            Self::String => 3u8.serialize(writer),
423            Self::Array(value) => {
424                4u8.serialize(writer)?;
425                value.serialize(writer)
426            }
427            Self::Object(value) => {
428                5u8.serialize(writer)?;
429                value.serialize(writer)
430            }
431            Self::Pubkey => 6u8.serialize(writer),
432            Self::Option(value) => {
433                7u8.serialize(writer)?;
434                value.serialize(writer)
435            }
436            Self::VecMap(k, v) => {
437                8u8.serialize(writer)?;
438                k.serialize(writer)?;
439                v.serialize(writer)
440            }
441            Self::Enum(variants) => {
442                9u8.serialize(writer)?;
443                variants.serialize(writer)
444            }
445            Schema::Any => 10u8.serialize(writer),
446        }
447    }
448}
449
450impl AnchorDeserialize for Schema {
451    fn deserialize_reader<R: std::io::prelude::Read>(reader: &mut R) -> std::io::Result<Self> {
452        #[cfg(feature = "log")]
453        crate::logger::debug!("Processing Schema");
454        let mut buf: [u8; 1] = [0; 1];
455        let limit = reader.read(&mut buf)?;
456        if limit != 1 {
457            #[cfg(feature = "log")]
458            crate::logger::debug!("limit: {}, Buf: {:?}", limit, buf);
459            return Err(std::io::Error::new(
460                std::io::ErrorKind::InvalidInput,
461                "Unexpected length of input",
462            ));
463        }
464        #[cfg(feature = "log")]
465        crate::logger::debug!("Schema Processing buf: {:?}", buf);
466        let result = match buf[0] {
467            0 => Ok(Self::Null),
468            1 => Ok(Self::Bool),
469            2 => Ok(Self::Number),
470            3 => Ok(Self::String),
471            4 => Schema::deserialize_reader(reader).and_then(|v| Ok(Self::Array(Box::new(v)))),
472            5 => VecMap::deserialize_reader(reader).and_then(|v| Ok(Self::Object(v))),
473            6 => Ok(Self::Pubkey),
474            7 => Schema::deserialize_reader(reader).and_then(|v| Ok(Self::Option(Box::new(v)))),
475            8 => {
476                let k = Schema::deserialize_reader(reader)?;
477                let v = Schema::deserialize_reader(reader)?;
478                Ok(Self::VecMap(Box::new(k), Box::new(v)))
479            }
480            9 => {
481                let variants = VecMap::<String, Self>::deserialize_reader(reader)?;
482                Ok(Self::Enum(variants))
483            }
484            10 => Ok(Schema::Any),
485            _ => {
486                let msg = format!(
487                    "Invalid Option representation: {}. The first byte must be 0 till 3",
488                    buf[0]
489                );
490                Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, msg))
491            }
492        };
493
494        if result.is_err() {
495            let mut vec = Vec::<u8>::new();
496            reader.read_to_end(&mut vec)?;
497            #[cfg(feature = "log")]
498            crate::logger::error!(
499                "Schema Failed at buf: {:?}, Bytes remaining: {:?}",
500                buf,
501                vec
502            );
503        }
504
505        result
506    }
507}