use crate::types::{ErrorKind, Value, ZonedInstant};
pub fn zoned_extreme(args: &[Value], want_min: bool) -> Option<Value> {
fn walk(
v: &Value,
best: &mut Option<ZonedInstant>,
saw_zoned: &mut bool,
saw_numeric: &mut bool,
error: &mut Option<Value>,
want_min: bool,
) {
match v {
Value::Zoned(z) => {
*saw_zoned = true;
let take = match best.as_ref() {
None => true,
Some(c) => {
if want_min {
z.utc_nanos < c.utc_nanos
} else {
z.utc_nanos > c.utc_nanos
}
}
};
if take {
*best = Some((**z).clone());
}
}
Value::Number(_) | Value::Date(_) | Value::Bool(_) | Value::Text(_) => *saw_numeric = true,
Value::Error(e) => {
if error.is_none() {
*error = Some(Value::Error(e.clone()));
}
}
Value::Empty => {}
Value::Array(elems) => {
for e in elems {
walk(e, best, saw_zoned, saw_numeric, error, want_min);
}
}
}
}
let mut best: Option<ZonedInstant> = None;
let mut saw_zoned = false;
let mut saw_numeric = false;
let mut error: Option<Value> = None;
for a in args {
walk(a, &mut best, &mut saw_zoned, &mut saw_numeric, &mut error, want_min);
}
if !saw_zoned {
return None;
}
if let Some(e) = error {
return Some(e);
}
if saw_numeric {
return Some(Value::Error(ErrorKind::Value));
}
best.map(|z| Value::Zoned(Box::new(z)))
}
pub fn collect_nums(args: &[Value]) -> Vec<f64> {
let mut nums = Vec::new();
collect_nums_into(args, &mut nums);
nums
}
fn collect_nums_into(args: &[Value], out: &mut Vec<f64>) {
for arg in args {
match arg {
Value::Number(n) => out.push(*n),
Value::Date(n) => out.push(*n),
Value::Array(inner) => collect_nums_into(inner, out),
_ => {}
}
}
}
fn collect_nums_into_checked(args: &[Value], out: &mut Vec<f64>) -> Option<Value> {
for arg in args {
match arg {
Value::Number(n) => out.push(*n),
Value::Date(n) => out.push(*n),
Value::Array(inner) => {
if let Some(err) = collect_nums_into_checked(inner, out) {
return Some(err);
}
}
Value::Error(e) => return Some(Value::Error(e.clone())),
_ => {}
}
}
None
}
fn collect_nums_a_into_checked(args: &[Value], out: &mut Vec<f64>) -> Option<Value> {
for arg in args {
match arg {
Value::Number(n) => out.push(*n),
Value::Date(n) => out.push(*n),
Value::Bool(b) => out.push(if *b { 1.0 } else { 0.0 }),
Value::Text(_) => out.push(0.0),
Value::Array(inner) => {
if let Some(err) = collect_nums_a_into_checked(inner, out) {
return Some(err);
}
}
Value::Error(e) => return Some(Value::Error(e.clone())),
Value::Empty => {}
Value::Zoned(_) => {}
}
}
None
}
pub fn collect_numbers_checked(v: &Value) -> Result<Vec<f64>, Value> {
match v {
Value::Array(arr) => {
let mut nums = Vec::new();
if let Some(err) = collect_nums_into_checked(arr, &mut nums) {
return Err(err);
}
Ok(nums)
}
Value::Number(n) => Ok(vec![*n]),
Value::Error(e) => Err(Value::Error(e.clone())),
_ => Ok(vec![]),
}
}
pub fn collect_nums_a_checked(args: &[Value]) -> Result<Vec<f64>, Value> {
let mut nums = Vec::new();
for arg in args {
match arg {
Value::Number(n) => nums.push(*n),
Value::Date(n) => nums.push(*n),
Value::Bool(b) => nums.push(if *b { 1.0 } else { 0.0 }),
Value::Text(s) => {
let trimmed = s.trim();
if trimmed.is_empty() {
nums.push(0.0);
} else {
match trimmed.parse::<f64>() {
Ok(v) if v.is_finite() => nums.push(v),
_ => return Err(Value::Error(ErrorKind::Value)),
}
}
}
Value::Empty => {}
Value::Array(inner) => {
if let Some(err) = collect_nums_a_into_checked(inner, &mut nums) {
return Err(err);
}
}
Value::Zoned(_) => return Err(Value::Error(ErrorKind::Value)),
Value::Error(e) => return Err(Value::Error(e.clone())),
}
}
Ok(nums)
}
pub fn collect_nums_direct(args: &[Value]) -> Result<Vec<f64>, Value> {
let mut nums = Vec::new();
for arg in args {
match arg {
Value::Number(n) => nums.push(*n),
Value::Date(n) => nums.push(*n),
Value::Bool(b) => nums.push(if *b { 1.0 } else { 0.0 }),
Value::Text(s) => {
let trimmed = s.trim();
if trimmed.is_empty() {
return Err(Value::Error(ErrorKind::Value));
}
match trimmed.parse::<f64>() {
Ok(v) if v.is_finite() => nums.push(v),
_ => return Err(Value::Error(ErrorKind::Value)),
}
}
Value::Empty => {} Value::Array(inner) => {
if let Some(err) = collect_nums_into_checked(inner, &mut nums) {
return Err(err);
}
}
Value::Zoned(_) => return Err(Value::Error(ErrorKind::Value)),
Value::Error(e) => return Err(Value::Error(e.clone())),
}
}
Ok(nums)
}
pub fn collect_nums_a_direct(args: &[Value]) -> Result<Vec<f64>, Value> {
let mut nums = Vec::new();
for arg in args {
match arg {
Value::Number(n) => nums.push(*n),
Value::Date(n) => nums.push(*n),
Value::Bool(b) => nums.push(if *b { 1.0 } else { 0.0 }),
Value::Text(s) => {
let trimmed = s.trim();
if trimmed.is_empty() {
nums.push(0.0);
} else {
match trimmed.parse::<f64>() {
Ok(v) if v.is_finite() => nums.push(v),
_ => return Err(Value::Error(ErrorKind::Value)),
}
}
}
Value::Empty => {} Value::Array(inner) => {
collect_nums_a_into(inner, &mut nums);
}
Value::Zoned(_) => return Err(Value::Error(ErrorKind::Value)),
Value::Error(e) => return Err(Value::Error(e.clone())),
}
}
Ok(nums)
}
pub fn collect_nums_a(args: &[Value]) -> Vec<f64> {
let mut nums = Vec::new();
collect_nums_a_into(args, &mut nums);
nums
}
pub fn collect_nums_a_into(args: &[Value], out: &mut Vec<f64>) {
for arg in args {
match arg {
Value::Number(n) => out.push(*n),
Value::Date(n) => out.push(*n),
Value::Bool(b) => out.push(if *b { 1.0 } else { 0.0 }),
Value::Text(_) => out.push(0.0),
Value::Array(inner) => collect_nums_a_into(inner, out),
Value::Empty => {}
Value::Error(_) => {}
Value::Zoned(_) => {}
}
}
}
pub fn collect_paired(x_arg: &Value, y_arg: &Value) -> Result<(Vec<f64>, Vec<f64>), Value> {
let xs_raw = flatten_to_values(x_arg);
let ys_raw = flatten_to_values(y_arg);
if xs_raw.is_empty() || ys_raw.is_empty() {
return Err(Value::Error(ErrorKind::Ref));
}
if xs_raw.len() != ys_raw.len() {
return Err(Value::Error(ErrorKind::NA));
}
let mut xs = Vec::new();
let mut ys = Vec::new();
for (x, y) in xs_raw.iter().zip(ys_raw.iter()) {
if let Value::Error(e) = x { return Err(Value::Error(e.clone())); }
if let Value::Error(e) = y { return Err(Value::Error(e.clone())); }
if let (Value::Number(xn), Value::Number(yn)) = (x, y) {
xs.push(*xn);
ys.push(*yn);
}
}
Ok((xs, ys))
}
fn flatten_to_values(v: &Value) -> Vec<Value> {
match v {
Value::Array(inner) => {
let mut out = Vec::new();
flatten_array_into(inner, &mut out);
out
}
other => vec![other.clone()],
}
}
fn flatten_array_into(arr: &[Value], out: &mut Vec<Value>) {
for v in arr {
match v {
Value::Array(inner) => flatten_array_into(inner, out),
other => out.push(other.clone()),
}
}
}