use super::{schema_value::SchemaValue, traits::ToSchema};
use anchor_lang::prelude::*;
#[cfg(feature = "compression")]
use crate::compression::{enum_idl, ToNode};
use crate::utils::VecMap;
#[cfg(feature = "compression")]
use anchor_lang::solana_program::keccak;
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(feature = "compression", derive(ToNode))]
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "compression", enum_idl)]
pub enum Schema {
Null,
Bool,
Number,
String,
Array(Box<Self>),
Object(VecMap<String, Self>),
Pubkey,
Option(Box<Self>),
VecMap(Box<Self>, Box<Self>),
Enum(VecMap<String, Self>),
Any,
}
impl ToString for Schema {
fn to_string(&self) -> String {
let mut schema_str = String::new();
match self {
Self::Null => schema_str.push_str("null"),
Self::Bool => schema_str.push_str("bool"),
Self::Number => schema_str.push_str("number"),
Self::String => schema_str.push_str("string"),
Self::Array(schema) => {
schema_str.push_str(format!("Array<{}>", schema.to_string()).as_str())
}
Self::Object(schema) => {
schema_str.push_str("{ ");
schema.iter().for_each(|(key, value)| {
schema_str.push_str(format!("{}: {}, ", key, value.to_string()).as_str());
});
schema_str.push_str("}");
}
Self::Pubkey => schema_str.push_str("pubkey"),
Self::Option(schema) => {
schema_str.push_str(format!("Option<{}>", schema.to_string()).as_str())
}
Self::VecMap(key, schema) => schema_str
.push_str(format!("Map<{}, {}>", key.to_string(), schema.to_string()).as_str()),
Self::Enum(variants) => {
schema_str.push_str("Enum {");
variants.iter().for_each(|variant| {
schema_str.push_str(&format!("{}: {}, ", variant.0, variant.1.to_string()));
});
schema_str.push_str("}");
}
Schema::Any => schema_str.push_str("Any"),
};
schema_str
}
}
impl ToSchema for Schema {
fn schema() -> Self {
Schema::Any
}
fn schema_value(&self) -> SchemaValue {
SchemaValue::Schema(self.clone())
}
}
impl Into<serde_json::Value> for Schema {
fn into(self) -> serde_json::Value {
let mut schema_json = serde_json::Map::<String, serde_json::Value>::new();
match self {
Schema::Null => {
schema_json.insert(
String::from("__kind"),
serde_json::Value::String(String::from("null")),
);
}
Schema::Bool => {
schema_json.insert(
String::from("__kind"),
serde_json::Value::String(String::from("bool")),
);
}
Schema::Number => {
schema_json.insert(
String::from("__kind"),
serde_json::Value::String(String::from("number")),
);
}
Schema::String => {
schema_json.insert(
String::from("__kind"),
serde_json::Value::String(String::from("string")),
);
}
Schema::Array(value) => {
schema_json.insert(
String::from("__kind"),
serde_json::Value::String(String::from("array")),
);
schema_json.insert(String::from("type"), (*value).into());
}
Schema::Object(value) => {
schema_json.insert(
String::from("__kind"),
serde_json::Value::String(String::from("object")),
);
let mut params = serde_json::Map::<String, serde_json::Value>::new();
for (key, value) in value {
params.insert(key, value.into());
}
schema_json.insert(String::from("params"), serde_json::Value::Object(params));
}
Schema::Pubkey => {
schema_json.insert(
String::from("__kind"),
serde_json::Value::String(String::from("pubkey")),
);
}
Schema::Option(value) => {
schema_json.insert(
String::from("__kind"),
serde_json::Value::String(String::from("option")),
);
schema_json.insert(String::from("type"), (*value).into());
}
Schema::VecMap(key, value) => {
schema_json.insert(
String::from("__kind"),
serde_json::Value::String(String::from("map")),
);
schema_json.insert(String::from("key_type"), (*key).into());
schema_json.insert(String::from("value_type"), (*value).into());
}
Schema::Enum(variants) => {
schema_json.insert(
String::from("__kind"),
serde_json::Value::String(String::from("enum")),
);
let mut enum_variants = serde_json::Map::<String, serde_json::Value>::new();
for (kind, value) in variants {
enum_variants.insert(kind, value.into());
}
schema_json.insert(
String::from("variants"),
serde_json::Value::Object(enum_variants),
);
}
Schema::Any => {
schema_json.insert(
String::from("__kind"),
serde_json::Value::String(String::from("any")),
);
}
};
serde_json::Value::Object(schema_json)
}
}
impl From<serde_json::Value> for Schema {
fn from(value: serde_json::Value) -> Self {
if let Some(kind) = value.get("__kind") {
match kind.as_str().unwrap() {
"null" => return Schema::Null,
"bool" => return Schema::Bool,
"number" => return Schema::Number,
"string" => return Schema::String,
"array" => {
if let Some(value) = value.get("type") {
return Schema::Array(Box::new(Self::from(value.to_owned())));
}
}
"object" => {
if let Some(params) = value.get("params") {
if let serde_json::Value::Object(params) = params {
let mut obj = VecMap::new();
for (key, value) in params {
obj.insert(key.to_owned(), Schema::from(value.to_owned()));
}
return Schema::Object(obj);
}
}
}
"pubkey" => return Schema::Pubkey,
"option" => {
if let Some(value) = value.get("type") {
return Schema::Option(Box::new(Schema::from(value.to_owned())));
}
}
"map" => {
if let Some(key) = value.get("key") {
if let Some(value) = value.get("value") {
return Schema::VecMap(
Box::new(Schema::from(key.to_owned())),
Box::new(Schema::from(value.to_owned())),
);
}
}
}
"enum" => {
if let Some(variants) = value.get("variants") {
if let serde_json::Value::Object(variants) = variants {
let mut obj = VecMap::new();
for (kind, value) in variants {
obj.insert(kind.to_owned(), Schema::from(value.to_owned()));
}
return Schema::Enum(obj);
}
}
}
"any" => {
return Schema::Any;
}
_ => {
return Self::Null;
}
}
}
Self::Null
}
}
impl Schema {
pub fn pack(self) -> serde_json::Value {
let mut obj = serde_json::Map::<String, serde_json::Value>::new();
obj.insert(String::from("__schema"), self.into());
serde_json::Value::Object(obj)
}
pub fn try_unpack(value: &serde_json::Value) -> Option<Self> {
if let Some(schema) = value.get("__schema") {
return Some(Schema::from(schema.to_owned()));
}
None
}
}
impl Schema {
pub fn validate(&self, value: &mut SchemaValue) -> bool {
value.validate(self)
}
pub fn size(&self) -> usize {
match self {
Self::Array(schema) => 1 + schema.size(),
Self::Object(map) => {
let mut size = 1;
for (key, value) in map.iter() {
size += key.as_bytes().len();
size += value.size();
}
size
}
Self::Option(schema) => 2 + schema.size(),
Self::VecMap(key_schema, value_schema) => 1 + key_schema.size() + value_schema.size(),
Self::Enum(variants) => {
1 + variants.iter().fold(0, |acc, variant| {
acc + variant.0.as_bytes().len() + variant.1.size()
})
}
_ => 1,
}
}
pub fn size_for_borsh(&self) -> usize {
match self.try_to_vec() {
Ok(vec) => vec.len(),
Err(_) => 0,
}
}
}
impl AnchorSerialize for Schema {
#[inline]
fn serialize<W: std::io::prelude::Write>(&self, writer: &mut W) -> std::io::Result<()> {
match self {
Self::Null => 0u8.serialize(writer),
Self::Bool => 1u8.serialize(writer),
Self::Number => 2u8.serialize(writer),
Self::String => 3u8.serialize(writer),
Self::Array(value) => {
4u8.serialize(writer)?;
value.serialize(writer)
}
Self::Object(value) => {
5u8.serialize(writer)?;
value.serialize(writer)
}
Self::Pubkey => 6u8.serialize(writer),
Self::Option(value) => {
7u8.serialize(writer)?;
value.serialize(writer)
}
Self::VecMap(k, v) => {
8u8.serialize(writer)?;
k.serialize(writer)?;
v.serialize(writer)
}
Self::Enum(variants) => {
9u8.serialize(writer)?;
variants.serialize(writer)
}
Schema::Any => 10u8.serialize(writer),
}
}
}
impl AnchorDeserialize for Schema {
fn deserialize_reader<R: std::io::prelude::Read>(reader: &mut R) -> std::io::Result<Self> {
#[cfg(feature = "log")]
crate::logger::debug!("Processing Schema");
let mut buf: [u8; 1] = [0; 1];
let limit = reader.read(&mut buf)?;
if limit != 1 {
#[cfg(feature = "log")]
crate::logger::debug!("limit: {}, Buf: {:?}", limit, buf);
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Unexpected length of input",
));
}
#[cfg(feature = "log")]
crate::logger::debug!("Schema Processing buf: {:?}", buf);
let result = match buf[0] {
0 => Ok(Self::Null),
1 => Ok(Self::Bool),
2 => Ok(Self::Number),
3 => Ok(Self::String),
4 => Schema::deserialize_reader(reader).and_then(|v| Ok(Self::Array(Box::new(v)))),
5 => VecMap::deserialize_reader(reader).and_then(|v| Ok(Self::Object(v))),
6 => Ok(Self::Pubkey),
7 => Schema::deserialize_reader(reader).and_then(|v| Ok(Self::Option(Box::new(v)))),
8 => {
let k = Schema::deserialize_reader(reader)?;
let v = Schema::deserialize_reader(reader)?;
Ok(Self::VecMap(Box::new(k), Box::new(v)))
}
9 => {
let variants = VecMap::<String, Self>::deserialize_reader(reader)?;
Ok(Self::Enum(variants))
}
10 => Ok(Schema::Any),
_ => {
let msg = format!(
"Invalid Option representation: {}. The first byte must be 0 till 3",
buf[0]
);
Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, msg))
}
};
if result.is_err() {
let mut vec = Vec::<u8>::new();
reader.read_to_end(&mut vec)?;
#[cfg(feature = "log")]
crate::logger::error!(
"Schema Failed at buf: {:?}, Bytes remaining: {:?}",
buf,
vec
);
}
result
}
}