1use serde_json::Value;
9
10pub const APP_CLOCK: &str = "app.clock";
13
14pub const MAX_STEP_MS: f64 = 60_000.0;
16
17#[derive(Debug, Clone, Copy, PartialEq)]
19pub enum ClockOp {
20 Read,
22 Pause,
24 Step {
26 ms: f64,
28 },
29 Resume,
31}
32
33impl ClockOp {
34 pub fn from_params(p: &Value) -> Result<ClockOp, String> {
37 if !p.is_null() && !p.is_object() {
38 return Err("app.clock takes {pause: true}, {step_ms: N}, {resume: true} or {}".into());
39 }
40 let yes = |k: &str| p.get(k).and_then(Value::as_bool) == Some(true);
41 let mut ops = Vec::new();
42 if yes("pause") {
43 ops.push(ClockOp::Pause);
44 }
45 if yes("resume") {
46 ops.push(ClockOp::Resume);
47 }
48 if let Some(v) = p.get("step_ms").filter(|v| !v.is_null()) {
49 let ms = v.as_f64().filter(|m| m.is_finite() && *m > 0.0 && *m <= MAX_STEP_MS);
50 let Some(ms) = ms else { return Err(format!("step_ms is a number of milliseconds above 0 and at most {MAX_STEP_MS}")) };
51 ops.push(ClockOp::Step { ms });
52 }
53 match ops.as_slice() {
54 [] => Ok(ClockOp::Read),
55 [op] => Ok(*op),
56 _ => Err("app.clock does one of pause, step_ms or resume per request".into()),
57 }
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use serde_json::json;
65
66 #[test]
67 fn params_name_one_operation() {
68 assert_eq!(ClockOp::from_params(&json!({})), Ok(ClockOp::Read));
69 assert_eq!(ClockOp::from_params(&Value::Null), Ok(ClockOp::Read));
70 assert_eq!(ClockOp::from_params(&json!({"pause": true})), Ok(ClockOp::Pause));
71 assert_eq!(ClockOp::from_params(&json!({"pause": false})), Ok(ClockOp::Read));
72 assert_eq!(ClockOp::from_params(&json!({"resume": true})), Ok(ClockOp::Resume));
73 assert_eq!(ClockOp::from_params(&json!({"step_ms": 16.7})), Ok(ClockOp::Step { ms: 16.7 }));
74 assert!(ClockOp::from_params(&json!({"pause": true, "resume": true})).is_err());
75 assert!(ClockOp::from_params(&json!({"step_ms": 0})).is_err());
76 assert!(ClockOp::from_params(&json!({"step_ms": -5})).is_err());
77 assert!(ClockOp::from_params(&json!({"step_ms": "16"})).is_err());
78 assert!(ClockOp::from_params(&json!({"step_ms": 1e9})).is_err());
79 assert!(ClockOp::from_params(&json!([1])).is_err());
80 }
81}