use crate::eval::coercion::to_number;
use crate::eval::functions::check_arity;
use crate::types::{ErrorKind, Value};
fn is_nested(items: &[Value]) -> bool {
!items.is_empty() && matches!(items[0], Value::Array(_))
}
pub fn rows_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 1, 1) {
return err;
}
match &args[0] {
Value::Array(items) => {
if is_nested(items) {
Value::Number(items.len() as f64)
} else {
Value::Number(1.0)
}
}
_ => Value::Number(1.0),
}
}
pub fn columns_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 1, 1) {
return err;
}
match &args[0] {
Value::Array(items) => {
if is_nested(items) {
match &items[0] {
Value::Array(inner) => Value::Number(inner.len() as f64),
_ => Value::Number(1.0),
}
} else {
Value::Number(items.len() as f64)
}
}
_ => Value::Number(1.0),
}
}
pub fn index_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 2, 3) {
return err;
}
let row = match to_number(args[1].clone()) {
Err(e) => return e,
Ok(v) => v as usize,
};
match &args[0] {
Value::Array(items) => {
if is_nested(items) {
let col = if args.len() == 3 {
match to_number(args[2].clone()) {
Err(e) => return e,
Ok(v) => v as usize,
}
} else {
1
};
if row == 0 || col == 0 {
return Value::Error(ErrorKind::Value);
}
let outer_idx = row - 1;
if outer_idx >= items.len() {
return Value::Error(ErrorKind::Ref);
}
match &items[outer_idx] {
Value::Array(inner) => {
let inner_idx = col - 1;
if inner_idx >= inner.len() {
return Value::Error(ErrorKind::Ref);
}
inner[inner_idx].clone()
}
other => other.clone(),
}
} else {
if row == 0 {
return Value::Error(ErrorKind::Value);
}
if args.len() == 3 {
let col = match to_number(args[2].clone()) {
Err(e) => return e,
Ok(v) => v as usize,
};
if row != 1 {
return Value::Error(ErrorKind::Ref);
}
if col == 0 || col > items.len() {
return Value::Error(ErrorKind::Ref);
}
items[col - 1].clone()
} else {
if row > items.len() {
return Value::Error(ErrorKind::Ref);
}
items[row - 1].clone()
}
}
}
other => {
if row != 1 {
return Value::Error(ErrorKind::Ref);
}
if args.len() == 3 {
let col = match to_number(args[2].clone()) {
Err(e) => return e,
Ok(v) => v as usize,
};
if col != 1 {
return Value::Error(ErrorKind::Ref);
}
}
other.clone()
}
}
}