use crate::eval::functions::check_arity;
use crate::types::{ErrorKind, Value};
pub fn indirect_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 1, 2) {
return err;
}
let ref_text = match &args[0] {
Value::Text(s) => s.clone(),
_ => return Value::Error(ErrorKind::Ref),
};
if is_valid_ref(&ref_text) {
Value::Empty
} else {
Value::Error(ErrorKind::Ref)
}
}
fn is_valid_ref(s: &str) -> bool {
if s.is_empty() {
return false;
}
is_a1_ref(s) || is_r1c1_ref(s)
}
fn is_a1_ref(s: &str) -> bool {
let bytes = s.as_bytes();
let col_end = bytes.iter().take_while(|b| b.is_ascii_alphabetic()).count();
if col_end == 0 {
return false;
}
let rest = &bytes[col_end..];
!rest.is_empty() && rest.iter().all(|b| b.is_ascii_digit())
}
fn is_r1c1_ref(s: &str) -> bool {
let bytes = s.as_bytes();
if bytes.first().map(|b| b.to_ascii_uppercase()) != Some(b'R') {
return false;
}
let rest = &bytes[1..];
let row_end = rest.iter().take_while(|b| b.is_ascii_digit()).count();
if row_end == 0 {
return false;
}
let rest = &rest[row_end..];
if rest.first().map(|b| b.to_ascii_uppercase()) != Some(b'C') {
return false;
}
let rest = &rest[1..];
!rest.is_empty() && rest.iter().all(|b| b.is_ascii_digit())
}
#[cfg(test)]
mod tests;