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