1use crate::compiler::prelude::*;
2
3fn assert(condition: Value, message: Option<Value>, format: Option<String>) -> Resolved {
4 if condition.try_boolean()? {
5 Ok(true.into())
6 } else if let Some(message) = message {
7 let message = message.try_bytes_utf8_lossy()?.into_owned();
8 Err(ExpressionError::Error {
9 message: message.clone(),
10 labels: vec![],
11 notes: vec![Note::UserErrorMessage(message)],
12 })
13 } else {
14 let message = match format {
15 Some(string) => format!("assertion failed: {string}"),
16 None => "assertion failed".to_owned(),
17 };
18 Err(ExpressionError::from(message))
19 }
20}
21
22#[derive(Clone, Copy, Debug)]
23pub struct Assert;
24
25impl Function for Assert {
26 fn identifier(&self) -> &'static str {
27 "assert"
28 }
29
30 fn usage(&self) -> &'static str {
31 "Asserts the `condition`, which must be a Boolean expression. The program is aborted with `message` if the condition evaluates to `false`."
32 }
33
34 fn category(&self) -> &'static str {
35 Category::Debug.as_ref()
36 }
37
38 fn internal_failure_reasons(&self) -> &'static [&'static str] {
39 &["`condition` evaluates to `false`."]
40 }
41
42 fn return_kind(&self) -> u16 {
43 kind::BOOLEAN
44 }
45
46 fn notices(&self) -> &'static [&'static str] {
47 &[indoc! {"
48 The `assert` function should be used in a standalone fashion and only when you want
49 to abort the program. You should avoid it in logical expressions and other situations
50 in which you want the program to continue if the condition evaluates to `false`.
51 "}]
52 }
53
54 fn pure(&self) -> bool {
55 false
56 }
57
58 fn parameters(&self) -> &'static [Parameter] {
59 const PARAMETERS: &[Parameter] = &[
60 Parameter::required("condition", kind::BOOLEAN, "The condition to check."),
61 Parameter::optional(
62 "message",
63 kind::BYTES,
64 "An optional custom error message. If the equality assertion fails, `message` is
65appended to the default message prefix. See the [examples](#assert-examples) below
66for a fully formed log message sample.",
67 ),
68 ];
69 PARAMETERS
70 }
71
72 fn examples(&self) -> &'static [Example] {
73 &[
74 example! {
75 title: "Assertion (true) - with message",
76 source: r#"assert!("foo" == "foo", message: "\"foo\" must be \"foo\"!")"#,
77 result: Ok("true"),
78 },
79 example! {
80 title: "Assertion (false) - with message",
81 source: r#"assert!("foo" == "bar", message: "\"foo\" must be \"foo\"!")"#,
82 result: Err(r#"function call error for "assert" at (0:60): "foo" must be "foo"!"#),
83 },
84 example! {
85 title: "Assertion (false) - simple",
86 source: "assert!(false)",
87 result: Err(r#"function call error for "assert" at (0:14): assertion failed"#),
88 },
89 ]
90 }
91
92 fn compile(
93 &self,
94 _state: &state::TypeState,
95 _ctx: &mut FunctionCompileContext,
96 arguments: ArgumentList,
97 ) -> Compiled {
98 let condition = arguments.required("condition");
99 let message = arguments.optional("message");
100
101 Ok(AssertFn { condition, message }.as_expr())
102 }
103}
104
105#[derive(Debug, Clone)]
106struct AssertFn {
107 condition: Box<dyn Expression>,
108 message: Option<Box<dyn Expression>>,
109}
110
111impl FunctionExpression for AssertFn {
112 fn resolve(&self, ctx: &mut Context) -> Resolved {
113 let condition = self.condition.resolve(ctx)?;
114 let format = self.condition.format();
115 let message = self.message.as_ref().map(|m| m.resolve(ctx)).transpose()?;
116
117 assert(condition, message, format)
118 }
119
120 fn type_def(&self, _: &state::TypeState) -> TypeDef {
121 TypeDef::boolean().fallible()
122 }
123}
124
125impl fmt::Display for AssertFn {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 f.write_str("")
128 }
129}