1use crate::{
34 error::{
35 parse::validate::{ExpressionKind, InvalidVariableExpression, ValidationError},
36 utils::MetaData,
37 },
38 knot::Address,
39 line::{Expression, Variable},
40 process::check_condition,
41 story::validate::{ValidateContent, ValidationData},
42};
43
44use std::{cmp::Ordering, error::Error};
45
46#[cfg(feature = "serde_support")]
47use crate::utils::OrderingDerive;
48#[cfg(feature = "serde_support")]
49use serde::{Deserialize, Serialize};
50
51#[derive(Clone, Debug, PartialEq)]
52#[cfg_attr(feature = "serde_support", derive(Deserialize, Serialize))]
53pub struct Condition {
55 pub root: ConditionItem,
57 pub items: Vec<AndOr>,
59}
60
61#[derive(Clone, Debug, PartialEq)]
62#[cfg_attr(feature = "serde_support", derive(Deserialize, Serialize))]
63pub struct ConditionItem {
75 pub negate: bool,
77 pub kind: ConditionKind,
79}
80
81#[derive(Clone, Debug, PartialEq)]
82#[cfg_attr(feature = "serde_support", derive(Deserialize, Serialize))]
83pub enum ConditionKind {
85 True,
87 False,
89 Nested(Box<Condition>),
91 Single(StoryCondition),
93}
94
95#[derive(Clone, Debug, PartialEq)]
96#[cfg_attr(feature = "serde_support", derive(Deserialize, Serialize))]
97pub enum StoryCondition {
99 Comparison {
108 lhs_variable: Expression,
110 rhs_variable: Expression,
112 #[cfg_attr(feature = "serde_support", serde(with = "OrderingDerive"))]
117 ordering: Ordering,
118 },
119 IsTrueLike { variable: Variable },
133}
134
135#[derive(Clone, Debug, PartialEq)]
136#[cfg_attr(feature = "serde_support", derive(Deserialize, Serialize))]
137pub enum AndOr {
139 And(ConditionItem),
140 Or(ConditionItem),
141}
142
143impl Condition {
144 pub fn evaluate<F, E>(&self, evaluator: &F) -> Result<bool, E>
149 where
150 F: Fn(&StoryCondition) -> Result<bool, E>,
151 E: Error,
152 {
153 self.items
154 .iter()
155 .fold(inner_eval(&self.root, evaluator), |acc, next_condition| {
156 acc.and_then(|current| match next_condition {
157 AndOr::And(item) => inner_eval(item, evaluator).map(|next| current && next),
158 AndOr::Or(item) => inner_eval(item, evaluator).map(|next| current || next),
159 })
160 })
161 }
162}
163
164fn inner_eval<F, E>(item: &ConditionItem, evaluator: &F) -> Result<bool, E>
166where
167 F: Fn(&StoryCondition) -> Result<bool, E>,
168 E: Error,
169{
170 let mut result = match &item.kind {
171 ConditionKind::True => Ok(true),
172 ConditionKind::False => Ok(false),
173 ConditionKind::Nested(condition) => condition.evaluate(evaluator),
174 ConditionKind::Single(ref kind) => evaluator(kind),
175 }?;
176
177 if item.negate {
178 result = !result;
179 }
180
181 Ok(result)
182}
183
184pub struct ConditionBuilder {
186 root: ConditionItem,
187 items: Vec<AndOr>,
188}
189
190impl ConditionBuilder {
191 pub fn from_kind(kind: &ConditionKind, negate: bool) -> Self {
193 let root = ConditionItem {
194 kind: kind.clone(),
195 negate,
196 };
197
198 ConditionBuilder {
199 root,
200 items: Vec::new(),
201 }
202 }
203
204 pub fn build(self) -> Condition {
206 Condition {
207 root: self.root,
208 items: self.items,
209 }
210 }
211
212 pub fn and(&mut self, kind: &ConditionKind, negate: bool) {
214 self.items.push(AndOr::And(ConditionItem {
215 kind: kind.clone(),
216 negate,
217 }));
218 }
219
220 pub fn or(&mut self, kind: &ConditionKind, negate: bool) {
222 self.items.push(AndOr::Or(ConditionItem {
223 kind: kind.clone(),
224 negate,
225 }));
226 }
227
228 pub fn extend(&mut self, items: &[AndOr]) {
230 self.items.extend_from_slice(items);
231 }
232}
233
234impl ValidateContent for Condition {
235 fn validate(
236 &mut self,
237 error: &mut ValidationError,
238 current_location: &Address,
239 meta_data: &MetaData,
240 data: &ValidationData,
241 ) {
242 let num_errors = error.num_errors();
243
244 self.root
245 .kind
246 .validate(error, current_location, meta_data, data);
247
248 self.items.iter_mut().for_each(|item| match item {
249 AndOr::And(item) | AndOr::Or(item) => {
250 item.kind.validate(error, current_location, meta_data, data)
251 }
252 });
253
254 if num_errors == error.num_errors() {
255 if let Err(err) = check_condition(self, &data.follow_data) {
256 error.variable_errors.push(InvalidVariableExpression {
257 expression_kind: ExpressionKind::Condition,
258 kind: err.into(),
259 meta_data: meta_data.clone(),
260 });
261 }
262 }
263 }
264}
265
266impl ValidateContent for ConditionKind {
267 fn validate(
268 &mut self,
269 error: &mut ValidationError,
270 current_location: &Address,
271 meta_data: &MetaData,
272 data: &ValidationData,
273 ) {
274 match self {
275 ConditionKind::True | ConditionKind::False => (),
276 ConditionKind::Nested(condition) => {
277 condition.validate(error, current_location, meta_data, data)
278 }
279 ConditionKind::Single(kind) => kind.validate(error, current_location, meta_data, data),
280 }
281 }
282}
283
284impl ValidateContent for StoryCondition {
285 fn validate(
286 &mut self,
287 error: &mut ValidationError,
288 current_location: &Address,
289 meta_data: &MetaData,
290 data: &ValidationData,
291 ) {
292 match self {
293 StoryCondition::Comparison {
294 ref mut lhs_variable,
295 ref mut rhs_variable,
296 ..
297 } => {
298 lhs_variable.validate(error, current_location, meta_data, data);
299 rhs_variable.validate(error, current_location, meta_data, data);
300 }
301 StoryCondition::IsTrueLike { variable } => {
302 variable.validate(error, current_location, meta_data, data)
303 }
304 }
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311
312 use std::fmt;
313
314 use ConditionKind::{False, True};
315
316 impl From<StoryCondition> for Condition {
317 fn from(kind: StoryCondition) -> Self {
318 ConditionBuilder::from_kind(&kind.into(), false).build()
319 }
320 }
321
322 impl From<StoryCondition> for ConditionKind {
323 fn from(kind: StoryCondition) -> Self {
324 ConditionKind::Single(kind)
325 }
326 }
327
328 impl From<&StoryCondition> for ConditionKind {
329 fn from(kind: &StoryCondition) -> Self {
330 ConditionKind::Single(kind.clone())
331 }
332 }
333
334 impl Condition {
335 pub fn story_condition(&self) -> &StoryCondition {
336 &self.root.kind.story_condition()
337 }
338
339 pub fn with_and(mut self, kind: ConditionKind) -> Self {
340 let item = ConditionItem {
341 kind,
342 negate: false,
343 };
344
345 self.items.push(AndOr::And(item));
346 self
347 }
348
349 pub fn with_or(mut self, kind: ConditionKind) -> Self {
350 let item = ConditionItem {
351 kind,
352 negate: false,
353 };
354
355 self.items.push(AndOr::Or(item));
356 self
357 }
358 }
359
360 impl ConditionKind {
361 pub fn nested(&self) -> &Condition {
362 match self {
363 ConditionKind::Nested(condition) => condition,
364 other => panic!(
365 "tried to extract nested `Condition`, but item was not `ConditionKind::Nested` \
366 (was: {:?})",
367 other
368 ),
369 }
370 }
371
372 pub fn story_condition(&self) -> &StoryCondition {
373 match self {
374 ConditionKind::Single(story_condition) => story_condition,
375 other => panic!(
376 "tried to extract `StoryCondition`, but item was not `ConditionKind::Single` \
377 (was: {:?})",
378 other
379 ),
380 }
381 }
382 }
383
384 impl AndOr {
385 pub fn nested(&self) -> &Condition {
386 match self {
387 AndOr::And(item) | AndOr::Or(item) => item.kind.nested(),
388 }
389 }
390
391 pub fn story_condition(&self) -> &StoryCondition {
392 match self {
393 AndOr::And(item) | AndOr::Or(item) => item.kind.story_condition(),
394 }
395 }
396
397 pub fn is_and(&self) -> bool {
398 match self {
399 AndOr::And(..) => true,
400 _ => false,
401 }
402 }
403
404 pub fn is_or(&self) -> bool {
405 match self {
406 AndOr::Or(..) => true,
407 _ => false,
408 }
409 }
410 }
411
412 #[derive(Debug)]
413 struct MockError;
414
415 impl fmt::Display for MockError {
416 fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
417 unreachable!();
418 }
419 }
420
421 impl Error for MockError {}
422
423 #[test]
424 fn condition_links_from_left_to_right() {
425 let f = |kind: &StoryCondition| match kind {
426 _ => Err(MockError),
427 };
428
429 assert!(ConditionBuilder::from_kind(&True.into(), false)
430 .build()
431 .evaluate(&f)
432 .unwrap());
433
434 assert!(!ConditionBuilder::from_kind(&False.into(), false)
435 .build()
436 .evaluate(&f)
437 .unwrap());
438
439 assert!(ConditionBuilder::from_kind(&True.into(), false)
440 .build()
441 .with_and(True.into())
442 .evaluate(&f)
443 .unwrap());
444
445 assert!(!ConditionBuilder::from_kind(&True.into(), false)
446 .build()
447 .with_and(False.into())
448 .evaluate(&f)
449 .unwrap());
450
451 assert!(ConditionBuilder::from_kind(&False.into(), false)
452 .build()
453 .with_and(False.into())
454 .with_or(True)
455 .evaluate(&f)
456 .unwrap());
457
458 assert!(!ConditionBuilder::from_kind(&False.into(), false)
459 .build()
460 .with_and(False)
461 .with_or(True)
462 .with_and(False)
463 .evaluate(&f)
464 .unwrap());
465 }
466
467 #[test]
468 fn conditions_can_be_negated() {
469 let f = |kind: &StoryCondition| match kind {
470 _ => Err(MockError),
471 };
472
473 assert!(ConditionBuilder::from_kind(&False.into(), true)
474 .build()
475 .evaluate(&f)
476 .unwrap());
477 }
478}