|
@@ -1,5 +1,158 @@
|
|
|
+
|
|
|
+// Requirements for working with time durations in SCCD:
|
|
|
+// 1) Use of integer types. Floating point types have complex behavior when it comes to precision, mathematical operations introducing roundoff errors that are hard to predict.
|
|
|
+// 2) The type system should prevent mixing up durations' units (e.g. assuming a value in milliseconds is a value in microsends).
|
|
|
+// 3) Under the hood, duration values should not all be converted to the same "base" unit (e.g. microseconds). There is always a tradeoff between the smallest duration expressable vs. the largest duration expressable, and the optimal tradeoff is model-specific.
|
|
|
+// 4) There should be no additional runtime cost when performing arithmetic on durations of the same unit.
|
|
|
+
|
|
|
+use std::marker::PhantomData;
|
|
|
+use std::ops::{Add, Sub};
|
|
|
+
|
|
|
+// 32 bit provides wide enough range for most applications, and has better performance than 64 bit, even on amd64 architectures
|
|
|
+pub type TimeType = i32;
|
|
|
+
|
|
|
+// Just a marker
|
|
|
+pub trait Unit{}
|
|
|
+
|
|
|
+// Our units are also just markers
|
|
|
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
|
|
+pub struct Femtos();
|
|
|
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
|
|
+pub struct Picos();
|
|
|
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
|
|
+pub struct Nanos();
|
|
|
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
|
|
+pub struct Micros();
|
|
|
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
|
|
+pub struct Millis();
|
|
|
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
|
|
+pub struct Seconds();
|
|
|
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
|
|
+pub struct Minutes();
|
|
|
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
|
|
+pub struct Hours();
|
|
|
+
|
|
|
+impl Unit for Femtos{}
|
|
|
+impl Unit for Picos{}
|
|
|
+impl Unit for Nanos{}
|
|
|
+impl Unit for Micros{}
|
|
|
+impl Unit for Millis{}
|
|
|
+impl Unit for Seconds{}
|
|
|
+impl Unit for Minutes{}
|
|
|
+impl Unit for Hours{}
|
|
|
+
|
|
|
+// Duration type
|
|
|
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
|
|
+pub struct Dur<U: Unit> {
|
|
|
+ value: TimeType,
|
|
|
+ unit: PhantomData<U>,
|
|
|
+}
|
|
|
+impl<U: Unit> Dur<U> {
|
|
|
+ fn new(value: TimeType) -> Self {
|
|
|
+ Self{
|
|
|
+ value,
|
|
|
+ unit: Default::default(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+impl<U: Unit> Add for Dur<U> {
|
|
|
+ type Output = Self;
|
|
|
+
|
|
|
+ fn add(self, other: Self) -> Self::Output {
|
|
|
+ Self::new(self.value + other.value)
|
|
|
+ }
|
|
|
+}
|
|
|
+impl<U: Unit> Sub for Dur<U> {
|
|
|
+ type Output = Self;
|
|
|
+
|
|
|
+ fn sub(self, other: Self) -> Self::Output {
|
|
|
+ Self::new(self.value - other.value)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl Dur<Femtos> {
|
|
|
+ pub fn to_picos(&self) -> Dur<Picos> {
|
|
|
+ Dur::<Picos>::new(self.value / 1000)
|
|
|
+ }
|
|
|
+}
|
|
|
+impl Dur<Picos> {
|
|
|
+ pub fn to_femtos(&self) -> Dur<Femtos> {
|
|
|
+ Dur::<Femtos>::new(self.value * 1000)
|
|
|
+ }
|
|
|
+ pub fn to_nanos(&self) -> Dur<Nanos> {
|
|
|
+ Dur::<Nanos>::new(self.value / 1000)
|
|
|
+ }
|
|
|
+}
|
|
|
+impl Dur<Nanos> {
|
|
|
+ pub fn to_picos(&self) -> Dur<Picos> {
|
|
|
+ Dur::<Picos>::new(self.value * 1000)
|
|
|
+ }
|
|
|
+ pub fn to_micros(&self) -> Dur<Micros> {
|
|
|
+ Dur::<Micros>::new(self.value / 1000)
|
|
|
+ }
|
|
|
+}
|
|
|
+impl Dur<Micros> {
|
|
|
+ pub fn to_nanos(&self) -> Dur<Nanos> {
|
|
|
+ Dur::<Nanos>::new(self.value * 1000)
|
|
|
+ }
|
|
|
+ pub fn to_millis(&self) -> Dur<Millis> {
|
|
|
+ Dur::<Millis>::new(self.value / 1000)
|
|
|
+ }
|
|
|
+}
|
|
|
+impl Dur<Millis> {
|
|
|
+ pub fn to_micros(&self) -> Dur<Micros> {
|
|
|
+ Dur::<Micros>::new(self.value * 1000)
|
|
|
+ }
|
|
|
+ pub fn to_seconds(&self) -> Dur<Seconds> {
|
|
|
+ Dur::<Seconds>::new(self.value / 1000)
|
|
|
+ }
|
|
|
+}
|
|
|
+impl Dur<Seconds> {
|
|
|
+ pub fn to_millis(&self) -> Dur<Millis> {
|
|
|
+ Dur::<Millis>::new(self.value * 1000)
|
|
|
+ }
|
|
|
+ pub fn to_minutes(&self) -> Dur<Minutes> {
|
|
|
+ Dur::<Minutes>::new(self.value / 60)
|
|
|
+ }
|
|
|
+}
|
|
|
+impl Dur<Minutes> {
|
|
|
+ pub fn to_seconds(&self) -> Dur<Seconds> {
|
|
|
+ Dur::<Seconds>::new(self.value * 60)
|
|
|
+ }
|
|
|
+ pub fn to_hours(&self) -> Dur<Hours> {
|
|
|
+ Dur::<Hours>::new(self.value / 60)
|
|
|
+ }
|
|
|
+}
|
|
|
+impl Dur<Hours> {
|
|
|
+ pub fn to_minutes(&self) -> Dur<Minutes> {
|
|
|
+ Dur::<Minutes>::new(self.value * 60)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod tests {
|
|
|
+ use super::*;
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn duration_addition() {
|
|
|
+ let result = Dur::<Seconds>::new(42) + Dur::<Seconds>::new(10);
|
|
|
+ assert_eq!(result, Dur::<Seconds>::new(52))
|
|
|
+ }
|
|
|
+ #[test]
|
|
|
+ fn duration_subtraction() {
|
|
|
+ let result = Dur::<Seconds>::new(52) - Dur::<Seconds>::new(10);
|
|
|
+ assert_eq!(result, Dur::<Seconds>::new(42))
|
|
|
+ }
|
|
|
+ #[test]
|
|
|
+ fn duration_conversion() {
|
|
|
+ let result = Dur::<Millis>::new(42000).to_seconds();
|
|
|
+ assert_eq!(result, Dur::<Seconds>::new(42))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
// Helpers used by from-action-lang-generated Rust code
|
|
|
-
|
|
|
+
|
|
|
#[allow(unused_imports)]
|
|
|
use std::ops::Deref;
|
|
|
|