use quarb::Value;
use quarb::library::{CommandLibrary, LibCall, LibFailure, Synopsis};
use quarb_pyvalue::Reflective;
static REFLECT: Reflective = Reflective::new("numpy", &["numpy.ndarray"], "ndarray", false);
pub struct Numpy;
impl CommandLibrary for Numpy {
fn prefix(&self) -> &str {
"np"
}
fn commands(&self) -> &[Synopsis] {
&[]
}
fn requirements(&self) -> &[&str] {
&["numpy"]
}
fn available(&self) -> Result<(), String> {
quarb_pyvalue::importable(&["numpy"], "numpy")
}
fn resolve(&self, name: &str) -> Result<&Synopsis, String> {
REFLECT.resolve(name)
}
fn call(&self, name: &str, call: LibCall) -> Result<Value, LibFailure> {
let syn = REFLECT.resolve(name).map_err(LibFailure::Unknown)?;
REFLECT.call(syn, name, call)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn nums(v: &[i64]) -> Value {
Value::list(v.iter().map(|&n| Value::Int(n)).collect())
}
fn agg(name: &str, input: Value, operands: Vec<Value>) -> Result<Value, LibFailure> {
Numpy.call(
name,
LibCall {
agg: true,
input: Some(input),
operands,
options: vec![],
},
)
}
#[test]
fn functions_and_methods_by_reflection() {
if Numpy.available().is_err() {
eprintln!("numpy not importable; skipping");
return;
}
assert_eq!(agg("mean", nums(&[1, 2, 3, 6]), vec![]).unwrap(), Value::Float(3.0));
assert_eq!(
agg("percentile", nums(&[1, 2, 3, 4]), vec![Value::Int(50)]).unwrap(),
Value::Float(2.5)
);
assert_eq!(agg("linalg.norm", nums(&[3, 4]), vec![]).unwrap(), Value::Float(5.0));
let t = agg(
"transpose",
Value::list(vec![nums(&[1, 2, 3]), nums(&[4, 5, 6])]),
vec![],
)
.unwrap();
let Value::Table(f) = &t else { panic!("{t:?}") };
assert!(f.header.is_none() && f.kind().starts_with("an array 3×2"), "{}", f.kind());
assert_eq!(
f.rows_value(),
Value::list(vec![nums(&[1, 4]), nums(&[2, 5]), nums(&[3, 6])])
);
let syn = Numpy.resolve("percentile").unwrap();
assert_eq!(syn.operands[0].name, "a");
assert_eq!(syn.operands[1].name, "q");
assert!(syn.options.iter().any(|o| o.long == "method"), "{:?}", syn.options);
let e = Numpy.resolve("meen").unwrap_err();
assert!(e.contains("numpy") && e.contains("mean"), "{e}");
}
}