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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
use crate::impl_codec_bitflags;
#[cfg(feature = "std")]
use crate::utils;
use enumflags2::{bitflags, BitFlags};
use parity_scale_codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter};
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_runtime::RuntimeDebug;
use sp_std::prelude::*;
#[cfg(feature = "std")]
use utils::*;

/// Schema Id is the unique identifier for a Schema
pub type SchemaId = u16;

/// Schema version number
pub type SchemaVersion = u8;

/// Types of modeling in which a message payload may be defined
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Encode, Decode, PartialEq, Debug, TypeInfo, Eq, MaxEncodedLen)]
pub enum ModelType {
	/// Message payload modeled with Apache Avro: <https://avro.apache.org/docs/current/spec.html>
	AvroBinary,
	/// Message payload modeled with Apache Parquet: <https://parquet.apache.org/>
	Parquet,
}

/// Types of payload locations
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Encode, Decode, PartialEq, Debug, TypeInfo, Eq, MaxEncodedLen)]
pub enum PayloadLocation {
	/// Message payload is located on chain
	OnChain,
	/// Message payload is located on IPFS
	IPFS,
	/// Itemized payload location for onchain storage in itemized form
	Itemized,
	/// Paginated payload location for onchain storage in paginated form
	Paginated,
}

/// Support for up to 16 user-enabled features on a collection.
#[bitflags]
#[repr(u16)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)]
pub enum SchemaSetting {
	/// Schema setting to enforce append-only behavior on payload.
	/// Applied to schemas of type `PayloadLocation::Itemized` or `PayloadLocation::Paginated`.
	AppendOnly,
	/// Schema may enforce signature requirement on payload.
	/// Applied to schemas of type `PayloadLocation::Paginated`.
	SignatureRequired,
}

/// Wrapper type for `BitFlags<SchemaSetting>` that implements `Codec`.
#[derive(Clone, Copy, PartialEq, Eq, Default, RuntimeDebug)]
pub struct SchemaSettings(pub BitFlags<SchemaSetting>);

/// RPC Response form for a Schema
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, Encode, Decode, PartialEq, Debug, TypeInfo, Eq)]
pub struct SchemaResponse {
	/// The unique identifier for this Schema
	pub schema_id: SchemaId,
	/// The data that represents how this schema is structured
	#[cfg_attr(feature = "std", serde(with = "as_string"))]
	pub model: Vec<u8>,
	/// The model format type for how the schema model is represented
	pub model_type: ModelType,
	/// The payload location
	pub payload_location: PayloadLocation,
	/// grants for the schema
	pub settings: Vec<SchemaSetting>,
}

/// RPC Response form for a Schema Info
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, Encode, Decode, PartialEq, Debug, TypeInfo, Eq)]
pub struct SchemaInfoResponse {
	/// The unique identifier for this Schema
	pub schema_id: SchemaId,
	/// The model format type for how the schema model is represented
	pub model_type: ModelType,
	/// The payload location
	pub payload_location: PayloadLocation,
	/// grants for the schema
	pub settings: Vec<SchemaSetting>,
}

/// This allows other pallets to resolve Schema information. With generic SchemaId
pub trait SchemaProvider<SchemaId> {
	/// Gets the Schema details associated with this `SchemaId` if any
	fn get_schema_by_id(schema_id: SchemaId) -> Option<SchemaResponse>;

	/// Gets the Schema Info associated with this `SchemaId` if any
	fn get_schema_info_by_id(schema_id: SchemaId) -> Option<SchemaInfoResponse>;
}

/// This allows other Pallets to check validity of schema ids.
pub trait SchemaValidator<SchemaId> {
	/// Checks that a collection of SchemaIds are all valid
	fn are_all_schema_ids_valid(schema_ids: &Vec<SchemaId>) -> bool;

	/// Set the schema counter for testing purposes.
	#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
	fn set_schema_count(n: SchemaId);
}

impl SchemaSettings {
	/// Returns new SchemaSettings with all settings disabled
	pub fn all_disabled() -> Self {
		Self(BitFlags::EMPTY)
	}
	/// Get all setting enabled
	pub fn get_enabled(&self) -> BitFlags<SchemaSetting> {
		self.0
	}
	/// Check if a setting is enabled
	pub fn is_enabled(&self, grant: SchemaSetting) -> bool {
		self.0.contains(grant)
	}
	/// Enable a setting
	pub fn set(&mut self, grant: SchemaSetting) {
		self.0.insert(grant)
	}
	/// Copy the settings from a BitFlags
	pub fn from(settings: BitFlags<SchemaSetting>) -> Self {
		Self(settings)
	}
}
impl_codec_bitflags!(SchemaSettings, u16, SchemaSetting);

/// RPC Response from a schema name query
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, Encode, Decode, PartialEq, Debug, TypeInfo, Eq)]
pub struct SchemaVersionResponse {
	/// Schema name in following format: namespace.descriptor
	#[cfg_attr(feature = "std", serde(with = "as_string"))]
	pub schema_name: Vec<u8>,
	/// The version for this schema
	pub schema_version: SchemaVersion,
	/// The unique identifier for this Schema
	pub schema_id: SchemaId,
}

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn schema_settings_when_disabled_has_no_enabled() {
		let settings = SchemaSettings::all_disabled();
		assert_eq!(settings.get_enabled(), BitFlags::EMPTY);
	}

	#[test]
	fn schema_settings_set_from_all_enabled_check() {
		let settings = SchemaSettings::from(BitFlags::ALL);
		assert!(settings.is_enabled(SchemaSetting::AppendOnly));
		assert!(settings.is_enabled(SchemaSetting::SignatureRequired));
	}
}