use std::cmp::Ordering;
use starlark_derive::starlark_module;
use crate as starlark;
use crate::environment::GlobalsBuilder;
use crate::eval::Evaluator;
use crate::values::list::AllocList;
use crate::values::tuple::UnpackTuple;
use crate::values::typing::never::StarlarkNever;
use crate::values::typing::ty::AbstractType;
use crate::values::typing::StarlarkIter;
use crate::values::value_of_unchecked::ValueOfUnchecked;
use crate::values::AllocValue;
use crate::values::FrozenStringValue;
use crate::values::Heap;
use crate::values::Value;
use crate::values::ValueError;
use crate::values::ValueLike;
#[starlark_module]
pub(crate) fn register_other(builder: &mut GlobalsBuilder) {
fn fail(#[starlark(args)] args: UnpackTuple<Value>) -> starlark::Result<StarlarkNever> {
let mut s = String::new();
for x in args.items {
s.push(' ');
match x.unpack_str() {
Some(x) => s.push_str(x),
None => x.collect_repr(&mut s),
}
}
Err(starlark::Error::new_kind(starlark::ErrorKind::Fail(
anyhow::Error::msg(s),
)))
}
#[starlark(speculative_exec_safe)]
fn any<'v>(
#[starlark(require = pos)] x: ValueOfUnchecked<'v, StarlarkIter<Value<'v>>>,
heap: &'v Heap,
) -> starlark::Result<bool> {
for i in x.get().iterate(heap)? {
if i.to_bool() {
return Ok(true);
}
}
Ok(false)
}
#[starlark(speculative_exec_safe)]
fn all<'v>(
#[starlark(require = pos)] x: ValueOfUnchecked<'v, StarlarkIter<Value<'v>>>,
heap: &'v Heap,
) -> starlark::Result<bool> {
for i in x.get().iterate(heap)? {
if !i.to_bool() {
return Ok(false);
}
}
Ok(true)
}
#[starlark(speculative_exec_safe)]
fn dir(#[starlark(require = pos)] x: Value) -> anyhow::Result<Vec<String>> {
Ok(x.dir_attr())
}
#[starlark(speculative_exec_safe)]
fn enumerate<'v>(
#[starlark(require = pos)] it: ValueOfUnchecked<'v, StarlarkIter<Value<'v>>>,
#[starlark(default = 0)] start: i32,
heap: &'v Heap,
) -> starlark::Result<impl AllocValue<'v>> {
let v = it
.get()
.iterate(heap)?
.enumerate()
.map(move |(k, v)| (k as i32 + start, v));
Ok(AllocList(v))
}
#[starlark(speculative_exec_safe)]
fn getattr<'v>(
#[starlark(require = pos)] a: Value<'v>,
#[starlark(require = pos)] attr: &str,
#[starlark(require = pos)] default: Option<Value<'v>>,
heap: &'v Heap,
) -> starlark::Result<Value<'v>> {
match a.get_attr(attr, heap)? {
Some(v) => Ok(v),
None => match default {
Some(x) => Ok(x),
None => ValueError::unsupported_owned(a.get_type(), &format!(".{}", attr), None),
},
}
}
#[starlark(speculative_exec_safe)]
fn hasattr<'v>(
#[starlark(require = pos)] a: Value<'v>,
#[starlark(require = pos)] attr: &str,
heap: &'v Heap,
) -> anyhow::Result<bool> {
Ok(a.has_attr(attr, heap))
}
#[starlark(speculative_exec_safe)]
fn hash(#[starlark(require = pos)] a: &str) -> anyhow::Result<i32> {
#[allow(clippy::never_loop)]
'ascii: loop {
let mut hash = 0i32;
for &b in a.as_bytes() {
if b > 0x7f {
break 'ascii;
}
hash = hash.wrapping_mul(31i32).wrapping_add(b as i32);
}
return Ok(hash);
}
Ok(a.encode_utf16().fold(0i32, |hash: i32, c: u16| {
31i32.wrapping_mul(hash).wrapping_add(c as i32)
}))
}
#[starlark(speculative_exec_safe)]
fn len(#[starlark(require = pos)] a: Value) -> starlark::Result<i32> {
a.length()
}
#[starlark(speculative_exec_safe)]
fn reversed<'v>(
#[starlark(require = pos)] a: ValueOfUnchecked<'v, StarlarkIter<Value<'v>>>,
heap: &'v Heap,
) -> starlark::Result<Vec<Value<'v>>> {
let mut v: Vec<Value> = a.get().iterate(heap)?.collect();
v.reverse();
Ok(v)
}
fn sorted<'v>(
#[starlark(require = pos)] x: ValueOfUnchecked<'v, StarlarkIter<Value<'v>>>,
#[starlark(require = named)] key: Option<Value<'v>>,
#[starlark(require = named, default = false)] reverse: bool,
eval: &mut Evaluator<'v, '_, '_>,
) -> starlark::Result<AllocList<impl IntoIterator<Item = Value<'v>>>> {
let it = x.get().iterate(eval.heap())?;
let mut it = match key {
None => it.map(|x| (x, x)).collect(),
Some(key) => {
let mut v = Vec::new();
for el in it {
v.push((el, key.invoke_pos(&[el], eval)?));
}
v
}
};
let mut compare_ok = Ok(());
it.sort_by(|x: &(Value, Value), y: &(Value, Value)| {
let ord_or_err = if reverse {
x.1.compare(y.1).map(Ordering::reverse)
} else {
x.1.compare(y.1)
};
match ord_or_err {
Ok(r) => r,
Err(e) => {
compare_ok = Err(e);
Ordering::Equal }
}
});
compare_ok?;
Ok(AllocList(it.into_iter().map(|x| x.0)))
}
#[starlark(speculative_exec_safe, as_type = AbstractType)]
fn r#type<'v>(#[starlark(require = pos)] a: Value) -> anyhow::Result<FrozenStringValue> {
Ok(a.get_type_value())
}
}
#[cfg(test)]
mod tests {
use crate::assert;
use crate::assert::Assert;
#[test]
fn test_abs() {
assert::eq("1", "abs(1)");
assert::eq("1", "abs(-1)");
assert::eq("2147483647", "abs(2147483647)");
assert::eq("2147483648", "abs(-2147483648)");
assert::eq("2147483648000", "abs(2147483648000)");
assert::eq("2147483648000", "abs(-2147483648000)");
assert::eq("1.23", "abs(-1.23)");
assert::eq("2.3", "abs(2.3)");
assert::is_true("isinstance(abs(1), int)");
}
#[test]
fn test_constants() {
assert::is_true("not None");
assert::is_true("not False");
assert::is_true("True");
}
#[test]
fn test_chr() {
assert::fail("chr(0x110000)", "not a valid UTF-8");
assert::fail("chr(-1)", "negative");
}
#[test]
fn test_hash() {
assert::eq("0", "hash('')");
assert::eq("97", "hash('a')");
assert::eq("3105", "hash('ab')");
assert::eq("96354", "hash('abc')");
assert::eq("2987074", "hash('abcd')");
assert::eq("92599395", "hash('abcde')");
assert::eq("-1424385949", "hash('abcdef')");
assert::all_true(
r#"
hash("te") == hash("te")
hash("te") != hash("st")
x = "test"; y = "te" + "st"; hash(y) == hash(y)
"#,
);
assert::fail("noop(hash)(None)", "doesn't match");
assert::fail("noop(hash)(True)", "doesn't match");
assert::fail("noop(hash)(1)", "doesn't match");
assert::fail("noop(hash)([])", "doesn't match");
assert::fail("noop(hash)({})", "doesn't match");
assert::fail("noop(hash)(range(1))", "doesn't match");
assert::fail("noop(hash)((1, 2))", "doesn't match");
assert::fail(
r#"
def foo():
pass
noop(hash)(foo)
"#,
"doesn't match",
);
}
#[test]
fn test_int() {
assert::eq("2147483647", "int('2147483647')");
assert::eq("-2147483647 - 1", "int('-2147483648')");
assert::eq("0", "int('0')");
assert::eq("0", "int('-0')");
assert::eq(
"999999999999999945322333868247445125709646570021247924665841614848",
"int(1e66)",
);
assert::eq("2147483648", "int('2147483648')");
assert::eq("-2147483649", "int('-2147483649')");
}
#[test]
fn test_tuple() {
let mut a = Assert::new();
a.disable_static_typechecking();
a.eq("(1, 2)", "tuple((1, 2))");
a.eq("(1, 2)", "tuple([1, 2])");
}
}