use crate::{Config, Error};
use common_primitives::schema::{
ModelType, PayloadLocation, SchemaId, SchemaSetting, SchemaSettings, SchemaVersion,
SchemaVersionResponse,
};
use frame_support::{ensure, pallet_prelude::ConstU32, traits::StorageVersion, BoundedVec};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_runtime::DispatchError;
use sp_std::fmt::Debug;
extern crate alloc;
use alloc::string::String;
use frame_support::traits::Len;
use sp_std::{vec, vec::*};
pub const SCHEMA_STORAGE_VERSION: StorageVersion = StorageVersion::new(4);
pub const SCHEMA_NAME_BYTES_MAX: u32 = 32; pub type SchemaNamePayload = BoundedVec<u8, ConstU32<SCHEMA_NAME_BYTES_MAX>>;
pub type SchemaNamespace = BoundedVec<u8, ConstU32<NAMESPACE_MAX>>;
pub type SchemaDescriptor = BoundedVec<u8, ConstU32<DESCRIPTOR_MAX>>;
pub const NAMESPACE_MIN: u32 = 3;
pub const NAMESPACE_MAX: u32 = SCHEMA_NAME_BYTES_MAX - (DESCRIPTOR_MIN + 1);
pub const DESCRIPTOR_MIN: u32 = 1;
pub const DESCRIPTOR_MAX: u32 = SCHEMA_NAME_BYTES_MAX - (NAMESPACE_MIN + 1);
pub const SEPARATOR_CHAR: char = '.';
pub const MAX_NUMBER_OF_VERSIONS: u32 = SchemaVersion::MAX as u32 - 1;
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct GenesisSchema {
pub model_type: ModelType,
pub payload_location: PayloadLocation,
pub model: String,
pub name: String,
pub settings: Vec<SchemaSetting>,
}
#[derive(Clone, Encode, Decode, PartialEq, Debug, TypeInfo, Eq, MaxEncodedLen)]
pub struct SchemaInfo {
pub model_type: ModelType,
pub payload_location: PayloadLocation,
pub settings: SchemaSettings,
pub has_name: bool,
}
#[derive(Clone, Encode, Decode, PartialEq, Debug, TypeInfo, Eq, MaxEncodedLen)]
pub struct SchemaName {
pub namespace: SchemaNamespace,
pub descriptor: SchemaDescriptor,
}
#[derive(Clone, Encode, Decode, PartialEq, Debug, TypeInfo, Eq, MaxEncodedLen, Default)]
pub struct SchemaVersionId {
pub ids: BoundedVec<SchemaId, ConstU32<MAX_NUMBER_OF_VERSIONS>>,
}
impl SchemaName {
pub fn try_parse<T: Config>(
payload: SchemaNamePayload,
is_strict: bool,
) -> Result<SchemaName, DispatchError> {
let mut str = String::from_utf8(payload.into_inner())
.map_err(|_| Error::<T>::InvalidSchemaNameEncoding)?;
ensure!(str.is_ascii(), Error::<T>::InvalidSchemaNameEncoding);
str = String::from(str.to_lowercase().trim());
ensure!(
str.chars().all(|c| c.is_ascii_alphabetic() || c == '-' || c == SEPARATOR_CHAR),
Error::<T>::InvalidSchemaNameCharacters
);
let chunks: Vec<_> = str.split(SEPARATOR_CHAR).collect();
ensure!(
chunks.len() == 2 || (chunks.len() == 1 && !is_strict),
Error::<T>::InvalidSchemaNameStructure
);
let namespace = BoundedVec::try_from(chunks[0].as_bytes().to_vec())
.map_err(|_| Error::<T>::InvalidSchemaNamespaceLength)?;
ensure!(NAMESPACE_MIN <= namespace.len() as u32, Error::<T>::InvalidSchemaNamespaceLength);
ensure!(
!(namespace.starts_with(b"-") || namespace.ends_with(b"-")),
Error::<T>::InvalidSchemaNameStructure
);
let descriptor = match chunks.len() == 2 {
true => {
let descriptor = BoundedVec::try_from(chunks[1].as_bytes().to_vec())
.map_err(|_| Error::<T>::InvalidSchemaDescriptorLength)?;
ensure!(
DESCRIPTOR_MIN <= descriptor.len() as u32,
Error::<T>::InvalidSchemaDescriptorLength
);
ensure!(
!(descriptor.starts_with(b"-") || descriptor.ends_with(b"-")),
Error::<T>::InvalidSchemaNameStructure
);
descriptor
},
false => BoundedVec::default(),
};
Ok(SchemaName { namespace, descriptor })
}
pub fn get_combined_name(&self) -> Vec<u8> {
vec![
self.namespace.clone().into_inner(),
vec![SEPARATOR_CHAR as u8],
self.descriptor.clone().into_inner(),
]
.concat()
}
pub fn new_with_descriptor(&self, descriptor: SchemaDescriptor) -> Self {
Self { namespace: self.namespace.clone(), descriptor }
}
pub fn descriptor_exists(&self) -> bool {
self.descriptor.len() > 0
}
}
impl SchemaVersionId {
pub fn add<T: Config>(&mut self, schema_id: SchemaId) -> Result<SchemaVersion, DispatchError> {
let is_new = !self.ids.iter().any(|id| id == &schema_id);
ensure!(is_new, Error::<T>::SchemaIdAlreadyExists);
self.ids
.try_push(schema_id)
.map_err(|_| Error::<T>::ExceedsMaxNumberOfVersions)?;
let version = self.ids.len() as SchemaVersion;
Ok(version)
}
pub fn convert_to_response(&self, schema_name: &SchemaName) -> Vec<SchemaVersionResponse> {
self.ids
.iter()
.enumerate()
.map(|(index, schema_id)| SchemaVersionResponse {
schema_name: schema_name.get_combined_name(),
schema_id: *schema_id,
schema_version: (index + 1) as SchemaVersion,
})
.collect()
}
}