action_lang.rs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Requirements for working with time durations in SCCD:
  2. // 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.
  3. // 2) The type system should prevent mixing up durations' units (e.g. mistaking a value in milliseconds to be a value in microseconds).
  4. // 3) Under the hood, duration values should not all be converted to the same "base" unit (e.g. microseconds). Because we use integers, there is always a tradeoff between the smallest duration expressable vs. the largest duration expressable, and the optimal tradeoff is model-specific.
  5. // 4) There should be no additional runtime cost when performing arithmetic on durations of the same unit.
  6. use std::marker::PhantomData;
  7. use std::ops::{Add, Sub};
  8. // 32 bit provides wide enough range for most applications, and has better performance than 64 bit, even on amd64 architectures
  9. pub type TimeType = i32;
  10. // Just a marker
  11. pub trait Unit{}
  12. // Our units are also just markers
  13. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
  14. pub struct Femtos();
  15. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
  16. pub struct Picos();
  17. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
  18. pub struct Nanos();
  19. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
  20. pub struct Micros();
  21. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
  22. pub struct Millis();
  23. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
  24. pub struct Seconds();
  25. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
  26. pub struct Minutes();
  27. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
  28. pub struct Hours();
  29. impl Unit for Femtos{}
  30. impl Unit for Picos{}
  31. impl Unit for Nanos{}
  32. impl Unit for Micros{}
  33. impl Unit for Millis{}
  34. impl Unit for Seconds{}
  35. impl Unit for Minutes{}
  36. impl Unit for Hours{}
  37. // Duration type
  38. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
  39. pub struct Dur<U: Unit> {
  40. value: TimeType,
  41. unit: PhantomData<U>,
  42. }
  43. impl<U: Unit> Dur<U> {
  44. fn new(value: TimeType) -> Self {
  45. Self{
  46. value,
  47. unit: Default::default(),
  48. }
  49. }
  50. }
  51. impl<U: Unit> Add for Dur<U> {
  52. type Output = Self;
  53. fn add(self, other: Self) -> Self::Output {
  54. Self::new(self.value + other.value)
  55. }
  56. }
  57. impl<U: Unit> Sub for Dur<U> {
  58. type Output = Self;
  59. fn sub(self, other: Self) -> Self::Output {
  60. Self::new(self.value - other.value)
  61. }
  62. }
  63. impl Dur<Femtos> {
  64. pub fn to_picos(&self) -> Dur<Picos> {
  65. Dur::<Picos>::new(self.value / 1000)
  66. }
  67. }
  68. impl Dur<Picos> {
  69. pub fn to_femtos(&self) -> Dur<Femtos> {
  70. Dur::<Femtos>::new(self.value * 1000)
  71. }
  72. pub fn to_nanos(&self) -> Dur<Nanos> {
  73. Dur::<Nanos>::new(self.value / 1000)
  74. }
  75. }
  76. impl Dur<Nanos> {
  77. pub fn to_picos(&self) -> Dur<Picos> {
  78. Dur::<Picos>::new(self.value * 1000)
  79. }
  80. pub fn to_micros(&self) -> Dur<Micros> {
  81. Dur::<Micros>::new(self.value / 1000)
  82. }
  83. }
  84. impl Dur<Micros> {
  85. pub fn to_nanos(&self) -> Dur<Nanos> {
  86. Dur::<Nanos>::new(self.value * 1000)
  87. }
  88. pub fn to_millis(&self) -> Dur<Millis> {
  89. Dur::<Millis>::new(self.value / 1000)
  90. }
  91. }
  92. impl Dur<Millis> {
  93. pub fn to_micros(&self) -> Dur<Micros> {
  94. Dur::<Micros>::new(self.value * 1000)
  95. }
  96. pub fn to_seconds(&self) -> Dur<Seconds> {
  97. Dur::<Seconds>::new(self.value / 1000)
  98. }
  99. }
  100. impl Dur<Seconds> {
  101. pub fn to_millis(&self) -> Dur<Millis> {
  102. Dur::<Millis>::new(self.value * 1000)
  103. }
  104. pub fn to_minutes(&self) -> Dur<Minutes> {
  105. Dur::<Minutes>::new(self.value / 60)
  106. }
  107. }
  108. impl Dur<Minutes> {
  109. pub fn to_seconds(&self) -> Dur<Seconds> {
  110. Dur::<Seconds>::new(self.value * 60)
  111. }
  112. pub fn to_hours(&self) -> Dur<Hours> {
  113. Dur::<Hours>::new(self.value / 60)
  114. }
  115. }
  116. impl Dur<Hours> {
  117. pub fn to_minutes(&self) -> Dur<Minutes> {
  118. Dur::<Minutes>::new(self.value * 60)
  119. }
  120. }
  121. #[cfg(test)]
  122. mod tests {
  123. use super::*;
  124. #[test]
  125. fn duration_addition() {
  126. let result = Dur::<Seconds>::new(42) + Dur::<Seconds>::new(10);
  127. assert_eq!(result, Dur::<Seconds>::new(52))
  128. }
  129. #[test]
  130. fn duration_subtraction() {
  131. let result = Dur::<Seconds>::new(52) - Dur::<Seconds>::new(10);
  132. assert_eq!(result, Dur::<Seconds>::new(42))
  133. }
  134. #[test]
  135. fn duration_conversion() {
  136. let result = Dur::<Millis>::new(42000).to_seconds();
  137. assert_eq!(result, Dur::<Seconds>::new(42))
  138. }
  139. }
  140. // Helpers used by from-action-lang-generated Rust code
  141. #[allow(unused_imports)]
  142. use std::ops::Deref;
  143. #[allow(unused_imports)]
  144. use std::ops::DerefMut;
  145. // This macro lets a struct "inherit" the data members of another struct
  146. // The inherited struct is added as a struct member and the Deref and DerefMut
  147. // traits are implemented to return a reference to the base struct
  148. #[macro_export]
  149. macro_rules! inherit_struct {
  150. ($name: ident ($base: ty) { $($element: ident: $ty: ty),* $(,)? } ) => {
  151. #[derive(Copy, Clone)]
  152. struct $name {
  153. _base: $base,
  154. $($element: $ty),*
  155. }
  156. impl Deref for $name {
  157. type Target = $base;
  158. fn deref(&self) -> &$base {
  159. &self._base
  160. }
  161. }
  162. impl DerefMut for $name {
  163. fn deref_mut(&mut self) -> &mut $base {
  164. &mut self._base
  165. }
  166. }
  167. }
  168. }
  169. // "Base struct" for all scopes
  170. #[derive(Copy, Clone)]
  171. pub struct Empty{}
  172. // A closure object is a pair of a functions first argument and that function.
  173. // The call may be part of an larger expression, and therefore we cannot just write 'let' statements to assign the pair's elements to identifiers which we need for the call.
  174. // This macro does exactly that, in an anonymous Rust closure, which is immediately called.
  175. #[macro_export]
  176. macro_rules! call_closure {
  177. ($closure: expr, $($param: expr),* $(,)?) => {
  178. {
  179. let scope = &mut $closure.0;
  180. let function = &mut $closure.1;
  181. function($($param),* scope)
  182. }
  183. };
  184. }