rrpack_prime/
range.rs

1use serde::{Deserialize, Serialize};
2use std::cmp::Ordering;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub enum Bound {
6    Auto,
7    Accurate { value: f64, strict: bool },
8}
9
10impl Default for Bound {
11    fn default() -> Self {
12        Self::Auto
13    }
14}
15
16impl Bound {
17    pub fn from_options(value: Option<f64>, loose: Option<bool>) -> Self {
18        match value {
19            Some(value) => {
20                let strict = !loose.unwrap_or_default();
21                Self::Accurate { value, strict }
22            }
23            None => Self::Auto,
24        }
25    }
26
27    // TODO: Remove all that below
28
29    pub fn auto() -> Self {
30        Self::Auto
31    }
32
33    pub fn strict(value: impl Into<f64>) -> Self {
34        Self::Accurate {
35            value: value.into(),
36            strict: true,
37        }
38    }
39
40    pub fn loose(value: impl Into<f64>) -> Self {
41        Self::Accurate {
42            value: value.into(),
43            strict: false,
44        }
45    }
46
47    pub fn min(&self, active_min: f64) -> f64 {
48        self.clamp(active_min, Ordering::Less)
49    }
50
51    pub fn max(&self, active_max: f64) -> f64 {
52        self.clamp(active_max, Ordering::Greater)
53    }
54
55    fn clamp(&self, active: f64, ordering: Ordering) -> f64 {
56        match *self {
57            Self::Auto => active,
58            Self::Accurate { value, strict } => {
59                if active.partial_cmp(&value) == Some(ordering) {
60                    if strict {
61                        value
62                    } else {
63                        active
64                    }
65                } else {
66                    value
67                }
68            }
69        }
70    }
71}
72
73#[cfg(test)]
74#[allow(clippy::float_cmp)] // Consts are used only!
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_bound_min() {
80        let auto = Bound::Auto;
81        assert_eq!(auto.min(10.0), 10.0);
82        assert_eq!(auto.min(-5.0), -5.0);
83        let strict = Bound::Accurate {
84            value: 0.0,
85            strict: true,
86        };
87        assert_eq!(strict.min(10.0), 0.0);
88        assert_eq!(strict.min(0.0), 0.0);
89        assert_eq!(strict.min(-5.0), 0.0);
90        let loose = Bound::Accurate {
91            value: 0.0,
92            strict: false,
93        };
94        assert_eq!(loose.min(10.0), 0.0);
95        assert_eq!(loose.min(0.0), 0.0);
96        assert_eq!(loose.min(-5.0), -5.0);
97    }
98
99    #[test]
100    fn test_bound_max() {
101        let auto = Bound::Auto;
102        assert_eq!(auto.max(120.0), 120.0);
103        assert_eq!(auto.max(90.0), 90.0);
104        let strict = Bound::Accurate {
105            value: 100.0,
106            strict: true,
107        };
108        assert_eq!(strict.max(120.0), 100.0);
109        assert_eq!(strict.max(100.0), 100.0);
110        assert_eq!(strict.max(90.0), 100.0);
111        let loose = Bound::Accurate {
112            value: 100.0,
113            strict: false,
114        };
115        assert_eq!(loose.max(120.0), 120.0);
116        assert_eq!(loose.max(100.0), 100.0);
117        assert_eq!(loose.max(90.0), 100.0);
118    }
119}
120
121// TODO: Move some parts here from the `rill-protocol::Range`
122#[derive(Debug, Clone, Serialize, Deserialize, Default)]
123pub struct Range {
124    pub min: Bound,
125    pub max: Bound,
126}
127
128impl Range {
129    // TODO: Remove it
130    pub fn new(mut min: f64, mut max: f64) -> Self {
131        if min > max {
132            std::mem::swap(&mut min, &mut max);
133        }
134        Self {
135            min: Bound::Accurate {
136                value: min,
137                strict: true,
138            },
139            max: Bound::Accurate {
140                value: max,
141                strict: true,
142            },
143        }
144    }
145
146    pub fn min(min: f64) -> Self {
147        Self {
148            min: Bound::Accurate {
149                value: min,
150                strict: true,
151            },
152            max: Bound::Auto,
153        }
154    }
155
156    pub fn max(max: f64) -> Self {
157        Self {
158            min: Bound::Auto,
159            max: Bound::Accurate {
160                value: max,
161                strict: true,
162            },
163        }
164    }
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct Label {
169    pub caption: String,
170    pub divisor: f64,
171}
172
173impl Default for Label {
174    fn default() -> Self {
175        Self {
176            caption: String::new(),
177            divisor: 1.0,
178        }
179    }
180}
181
182impl Label {
183    pub fn from_options(caption: Option<String>, divisor: Option<f64>) -> Self {
184        Self {
185            caption: caption.unwrap_or_else(String::new),
186            divisor: divisor.unwrap_or(1.0),
187        }
188    }
189
190    // TODO: Consider removing all that below
191
192    pub fn new(caption: impl Into<String>, divisor: f64) -> Self {
193        Self {
194            caption: caption.into(),
195            divisor,
196        }
197    }
198
199    pub fn pct_100() -> Self {
200        Self::new("%", 1.0)
201    }
202
203    pub fn pct_1() -> Self {
204        Self::new("%", 1.0 / 100.0)
205    }
206}