Skip to main content

kcl_lib/std/
fail.rs

1//! Standard library support for user-defined failures.
2
3use anyhow::Result;
4
5use crate::errors::KclError;
6use crate::errors::KclErrorDetails;
7use crate::execution::ExecState;
8use crate::execution::KclValue;
9use crate::execution::types::RuntimeType;
10use crate::std::Args;
11
12pub async fn fail(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
13    let message = args.get_unlabeled_kw_arg("msg", &RuntimeType::string(), exec_state)?;
14
15    // The call expression adds its source range while unwinding. Starting with
16    // no ranges records the `fail(...)` call exactly once.
17    Err(KclError::new_user_defined(KclErrorDetails::new(message, Vec::new())))
18}
19
20#[cfg(test)]
21mod tests {
22    use crate::docs::kcl_doc::ArgKind;
23    use crate::docs::kcl_doc::DocData;
24    use crate::docs::kcl_doc::walk_prelude;
25
26    #[test]
27    fn fail_prelude_contract_is_added_in_v3_and_returns_never() {
28        let prelude = walk_prelude();
29        let fail = prelude.find_by_name("fail").unwrap();
30
31        assert!(!fail.is_experimental());
32        assert_eq!(
33            fail.properties().added_in.as_ref().map(ToString::to_string).as_deref(),
34            Some("3.0")
35        );
36        let DocData::Fn(fail) = fail else {
37            panic!("expected fail to be a function, found {fail:?}");
38        };
39        assert_eq!(fail.return_type.as_deref(), Some("never"));
40        assert_eq!(fail.args.len(), 1);
41        assert_eq!(fail.args[0].name, "msg");
42        assert_eq!(fail.args[0].ty.as_deref(), Some("string"));
43        assert!(matches!(fail.args[0].kind, ArgKind::Special));
44    }
45}