1use std::collections::BTreeSet;
4
5use serde::{Deserialize, Serialize};
6
7use crate::{
8 Account, Blob, Date, Decimal, Duration, FieldSourceKey, Float32, Float64,
9 MAX_PROPOSAL_LITERAL_BYTES, MAX_SOURCE_CHECK_INSTRUCTIONS, Principal, ScalarKind,
10 SchemaContractError, Subaccount, Timestamp, TypeSourceKey, U256, Ulid, Unit,
11};
12
13#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
15pub enum ScalarLiteral {
16 Account(Account),
18 Blob(#[serde(deserialize_with = "crate::codec::deserialize_literal_blob")] Blob),
20 Bool(bool),
22 Date(Date),
24 Decimal(#[serde(deserialize_with = "crate::codec::deserialize_literal_decimal")] Decimal),
26 Duration(Duration),
28 EnumUnit {
30 #[serde(
32 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
33 )]
34 enum_type: TypeSourceKey,
35 #[serde(
37 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
38 )]
39 variant: TypeSourceKey,
40 },
41 Float32(Float32),
43 Float64(Float64),
45 Int(i128),
47 Nat(u128),
49 U256(U256),
51 Principal(Principal),
53 Subaccount(Subaccount),
55 Text(#[serde(deserialize_with = "crate::codec::deserialize_literal_text")] String),
57 Timestamp(Timestamp),
59 Ulid(Ulid),
61 Unit(Unit),
63}
64
65impl ScalarLiteral {
66 #[must_use]
68 pub const fn kind(&self) -> ScalarKind {
69 match self {
70 Self::Account(_) => ScalarKind::Account,
71 Self::Blob(_) => ScalarKind::Blob,
72 Self::Bool(_) => ScalarKind::Bool,
73 Self::Date(_) => ScalarKind::Date,
74 Self::Decimal(_) => ScalarKind::Decimal,
75 Self::Duration(_) => ScalarKind::Duration,
76 Self::EnumUnit { .. } => ScalarKind::Enum,
77 Self::Float32(_) => ScalarKind::Float32,
78 Self::Float64(_) => ScalarKind::Float64,
79 Self::Int(_) => ScalarKind::Int128,
80 Self::Nat(_) => ScalarKind::Nat128,
81 Self::U256(_) => ScalarKind::U256,
82 Self::Principal(_) => ScalarKind::Principal,
83 Self::Subaccount(_) => ScalarKind::Subaccount,
84 Self::Text(_) => ScalarKind::Text,
85 Self::Timestamp(_) => ScalarKind::Timestamp,
86 Self::Ulid(_) => ScalarKind::Ulid,
87 Self::Unit(_) => ScalarKind::Unit,
88 }
89 }
90
91 pub(crate) const fn validate(&self) -> Result<(), SchemaContractError> {
92 match self {
93 Self::Decimal(value) if value.scale() > crate::decimal::MAX_SUPPORTED_SCALE => {
94 Err(SchemaContractError::InvalidLiteral)
95 }
96 Self::Blob(value) if value.len() > MAX_PROPOSAL_LITERAL_BYTES => {
97 Err(SchemaContractError::InvalidLiteral)
98 }
99 Self::Text(value) if value.len() > MAX_PROPOSAL_LITERAL_BYTES => {
100 Err(SchemaContractError::InvalidLiteral)
101 }
102 _ => Ok(()),
103 }
104 }
105}
106
107#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
112pub enum SourceCheckInstruction {
113 Field(
115 #[serde(
116 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
117 )]
118 FieldSourceKey,
119 ),
120 Literal(ScalarLiteral),
122 Equal,
124 NotEqual,
126 LessThan,
128 LessThanOrEqual,
130 GreaterThan,
132 GreaterThanOrEqual,
134 And,
136 Or,
138 Not,
140 IsNull,
142 IsNotNull,
144 Length,
146}
147
148#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
150pub struct SourceCheckExpr {
151 #[serde(
152 deserialize_with = "crate::codec::deserialize_list::<_, _, { crate::MAX_SOURCE_CHECK_INSTRUCTIONS }>"
153 )]
154 instructions: Vec<SourceCheckInstruction>,
155}
156
157impl SourceCheckExpr {
158 pub fn try_new(instructions: Vec<SourceCheckInstruction>) -> Result<Self, SchemaContractError> {
165 let expression = Self { instructions };
166 expression.validate()?;
167 Ok(expression)
168 }
169
170 #[must_use]
172 pub fn instructions(&self) -> &[SourceCheckInstruction] {
173 &self.instructions
174 }
175
176 #[must_use]
178 pub fn dependencies(&self) -> BTreeSet<FieldSourceKey> {
179 self.instructions
180 .iter()
181 .filter_map(|instruction| match instruction {
182 SourceCheckInstruction::Field(field) => Some(field.clone()),
183 _ => None,
184 })
185 .collect()
186 }
187
188 pub(crate) fn validate(&self) -> Result<(), SchemaContractError> {
189 if self.instructions.is_empty() || self.instructions.len() > MAX_SOURCE_CHECK_INSTRUCTIONS {
190 return Err(SchemaContractError::InvalidExpression);
191 }
192 let mut stack_depth = 0usize;
193 for instruction in &self.instructions {
194 match instruction {
195 SourceCheckInstruction::Field(_) => {
196 stack_depth = stack_depth
197 .checked_add(1)
198 .ok_or(SchemaContractError::InvalidExpression)?;
199 }
200 SourceCheckInstruction::Literal(literal) => {
201 literal.validate()?;
202 stack_depth = stack_depth
203 .checked_add(1)
204 .ok_or(SchemaContractError::InvalidExpression)?;
205 }
206 SourceCheckInstruction::Not
207 | SourceCheckInstruction::IsNull
208 | SourceCheckInstruction::IsNotNull
209 | SourceCheckInstruction::Length => {
210 if stack_depth < 1 {
211 return Err(SchemaContractError::InvalidExpression);
212 }
213 }
214 SourceCheckInstruction::Equal
215 | SourceCheckInstruction::NotEqual
216 | SourceCheckInstruction::LessThan
217 | SourceCheckInstruction::LessThanOrEqual
218 | SourceCheckInstruction::GreaterThan
219 | SourceCheckInstruction::GreaterThanOrEqual
220 | SourceCheckInstruction::And
221 | SourceCheckInstruction::Or => {
222 if stack_depth < 2 {
223 return Err(SchemaContractError::InvalidExpression);
224 }
225 stack_depth -= 1;
226 }
227 }
228 }
229 if stack_depth != 1 {
230 return Err(SchemaContractError::InvalidExpression);
231 }
232 Ok(())
233 }
234}