Create a Bounded structure for bounded values.
Some checks failed
Monorepo build / build-flake (push) Has been cancelled
Some checks failed
Monorepo build / build-flake (push) Has been cancelled
This commit was merged in pull request #404.
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -4964,6 +4964,10 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "utils"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
|
||||
@@ -37,7 +37,7 @@ members = [
|
||||
"visions/server",
|
||||
# "visions/types",
|
||||
"visions/ui",
|
||||
"visions/core", "glimmer/yew",
|
||||
"visions/core", "glimmer/yew", "utils",
|
||||
# "bike-lights/bike",
|
||||
]
|
||||
|
||||
|
||||
6
utils/Cargo.toml
Normal file
6
utils/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "utils"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
6
utils/Taskfile.yml
Normal file
6
utils/Taskfile.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
version: "3"
|
||||
|
||||
tasks:
|
||||
test:
|
||||
cmds:
|
||||
- cargo watch -x 'nextest run'
|
||||
211
utils/src/bounded.rs
Normal file
211
utils/src/bounded.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
use std::ops::{Add, Deref, Neg, Sub};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Ord)]
|
||||
pub struct Bounded<V> {
|
||||
current: V,
|
||||
min: V,
|
||||
max: V,
|
||||
}
|
||||
|
||||
fn clamp<V: PartialOrd>(value: V, min: V, max: V) -> V {
|
||||
if value < min {
|
||||
min
|
||||
} else if value > max {
|
||||
max
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Clone + Copy + PartialOrd> Bounded<V> {
|
||||
pub fn new(current: V, min: V, max: V) -> Self {
|
||||
Self {
|
||||
current: clamp(current, min, max),
|
||||
min,
|
||||
max,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: PartialEq> PartialEq for Bounded<V> {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
self.current == rhs.current
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: PartialEq> PartialEq<V> for Bounded<V> {
|
||||
fn eq(&self, rhs: &V) -> bool {
|
||||
self.current == *rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: PartialOrd> PartialOrd for Bounded<V> {
|
||||
fn partial_cmp(&self, rhs: &Self) -> Option<std::cmp::Ordering> {
|
||||
self.current.partial_cmp(&rhs.current)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: PartialOrd> PartialOrd<V> for Bounded<V> {
|
||||
fn partial_cmp(&self, rhs: &V) -> Option<std::cmp::Ordering> {
|
||||
self.current.partial_cmp(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Deref for Bounded<V> {
|
||||
type Target = V;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.current
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Add<Output = V> + PartialOrd + Copy> Add<V> for Bounded<V> {
|
||||
type Output = Bounded<V>;
|
||||
|
||||
fn add(self, rhs: V) -> Self::Output {
|
||||
let new_value: V = self.current + rhs;
|
||||
Bounded::new(new_value, self.min, self.max)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Add<Output = V> + PartialOrd + Copy> Add for Bounded<V> {
|
||||
type Output = Bounded<V>;
|
||||
|
||||
fn add(self, rhs: Bounded<V>) -> Self::Output {
|
||||
let new_value: V = self.current + rhs.current;
|
||||
Bounded::new(new_value, self.min, self.max)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Sub<Output = V> + PartialOrd + Copy> Sub<V> for Bounded<V> {
|
||||
type Output = Bounded<V>;
|
||||
|
||||
fn sub(self, rhs: V) -> Self::Output {
|
||||
let new_value: V = self.current - rhs;
|
||||
Bounded::new(new_value, self.min, self.max)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Sub<Output = V> + PartialOrd + Copy> Sub for Bounded<V> {
|
||||
type Output = Bounded<V>;
|
||||
|
||||
fn sub(self, rhs: Bounded<V>) -> Self::Output {
|
||||
let new_value: V = self.current - rhs.current;
|
||||
Bounded::new(new_value, self.min, self.max)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Neg<Output = V> + PartialOrd + Copy> Neg for Bounded<V> {
|
||||
type Output = Bounded<V>;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
Self {
|
||||
current: -self.current,
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn safe_construction() {
|
||||
assert_eq!(Bounded::new(5, 0, 10), 5);
|
||||
assert_eq!(Bounded::new(5, 6, 10), 6);
|
||||
assert_eq!(Bounded::new(5, 0, 4), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_compare_against_primitive() {
|
||||
let value = Bounded::new(5, 0, 10);
|
||||
|
||||
assert_eq!(value, 5);
|
||||
|
||||
assert_ne!(value, 4);
|
||||
assert!(Bounded::new(5, 0, 10) < 6);
|
||||
assert!(Bounded::new(5, 0, 10) > 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_compare_two_values() {
|
||||
assert_eq!(Bounded::new(5, 0, 10), Bounded::new(5, 0, 10));
|
||||
assert_eq!(Bounded::new(5, 0, 10), Bounded::new(5, 1, 9));
|
||||
assert_ne!(Bounded::new(5, 0, 10), Bounded::new(4, 0, 10));
|
||||
|
||||
assert!(Bounded::new(5, 0, 10) < Bounded::new(6, 0, 10));
|
||||
assert!(Bounded::new(5, 0, 10) < Bounded::new(6, 1, 9));
|
||||
assert!(Bounded::new(5, 0, 10) > Bounded::new(4, 0, 10));
|
||||
assert!(Bounded::new(5, 0, 10) > Bounded::new(4, 1, 9));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_extract_value() {
|
||||
let value = Bounded::new(5, 0, 10);
|
||||
let primitive: i32 = *value;
|
||||
|
||||
let func: Box<dyn FnOnce(i32)> = Box::new(|_: i32| {});
|
||||
|
||||
assert_eq!(primitive, 5);
|
||||
func(*value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_use_value_as_index() {
|
||||
let lst = vec!['a', 'b', 'c', 'd'];
|
||||
let idx: Bounded<usize> = Bounded::new(2, 0, 2);
|
||||
|
||||
assert_eq!(lst[*idx], 'c');
|
||||
assert_eq!(lst[*Bounded::new(3, 0, 9)], 'd');
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_add_number_to_value() {
|
||||
assert_eq!(Bounded::new(5, 0, 10) + 3, Bounded::new(8, 0, 10));
|
||||
assert_eq!(Bounded::new(5, 0, 10) + 6, Bounded::new(10, 0, 10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_add_two_values() {
|
||||
assert_eq!(
|
||||
Bounded::new(5, 0, 10) + Bounded::new(3, 0, 10),
|
||||
Bounded::new(8, 0, 10)
|
||||
);
|
||||
assert_eq!(
|
||||
Bounded::new(5, 0, 10) + Bounded::new(6, 0, 10),
|
||||
Bounded::new(10, 0, 10)
|
||||
);
|
||||
assert_eq!(
|
||||
Bounded::new(5, 0, 10) + Bounded::new(6, 0, 9),
|
||||
Bounded::new(10, 0, 10)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_subtract_number_from_value() {
|
||||
assert_eq!(Bounded::new(5, 0, 10) - 3, Bounded::new(2, 0, 10));
|
||||
assert_eq!(Bounded::new(5, 0, 10) - 6, Bounded::new(0, 0, 10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_subtract_two_values() {
|
||||
assert_eq!(
|
||||
Bounded::new(5, 0, 10) - Bounded::new(3, 0, 10),
|
||||
Bounded::new(2, 0, 10)
|
||||
);
|
||||
assert_eq!(
|
||||
Bounded::new(5, 0, 10) - Bounded::new(6, 0, 10),
|
||||
Bounded::new(0, 0, 10)
|
||||
);
|
||||
assert_eq!(
|
||||
Bounded::new(5, 0, 10) - Bounded::new(6, 1, 9),
|
||||
Bounded::new(0, 0, 10)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_negate() {
|
||||
assert_eq!(Bounded::new(5, -10, 10).neg(), Bounded::new(-5, -10, 10));
|
||||
}
|
||||
}
|
||||
2
utils/src/lib.rs
Normal file
2
utils/src/lib.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod bounded;
|
||||
pub use bounded::*;
|
||||
Reference in New Issue
Block a user