use wdl_ast::Diagnostic;
use super::CallContext;
use super::Callback;
use super::Function;
use super::Signature;
use crate::Array;
use crate::CompoundValue;
use crate::PrimitiveValue;
use crate::Value;
fn keys(context: CallContext<'_>) -> Result<Value, Diagnostic> {
debug_assert_eq!(context.arguments.len(), 1);
debug_assert!(
context.return_type.as_array().is_some(),
"return type should be an array"
);
let elements = match &context.arguments[0].value {
Value::Compound(CompoundValue::Map(map)) => map.keys().map(|k| k.clone().into()).collect(),
Value::Compound(CompoundValue::Object(object)) => object
.keys()
.map(|k| PrimitiveValue::new_string(k).into())
.collect(),
Value::Compound(CompoundValue::Struct(s)) => s
.keys()
.map(|k| PrimitiveValue::new_string(k).into())
.collect(),
_ => unreachable!("expected a map, object, or struct"),
};
Ok(Array::new_unchecked(context.return_type, elements).into())
}
pub const fn descriptor() -> Function {
Function::new(
const {
&[
Signature::new(
"(map: Map[K, V]) -> Array[K] where `K`: any non-optional primitive type",
Callback::Sync(keys),
),
Signature::new(
"(struct: S) -> Array[String] where `S`: any structure",
Callback::Sync(keys),
),
Signature::new("(object: Object) -> Array[String]", Callback::Sync(keys)),
]
},
)
}
#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
use wdl_analysis::types::PrimitiveType;
use wdl_analysis::types::StructType;
use wdl_ast::version::V1;
use crate::v1::test::TestEnv;
use crate::v1::test::eval_v1_expr;
#[tokio::test]
async fn keys() {
let mut env = TestEnv::default();
let ty = StructType::new(
"Foo",
[
("foo", PrimitiveType::Float),
("bar", PrimitiveType::String),
("baz", PrimitiveType::Integer),
],
);
env.insert_struct("Foo", ty);
let value = eval_v1_expr(&env, V1::One, "keys({})").await.unwrap();
assert_eq!(value.unwrap_array().len(), 0);
let value = eval_v1_expr(&env, V1::One, "keys({'foo': 1, 'bar': 2, 'baz': 3})")
.await
.unwrap();
let elements: Vec<_> = value
.as_array()
.unwrap()
.as_slice()
.iter()
.map(|v| v.as_string().unwrap().as_str())
.collect();
assert_eq!(elements, ["foo", "bar", "baz"]);
let value = eval_v1_expr(&env, V1::One, "keys({1: 4, 2: 5, 3: 6})")
.await
.unwrap();
let elements: Vec<_> = value
.as_array()
.unwrap()
.as_slice()
.iter()
.map(|v| v.as_integer().unwrap())
.collect();
assert_eq!(elements, [1, 2, 3]);
let value = eval_v1_expr(&env, V1::Two, "keys(object { foo: 1, bar: 2, baz: 3})")
.await
.unwrap();
let elements: Vec<_> = value
.as_array()
.unwrap()
.as_slice()
.iter()
.map(|v| v.as_string().unwrap().as_str())
.collect();
assert_eq!(elements, ["foo", "bar", "baz"]);
let value = eval_v1_expr(&env, V1::Two, "keys(Foo { foo: 1.0, bar: '2', baz: 3})")
.await
.unwrap();
let elements: Vec<_> = value
.as_array()
.unwrap()
.as_slice()
.iter()
.map(|v| v.as_string().unwrap().as_str())
.collect();
assert_eq!(elements, ["foo", "bar", "baz"]);
}
}