use std::cmp::Ordering;
use starlark_derive::starlark_module;
use crate as starlark;
use crate::environment::GlobalsBuilder;
use crate::eval::Evaluator;
use crate::values::tuple::UnpackTuple;
use crate::values::Value;
fn min_max_iter<'v>(
mut it: impl Iterator<Item = Value<'v>>,
key: Option<Value<'v>>,
eval: &mut Evaluator<'v, '_, '_>,
min: bool,
) -> crate::Result<Value<'v>> {
let mut max = match it.next() {
Some(x) => x,
None => {
return Err(anyhow::anyhow!(
"Argument is an empty iterable, max() expect a non empty iterable"
)
.into());
}
};
let update_max_ordering = if min {
Ordering::Greater
} else {
Ordering::Less
};
match key {
None => {
for i in it {
if max.compare(i)? == update_max_ordering {
max = i;
}
}
}
Some(key) => {
let mut cached = key.invoke_pos(&[max], eval)?;
for i in it {
let keyi = key.invoke_pos(&[i], eval)?;
if cached.compare(keyi)? == update_max_ordering {
max = i;
cached = keyi;
}
}
}
};
Ok(max)
}
fn min_max<'v>(
mut args: UnpackTuple<Value<'v>>,
key: Option<Value<'v>>,
eval: &mut Evaluator<'v, '_, '_>,
min: bool,
) -> crate::Result<Value<'v>> {
if args.items.len() == 1 {
let it = args.items.swap_remove(0).iterate(eval.heap())?;
min_max_iter(it, key, eval, min)
} else {
min_max_iter(args.items.into_iter(), key, eval, min)
}
}
#[starlark_module]
pub(crate) fn register_min_max(globals: &mut GlobalsBuilder) {
#[starlark(speculative_exec_safe)]
fn max<'v>(
#[starlark(args)] args: UnpackTuple<Value<'v>>,
key: Option<Value<'v>>,
eval: &mut Evaluator<'v, '_, '_>,
) -> starlark::Result<Value<'v>> {
min_max(args, key, eval, false)
}
#[starlark(speculative_exec_safe)]
fn min<'v>(
#[starlark(args)] args: UnpackTuple<Value<'v>>,
key: Option<Value<'v>>,
eval: &mut Evaluator<'v, '_, '_>,
) -> starlark::Result<Value<'v>> {
min_max(args, key, eval, true)
}
}