wdl_engine/stdlib/
find.rs1use regex::Regex;
4use wdl_analysis::types::Optional;
5use wdl_analysis::types::PrimitiveType;
6use wdl_analysis::types::Type;
7use wdl_ast::Diagnostic;
8
9use super::CallContext;
10use super::Callback;
11use super::Function;
12use super::Signature;
13use crate::PrimitiveValue;
14use crate::Value;
15use crate::diagnostics::function_call_failed;
16
17const FUNCTION_NAME: &str = "find";
19
20fn find(context: CallContext<'_>) -> Result<Value, Diagnostic> {
26 debug_assert_eq!(context.arguments.len(), 2);
27 debug_assert!(context.return_type_eq(Type::from(PrimitiveType::String).optional()));
28
29 let input = context
30 .coerce_argument(0, PrimitiveType::String)
31 .unwrap_string();
32 let pattern = context
33 .coerce_argument(1, PrimitiveType::String)
34 .unwrap_string();
35
36 let regex = Regex::new(pattern.as_str())
37 .map_err(|e| function_call_failed(FUNCTION_NAME, &e, context.arguments[1].span))?;
38
39 match regex.find(input.as_str()) {
40 Some(m) => Ok(PrimitiveValue::new_string(m.as_str()).into()),
41 None => Ok(Value::None),
42 }
43}
44
45pub const fn descriptor() -> Function {
47 Function::new(
48 const {
49 &[Signature::new(
50 "(String, String) -> String?",
51 Callback::Sync(find),
52 )]
53 },
54 )
55}
56
57#[cfg(test)]
58mod test {
59 use pretty_assertions::assert_eq;
60 use wdl_ast::version::V1;
61
62 use crate::v1::test::TestEnv;
63 use crate::v1::test::eval_v1_expr;
64
65 #[tokio::test]
66 async fn find() {
67 let env = TestEnv::default();
68 let diagnostic = eval_v1_expr(&env, V1::Two, "find('foo bar baz', '?')")
69 .await
70 .unwrap_err();
71 assert_eq!(
72 diagnostic.message(),
73 "call to function `find` failed: regex parse error:\n ?\n ^\nerror: repetition \
74 operator missing expression"
75 );
76
77 let value = eval_v1_expr(&env, V1::Two, "find('hello world', 'e..o')")
78 .await
79 .unwrap();
80 assert_eq!(value.unwrap_string().as_str(), "ello");
81
82 let value = eval_v1_expr(&env, V1::Two, "find('hello world', 'goodbye')")
83 .await
84 .unwrap();
85 assert!(value.is_none());
86
87 let value = eval_v1_expr(&env, V1::Two, "find('hello\tBob', '\\t')")
88 .await
89 .unwrap();
90 assert_eq!(value.unwrap_string().as_str(), "\t");
91 }
92}