use std::fmt;
use indexmap::IndexMap;
use wdl_ast::Diagnostic;
use super::CallContext;
use super::Callback;
use super::Function;
use super::Signature;
use crate::Map;
use crate::PrimitiveValue;
use crate::Value;
use crate::diagnostics::function_call_failed;
const FUNCTION_NAME: &str = "as_map";
struct DuplicateKeyError(PrimitiveValue);
impl fmt::Display for DuplicateKeyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"array contains a duplicate entry for map key `{v}`",
v = self.0.raw(None)
)
}
}
fn as_map(context: CallContext<'_>) -> Result<Value, Diagnostic> {
debug_assert_eq!(context.arguments.len(), 1);
debug_assert!(
context.return_type.as_map().is_some(),
"return type should be a map"
);
let array = context.arguments[0]
.value
.as_array()
.expect("argument should be an array");
let mut elements = IndexMap::with_capacity(array.len());
for e in array.as_slice() {
let pair = e.as_pair().expect("element should be a pair");
let key = match pair.left() {
Value::Primitive(v) => v.clone(),
_ => unreachable!("expected a primitive type for the left value"),
};
match elements.entry(key) {
indexmap::map::Entry::Occupied(e) => {
return Err(function_call_failed(
FUNCTION_NAME,
DuplicateKeyError(e.key().clone()),
context.arguments[0].span,
));
}
indexmap::map::Entry::Vacant(e) => {
e.insert(pair.right().clone());
}
}
}
Ok(Map::new_unchecked(context.return_type, elements).into())
}
pub const fn descriptor() -> Function {
Function::new(
const {
&[Signature::new(
"(pairs: Array[Pair[K, V]]) -> Map[K, V] where `K`: any non-optional primitive \
type",
Callback::Sync(as_map),
)]
},
)
}
#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
use wdl_ast::version::V1;
use crate::v1::test::TestEnv;
use crate::v1::test::eval_v1_expr;
#[tokio::test]
async fn as_map() {
let env = TestEnv::default();
let value = eval_v1_expr(&env, V1::One, "as_map([])").await.unwrap();
assert_eq!(value.unwrap_map().len(), 0);
let value = eval_v1_expr(&env, V1::One, "as_map([('foo', 'bar'), ('bar', 'baz')])")
.await
.unwrap();
let elements: Vec<_> = value
.as_map()
.unwrap()
.iter()
.map(|(k, v)| {
(
k.as_string().unwrap().as_str(),
v.as_string().unwrap().as_str(),
)
})
.collect();
assert_eq!(elements, [("foo", "bar"), ("bar", "baz")]);
let value = eval_v1_expr(&env, V1::One, "as_map([('a', 1), ('c', 3), ('b', 2)])")
.await
.unwrap();
let elements: Vec<_> = value
.as_map()
.unwrap()
.iter()
.map(|(k, v)| (k.as_string().unwrap().as_str(), v.as_integer().unwrap()))
.collect();
assert_eq!(elements, [("a", 1), ("c", 3), ("b", 2)]);
let value = eval_v1_expr(&env, V1::One, "as_map(as_pairs({'a': 1, 'c': 3, 'b': 2}))")
.await
.unwrap();
let elements: Vec<_> = value
.as_map()
.unwrap()
.iter()
.map(|(k, v)| (k.as_string().unwrap().as_str(), v.as_integer().unwrap()))
.collect();
assert_eq!(elements, [("a", 1), ("c", 3), ("b", 2)]);
let diagnostic = eval_v1_expr(&env, V1::One, "as_map([('a', 1), ('c', 3), ('a', 2)])")
.await
.unwrap_err();
assert_eq!(
diagnostic.message(),
"call to function `as_map` failed: array contains a duplicate entry for map key `a`"
);
}
}