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, 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 Principal(Principal),
47 Subaccount(Subaccount),
49 Text(String),
51 Timestamp(Timestamp),
53 Ulid(Ulid),
55 Unit(Unit),
57}
58
59impl ScalarLiteral {
60 #[must_use]
62 pub const fn kind(&self) -> ScalarKind {
63 match self {
64 Self::Account(_) => ScalarKind::Account,
65 Self::Blob(_) => ScalarKind::Blob,
66 Self::Bool(_) => ScalarKind::Bool,
67 Self::Date(_) => ScalarKind::Date,
68 Self::Decimal(_) => ScalarKind::Decimal,
69 Self::Duration(_) => ScalarKind::Duration,
70 Self::EnumUnit { .. } => ScalarKind::Enum,
71 Self::Float32(_) => ScalarKind::Float32,
72 Self::Float64(_) => ScalarKind::Float64,
73 Self::Int(_) => ScalarKind::Int128,
74 Self::IntBig(_) => ScalarKind::IntBig,
75 Self::Nat(_) => ScalarKind::Nat128,
76 Self::NatBig(_) => ScalarKind::NatBig,
77 Self::Principal(_) => ScalarKind::Principal,
78 Self::Subaccount(_) => ScalarKind::Subaccount,
79 Self::Text(_) => ScalarKind::Text,
80 Self::Timestamp(_) => ScalarKind::Timestamp,
81 Self::Ulid(_) => ScalarKind::Ulid,
82 Self::Unit(_) => ScalarKind::Unit,
83 }
84 }
85
86 pub(crate) fn validate(&self) -> Result<(), SchemaContractError> {
87 match self {
88 Self::Blob(value) if value.len() > MAX_PROPOSAL_LITERAL_BYTES => {
89 Err(SchemaContractError::InvalidLiteral)
90 }
91 Self::Text(value) if value.len() > MAX_PROPOSAL_LITERAL_BYTES => {
92 Err(SchemaContractError::InvalidLiteral)
93 }
94 Self::IntBig(value) if value.to_leb128().len() > MAX_PROPOSAL_LITERAL_BYTES => {
95 Err(SchemaContractError::InvalidLiteral)
96 }
97 Self::NatBig(value) if value.to_leb128().len() > MAX_PROPOSAL_LITERAL_BYTES => {
98 Err(SchemaContractError::InvalidLiteral)
99 }
100 _ => Ok(()),
101 }
102 }
103}
104
105#[derive(Clone, Debug, Eq, PartialEq)]
110pub enum SourceCheckInstruction {
111 Field(FieldSourceKey),
113 Literal(ScalarLiteral),
115 Equal,
117 NotEqual,
119 LessThan,
121 LessThanOrEqual,
123 GreaterThan,
125 GreaterThanOrEqual,
127 And,
129 Or,
131 Not,
133 IsNull,
135 IsNotNull,
137 Length,
139}
140
141#[derive(Clone, Debug, Eq, PartialEq)]
143pub struct SourceCheckExpr {
144 instructions: Vec<SourceCheckInstruction>,
145}
146
147impl SourceCheckExpr {
148 pub fn try_new(instructions: Vec<SourceCheckInstruction>) -> Result<Self, SchemaContractError> {
155 let expression = Self { instructions };
156 expression.validate()?;
157 Ok(expression)
158 }
159
160 #[must_use]
162 pub fn instructions(&self) -> &[SourceCheckInstruction] {
163 &self.instructions
164 }
165
166 #[must_use]
168 pub fn dependencies(&self) -> BTreeSet<FieldSourceKey> {
169 self.instructions
170 .iter()
171 .filter_map(|instruction| match instruction {
172 SourceCheckInstruction::Field(field) => Some(field.clone()),
173 _ => None,
174 })
175 .collect()
176 }
177
178 pub(crate) fn validate(&self) -> Result<(), SchemaContractError> {
179 if self.instructions.is_empty() || self.instructions.len() > MAX_SOURCE_CHECK_INSTRUCTIONS {
180 return Err(SchemaContractError::InvalidExpression);
181 }
182 let mut stack_depth = 0usize;
183 for instruction in &self.instructions {
184 match instruction {
185 SourceCheckInstruction::Field(_) => {
186 stack_depth = stack_depth
187 .checked_add(1)
188 .ok_or(SchemaContractError::InvalidExpression)?;
189 }
190 SourceCheckInstruction::Literal(literal) => {
191 literal.validate()?;
192 stack_depth = stack_depth
193 .checked_add(1)
194 .ok_or(SchemaContractError::InvalidExpression)?;
195 }
196 SourceCheckInstruction::Not
197 | SourceCheckInstruction::IsNull
198 | SourceCheckInstruction::IsNotNull
199 | SourceCheckInstruction::Length => {
200 if stack_depth < 1 {
201 return Err(SchemaContractError::InvalidExpression);
202 }
203 }
204 SourceCheckInstruction::Equal
205 | SourceCheckInstruction::NotEqual
206 | SourceCheckInstruction::LessThan
207 | SourceCheckInstruction::LessThanOrEqual
208 | SourceCheckInstruction::GreaterThan
209 | SourceCheckInstruction::GreaterThanOrEqual
210 | SourceCheckInstruction::And
211 | SourceCheckInstruction::Or => {
212 if stack_depth < 2 {
213 return Err(SchemaContractError::InvalidExpression);
214 }
215 stack_depth -= 1;
216 }
217 }
218 }
219 if stack_depth != 1 {
220 return Err(SchemaContractError::InvalidExpression);
221 }
222 Ok(())
223 }
224}