1use std::collections::BTreeSet;
4
5use crate::{
6 Account, Blob, Date, Decimal, Duration, FieldSourceKey, Float32, Float64,
7 MAX_PROPOSAL_LITERAL_BYTES, MAX_SOURCE_CHECK_INSTRUCTIONS, 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 Nat(u128),
41 U256(U256),
43 Principal(Principal),
45 Subaccount(Subaccount),
47 Text(String),
49 Timestamp(Timestamp),
51 Ulid(Ulid),
53 Unit(Unit),
55}
56
57impl ScalarLiteral {
58 #[must_use]
60 pub const fn kind(&self) -> ScalarKind {
61 match self {
62 Self::Account(_) => ScalarKind::Account,
63 Self::Blob(_) => ScalarKind::Blob,
64 Self::Bool(_) => ScalarKind::Bool,
65 Self::Date(_) => ScalarKind::Date,
66 Self::Decimal(_) => ScalarKind::Decimal,
67 Self::Duration(_) => ScalarKind::Duration,
68 Self::EnumUnit { .. } => ScalarKind::Enum,
69 Self::Float32(_) => ScalarKind::Float32,
70 Self::Float64(_) => ScalarKind::Float64,
71 Self::Int(_) => ScalarKind::Int128,
72 Self::Nat(_) => ScalarKind::Nat128,
73 Self::U256(_) => ScalarKind::U256,
74 Self::Principal(_) => ScalarKind::Principal,
75 Self::Subaccount(_) => ScalarKind::Subaccount,
76 Self::Text(_) => ScalarKind::Text,
77 Self::Timestamp(_) => ScalarKind::Timestamp,
78 Self::Ulid(_) => ScalarKind::Ulid,
79 Self::Unit(_) => ScalarKind::Unit,
80 }
81 }
82
83 pub(crate) const fn validate(&self) -> Result<(), SchemaContractError> {
84 match self {
85 Self::Blob(value) if value.len() > MAX_PROPOSAL_LITERAL_BYTES => {
86 Err(SchemaContractError::InvalidLiteral)
87 }
88 Self::Text(value) if value.len() > MAX_PROPOSAL_LITERAL_BYTES => {
89 Err(SchemaContractError::InvalidLiteral)
90 }
91 _ => Ok(()),
92 }
93 }
94}
95
96#[derive(Clone, Debug, Eq, PartialEq)]
101pub enum SourceCheckInstruction {
102 Field(FieldSourceKey),
104 Literal(ScalarLiteral),
106 Equal,
108 NotEqual,
110 LessThan,
112 LessThanOrEqual,
114 GreaterThan,
116 GreaterThanOrEqual,
118 And,
120 Or,
122 Not,
124 IsNull,
126 IsNotNull,
128 Length,
130}
131
132#[derive(Clone, Debug, Eq, PartialEq)]
134pub struct SourceCheckExpr {
135 instructions: Vec<SourceCheckInstruction>,
136}
137
138impl SourceCheckExpr {
139 pub fn try_new(instructions: Vec<SourceCheckInstruction>) -> Result<Self, SchemaContractError> {
146 let expression = Self { instructions };
147 expression.validate()?;
148 Ok(expression)
149 }
150
151 #[must_use]
153 pub fn instructions(&self) -> &[SourceCheckInstruction] {
154 &self.instructions
155 }
156
157 #[must_use]
159 pub fn dependencies(&self) -> BTreeSet<FieldSourceKey> {
160 self.instructions
161 .iter()
162 .filter_map(|instruction| match instruction {
163 SourceCheckInstruction::Field(field) => Some(field.clone()),
164 _ => None,
165 })
166 .collect()
167 }
168
169 pub(crate) fn validate(&self) -> Result<(), SchemaContractError> {
170 if self.instructions.is_empty() || self.instructions.len() > MAX_SOURCE_CHECK_INSTRUCTIONS {
171 return Err(SchemaContractError::InvalidExpression);
172 }
173 let mut stack_depth = 0usize;
174 for instruction in &self.instructions {
175 match instruction {
176 SourceCheckInstruction::Field(_) => {
177 stack_depth = stack_depth
178 .checked_add(1)
179 .ok_or(SchemaContractError::InvalidExpression)?;
180 }
181 SourceCheckInstruction::Literal(literal) => {
182 literal.validate()?;
183 stack_depth = stack_depth
184 .checked_add(1)
185 .ok_or(SchemaContractError::InvalidExpression)?;
186 }
187 SourceCheckInstruction::Not
188 | SourceCheckInstruction::IsNull
189 | SourceCheckInstruction::IsNotNull
190 | SourceCheckInstruction::Length => {
191 if stack_depth < 1 {
192 return Err(SchemaContractError::InvalidExpression);
193 }
194 }
195 SourceCheckInstruction::Equal
196 | SourceCheckInstruction::NotEqual
197 | SourceCheckInstruction::LessThan
198 | SourceCheckInstruction::LessThanOrEqual
199 | SourceCheckInstruction::GreaterThan
200 | SourceCheckInstruction::GreaterThanOrEqual
201 | SourceCheckInstruction::And
202 | SourceCheckInstruction::Or => {
203 if stack_depth < 2 {
204 return Err(SchemaContractError::InvalidExpression);
205 }
206 stack_depth -= 1;
207 }
208 }
209 }
210 if stack_depth != 1 {
211 return Err(SchemaContractError::InvalidExpression);
212 }
213 Ok(())
214 }
215}