use tan::{
context::Context,
error::Error,
expr::{expr_clone, format_value, Expr},
util::{
args::{unpack_map_arg, unpack_map_mut_arg, unpack_stringable_arg},
module_util::require_module,
},
};
pub fn map_eq(args: &[Expr]) -> Result<Expr, Error> {
let a = unpack_map_arg(args, 0, "a")?;
let b = unpack_map_arg(args, 1, "b")?;
Ok(Expr::Bool(*a == *b))
}
pub fn map_contains_key(args: &[Expr]) -> Result<Expr, Error> {
let [map, key] = args else {
return Err(Error::invalid_arguments(
"requires `this` and `key` argument",
None,
));
};
let Some(items) = map.as_map_mut() else {
return Err(Error::invalid_arguments(
"`map` argument should be a Map",
map.range(),
));
};
let key = format_value(key);
Ok(Expr::Bool(items.contains_key(&key)))
}
pub fn map_put(args: &[Expr]) -> Result<Expr, Error> {
let [map, key, value] = args else {
return Err(Error::invalid_arguments(
"requires `this`, `key`, and `value` arguments",
None,
));
};
let Some(mut items) = map.as_map_mut() else {
return Err(Error::invalid_arguments(
"`map` argument should be a Map",
map.range(),
));
};
let key = format_value(key);
items.insert(key.clone(), value.unpack().clone());
Ok(Expr::None)
}
pub fn map_update_mut(args: &[Expr]) -> Result<Expr, Error> {
let [this, other] = args else {
return Err(Error::invalid_arguments(
"requires `this` and `other` argument",
None,
));
};
let Some(mut this_items) = this.as_map_mut() else {
return Err(Error::invalid_arguments(
"`this` argument should be a Map",
this.range(),
));
};
let Some(other_items) = other.as_map() else {
return Err(Error::invalid_arguments(
"`other` argument should be a Map",
other.range(),
));
};
for (key, value) in other_items.iter() {
this_items.insert(key.clone(), value.clone());
}
Ok(Expr::None)
}
pub fn map_get_or(args: &[Expr]) -> Result<Expr, Error> {
let [map, key, default_value] = args else {
return Err(Error::invalid_arguments(
"requires `this` and `key` argument",
None,
));
};
let Some(items) = map.as_map_mut() else {
return Err(Error::invalid_arguments(
"`map` argument should be a Map",
map.range(),
));
};
let key = format_value(key);
let value = items.get(&key);
if let Some(value) = value {
Ok(expr_clone(value))
} else {
Ok(expr_clone(default_value))
}
}
pub fn map_remove(args: &[Expr]) -> Result<Expr, Error> {
let mut map = unpack_map_mut_arg(args, 0, "map")?;
let key = unpack_stringable_arg(args, 1, "key")?;
let value = map.remove(key);
Ok(value.unwrap_or(Expr::None))
}
pub fn map_get_keys(args: &[Expr]) -> Result<Expr, Error> {
let [map] = args else {
return Err(Error::invalid_arguments("requires `this` argument", None));
};
let Some(items) = map.as_map_mut() else {
return Err(Error::invalid_arguments(
"`map` argument should be a Map",
map.range(),
));
};
let keys: Vec<_> = items.keys().map(Expr::string).collect();
Ok(Expr::array(keys))
}
pub fn map_get_values(args: &[Expr]) -> Result<Expr, Error> {
let [map] = args else {
return Err(Error::invalid_arguments("requires `this` argument", None));
};
let Some(items) = map.as_map_mut() else {
return Err(Error::invalid_arguments(
"`map` argument should be a Map",
map.range(),
));
};
let keys: Vec<_> = items.values().map(expr_clone).collect();
Ok(Expr::array(keys))
}
pub fn map_get_entries(args: &[Expr]) -> Result<Expr, Error> {
let [map] = args else {
return Err(Error::invalid_arguments("requires `this` argument", None));
};
let Some(items) = map.as_map_mut() else {
return Err(Error::invalid_arguments(
"`map` argument should be a Map",
map.range(),
));
};
let entries: Vec<_> = items
.iter()
.map(|(k, v)| Expr::array(vec![Expr::KeySymbol(k.clone()), expr_clone(v)]))
.collect();
Ok(Expr::array(entries))
}
pub fn setup_lib_map(context: &mut Context) {
let module = require_module("prelude", context);
module.insert_invocable("=$$Map$$Map", Expr::foreign_func(&map_eq));
module.insert_invocable("contains-key?", Expr::foreign_func(&map_contains_key));
module.insert_invocable("contains-key", Expr::foreign_func(&map_contains_key));
module.insert_invocable("put", Expr::foreign_func(&map_put));
module.insert_invocable("put$$Map", Expr::foreign_func(&map_put));
module.insert_invocable("update!", Expr::foreign_func(&map_update_mut));
module.insert_invocable("get-or", Expr::foreign_func(&map_get_or));
module.insert_invocable("remove", Expr::foreign_func(&map_remove));
module.insert_invocable("get-keys", Expr::foreign_func(&map_get_keys));
module.insert_invocable("get-values", Expr::foreign_func(&map_get_values));
module.insert_invocable("get-entries", Expr::foreign_func(&map_get_entries));
module.insert_invocable("keys-of", Expr::foreign_func(&map_get_keys));
module.insert_invocable("values-of", Expr::foreign_func(&map_get_values));
module.insert_invocable("entries-of", Expr::foreign_func(&map_get_entries));
}