Functional Weave
Code in TypeScript

charts.ticks@1.0.0

impl/rust/time_tick_interval.rs

1,921 bytes · the Rust implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::charts_ticks_tick_step::tick_step;  ← tickStep, another function of this group · built into the same file, even by a slim install
use super::dates_add_days::epoch_day_from_iso;  ← from dates.add-days ^1.0.0 · built alongside by fune

// d3-scale's calendar ladder, from a day up, with the lengths in days it
// compares against: a month counts as 30, a quarter as 90, a year as 365.
const LADDER: [(&str, i64, f64); 6] = [
    ("day", 1, 1.0),
    ("day", 2, 2.0),
    ("week", 1, 7.0),
    ("month", 1, 30.0),
    ("quarter", 1, 90.0),
    ("year", 1, 365.0),
];

/// The calendar interval for about `count` ticks between two dates, as d3's
/// time scale picks it: the nearest rung of the ladder by ratio, or whole years
/// in steps of 1, 2 or 5 x 10^k beyond a year.
///
/// # Panics
/// Panics on a date that is not a real ISO date or `count` less than 1.
pub fn time_tick_interval(start: &str, stop: &str, count: i64) -> TimeTickInterval {
    if count < 1 {
        panic!("count must be a whole number of at least 1, got {}", count);
    }
    let span = (epoch_day_from_iso(stop) - epoch_day_from_iso(start)).abs() as f64;
    let target = span / count as f64;
    let mut i = 0;
    while i < LADDER.len() && LADDER[i].2 <= target {
        i += 1;
    }
    if i == LADDER.len() {
        let step = tick_step(0.0, span / 365.0, count) as i64;
        return TimeTickInterval { interval: "year".to_string(), step: step.max(1) };
    }
    if i == 0 {
        return TimeTickInterval { interval: "day".to_string(), step: 1 };
    }
    let pick = if target / LADDER[i - 1].2 < LADDER[i].2 / target { LADDER[i - 1] } else { LADDER[i] };
    TimeTickInterval { interval: pick.0.to_string(), step: pick.1 }
}

pub fn time_tick_interval_to_value(t: &TimeTickInterval) -> Value {
    Value::obj(vec![("interval", Value::str(&t.interval)), ("step", Value::Int(t.step))])
}

pub fn fune_vector(args: &[Value]) -> Value {
    time_tick_interval_to_value(&time_tick_interval(args[0].as_str(), args[1].as_str(), args[2].as_i64()))
}