use super::funejson::Value; /// The window with start <= at < end. Half-open, so a window ending at 02:00 /// and the next starting at 02:00 never both apply. When several overlap, the /// one that started first wins (ties: the first in the list), so the answer /// does not depend on how the caller sorted them. Every window is checked, /// not only the active ones: a backwards window is a config error that would /// otherwise silently never apply. /// /// # Panics /// Panics on a window that does not end after it starts. pub fn active_maintenance(windows: &[MaintenanceWindow], at: i64) -> Option { let mut best: Option<&MaintenanceWindow> = None; for w in windows { if w.end <= w.start { panic!( "maintenance window \"{}\" must end after it starts: start {}, end {}", w.name, w.start, w.end ); } if w.start <= at && at < w.end && best.map_or(true, |b| w.start < b.start) { best = Some(w); } } best.map(|b| MaintenanceWindow { name: b.name.clone(), start: b.start, end: b.end }) } /// A `MaintenanceWindow` from its JSON form, for adapters of capabilities built on this one. pub fn maintenance_window_from_value(v: &Value) -> MaintenanceWindow { MaintenanceWindow { name: v.get("name").as_str().to_string(), start: v.get("start").as_i64(), end: v.get("end").as_i64(), } } pub fn maintenance_windows_from_value(v: &Value) -> Vec { v.as_arr().iter().map(maintenance_window_from_value).collect() } pub fn maintenance_window_to_value(w: &MaintenanceWindow) -> Value { Value::obj(vec![ ("name", Value::str(&w.name)), ("start", Value::Int(w.start)), ("end", Value::Int(w.end)), ]) } pub fn fune_vector(args: &[Value]) -> Value { let windows = maintenance_windows_from_value(&args[0]); let at = match &args[1] { Value::Int(i) => *i, Value::Float(f) => panic!("at must be a whole second, received {}", f), _ => panic!("at must be a whole second"), }; match active_maintenance(&windows, at) { Some(w) => maintenance_window_to_value(&w), None => Value::Null, } }