use std::fmt;
use itertools::Itertools;
use starlark_derive::starlark_module;
use crate as starlark;
use crate::environment::GlobalsBuilder;
use crate::eval::Evaluator;
use crate::values::function::StarlarkFunction;
use crate::values::none::NoneOr;
use crate::values::none::NoneType;
use crate::values::tuple::UnpackTuple;
use crate::values::typing::iter::StarlarkIter;
use crate::values::Value;
use crate::values::ValueOfUnchecked;
#[starlark_module]
pub fn filter(builder: &mut GlobalsBuilder) {
fn filter<'v>(
#[starlark(require = pos)] func: NoneOr<ValueOfUnchecked<'v, StarlarkFunction>>,
#[starlark(require = pos)] seq: ValueOfUnchecked<'v, StarlarkIter<Value<'v>>>,
eval: &mut Evaluator<'v, '_>,
) -> starlark::Result<Vec<Value<'v>>> {
let mut res = Vec::new();
for v in seq.get().iterate(eval.heap())? {
match func {
NoneOr::None => {
if !v.is_none() {
res.push(v);
}
}
NoneOr::Other(func) => {
if func.get().invoke_pos(&[v], eval)?.to_bool() {
res.push(v);
}
}
}
}
Ok(res)
}
}
#[starlark_module]
pub fn map(builder: &mut GlobalsBuilder) {
fn map<'v>(
#[starlark(require = pos)] func: ValueOfUnchecked<'v, StarlarkFunction>,
#[starlark(require = pos)] seq: ValueOfUnchecked<'v, StarlarkIter<Value<'v>>>,
eval: &mut Evaluator<'v, '_>,
) -> starlark::Result<Vec<Value<'v>>> {
let it = seq.get().iterate(eval.heap())?;
let mut res = Vec::with_capacity(it.size_hint().0);
for v in it {
res.push(func.get().invoke_pos(&[v], eval)?);
}
Ok(res)
}
}
#[starlark_module]
pub fn debug(builder: &mut GlobalsBuilder) {
fn debug(#[starlark(require = pos)] val: Value) -> anyhow::Result<String> {
Ok(format!("{:?}", val))
}
}
struct PrintWrapper<'a, 'b>(&'a Vec<Value<'b>>);
impl fmt::Display for PrintWrapper<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, v) in self.0.iter().enumerate() {
if i != 0 {
f.write_str(" ")?;
}
fmt::Display::fmt(v, f)?;
}
Ok(())
}
}
pub trait PrintHandler {
fn println(&self, text: &str) -> anyhow::Result<()>;
}
pub(crate) struct StderrPrintHandler;
impl PrintHandler for StderrPrintHandler {
fn println(&self, text: &str) -> anyhow::Result<()> {
eprintln!("{}", text);
Ok(())
}
}
#[starlark_module]
pub fn print(builder: &mut GlobalsBuilder) {
fn print(
#[starlark(args)] args: UnpackTuple<Value>,
eval: &mut Evaluator,
) -> anyhow::Result<NoneType> {
eval.print_handler
.println(&args.items.iter().map(|x| x.to_str()).join(" "))?;
Ok(NoneType)
}
}
#[starlark_module]
pub fn pprint(builder: &mut GlobalsBuilder) {
fn pprint(
#[starlark(args)] args: UnpackTuple<Value>,
eval: &mut Evaluator,
) -> anyhow::Result<NoneType> {
eval.print_handler
.println(&format!("{:#}", PrintWrapper(&args.items)))?;
Ok(NoneType)
}
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use std::rc::Rc;
use dupe::Dupe;
use crate::assert;
use crate::assert::Assert;
use crate::stdlib::PrintHandler;
#[test]
fn test_filter() {
let mut a = Assert::new();
a.disable_static_typechecking();
a.pass(
r#"
def contains_hello(s):
if "hello" in s:
return True
return False
def positive(i):
return i > 0
assert_eq([], filter(positive, []))
assert_eq([1, 2, 3], filter(positive, [1, 2, 3]))
assert_eq([], filter(positive, [-1, -2, -3]))
assert_eq([1, 2, 3], filter(positive, [-1, 1, 2, -2, -3, 3]))
assert_eq(["hello world!"], filter(contains_hello, ["hello world!", "goodbye"]))
"#,
);
}
#[test]
fn test_map() {
let mut a = Assert::new();
a.disable_static_typechecking();
a.pass(
r#"
def double(x):
return x + x
assert_eq([], map(int, []))
assert_eq([1,2,3], map(int, ["1","2","3"]))
assert_eq(["0","1","2"], map(str, range(3)))
assert_eq(["11",8], map(double, ["1",4]))
"#,
);
}
#[test]
fn test_debug() {
assert::pass(
r#"assert_eq(
debug([1,2]),
"Value(ListGen(ListData { content: Cell { value: ValueTyped(Value(Array { len: 2, capacity: 2, iter_count: 0, content: [Value(1), Value(2)] })) } }))"
)"#,
);
}
#[test]
fn test_print() {
let s = Rc::new(RefCell::new(String::new()));
let s_copy = s.dupe();
struct PrintHandlerImpl {
s: Rc<RefCell<String>>,
}
impl PrintHandler for PrintHandlerImpl {
fn println(&self, s: &str) -> anyhow::Result<()> {
*self.s.borrow_mut() = s.to_owned();
Ok(())
}
}
let print_handler = PrintHandlerImpl { s: s.dupe() };
let mut a = Assert::new();
a.set_print_handler(&print_handler);
a.pass("print('hw')");
assert_eq!("hw", s_copy.borrow().as_str());
}
}