bellframe/
stroke.rs

1use std::ops::Not;
2
3use self::Stroke::{Back, Hand};
4
5#[cfg(feature = "serde")]
6use serde_crate::{Deserialize, Serialize};
7
8/// Stroke of a row, i.e. handstroke (`Stroke::Hand`) or backstroke (`Stroke::Back`).
9#[repr(u8)]
10#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]
11#[cfg_attr(
12    feature = "serde",
13    derive(Deserialize, Serialize),
14    serde(crate = "serde_crate", rename_all = "snake_case")
15)]
16pub enum Stroke {
17    Hand = 0,
18    Back = 1,
19}
20
21impl Stroke {
22    /// Returns the `Stroke` which happens `offset` [`Row`](crate::Row)s after a given `start`
23    /// `Stroke`.
24    ///
25    /// # Example
26    ///
27    /// ```
28    /// use bellframe::Stroke;
29    ///
30    /// assert_eq!(Stroke::Hand.offset(0), Stroke::Hand);
31    /// // The row after a backstroke is a handstroke
32    /// assert_eq!(Stroke::Back.offset(1), Stroke::Hand);
33    /// assert_eq!(Stroke::Hand.offset(1235124), Stroke::Hand); // Whoa lots of ringing
34    /// ```
35    pub fn offset(self, offset: usize) -> Stroke {
36        if offset % 2 == 0 {
37            self
38        } else {
39            !self
40        }
41    }
42
43    /// Returns the `Stroke` which happens `offset` [`Row`](crate::Row)s after (or before) `self`.
44    ///
45    /// # Example
46    ///
47    /// ```
48    /// use bellframe::Stroke;
49    ///
50    /// assert_eq!(Stroke::Hand.offset_i(0), Stroke::Hand);
51    /// // The row after a backstroke is a handstroke
52    /// assert_eq!(Stroke::Back.offset_i(1), Stroke::Hand);
53    /// // The row before a backstroke is also a handstroke
54    /// assert_eq!(Stroke::Back.offset_i(-1), Stroke::Hand);
55    /// assert_eq!(Stroke::Hand.offset_i(1235124), Stroke::Hand); // Whoa lots of ringing
56    /// ```
57    pub fn offset_i(self, offset: isize) -> Stroke {
58        if offset % 2 == 0 {
59            self
60        } else {
61            !self
62        }
63    }
64}
65
66impl Not for Stroke {
67    type Output = Self;
68
69    /// Returns the opposite `Stroke` to `self`
70    ///
71    /// # Example
72    ///
73    /// ```
74    /// use bellframe::Stroke;
75    ///
76    /// assert_eq!(!Stroke::Hand, Stroke::Back);
77    /// assert_eq!(!Stroke::Back, Stroke::Hand);
78    /// ```
79    fn not(self) -> Self::Output {
80        match self {
81            Hand => Back,
82            Back => Hand,
83        }
84    }
85}
86
87/// A set of at least one [`Stroke`]
88#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
89pub enum StrokeSet {
90    Hand,
91    Back,
92    Both,
93}
94
95impl StrokeSet {
96    pub fn contains(self, stroke: Stroke) -> bool {
97        match self {
98            Self::Both => true,
99            Self::Hand => stroke == Stroke::Hand,
100            Self::Back => stroke == Stroke::Back,
101        }
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn offset() {
111        #[track_caller]
112        fn check(start: Stroke, offset: usize, expected: Stroke) {
113            assert_eq!(start.offset(offset), expected);
114        }
115        check(Hand, 0, Hand);
116        check(Back, 0, Back);
117        check(Hand, 1, Back);
118        check(Back, 1, Hand);
119        check(Hand, 123547, Back);
120        check(Back, 123547, Hand);
121        check(Hand, usize::MAX, Back); // usize::MAX is odd
122        check(Back, usize::MAX, Hand);
123    }
124
125    #[test]
126    fn offset_i() {
127        #[track_caller]
128        fn check(start: Stroke, offset: isize, expected: Stroke) {
129            assert_eq!(start.offset_i(offset), expected);
130        }
131        check(Hand, 0, Hand);
132        check(Back, 0, Back);
133        check(Hand, 1, Back);
134        check(Back, 1, Hand);
135        check(Hand, 123547, Back);
136        check(Back, 123547, Hand);
137        check(Hand, isize::MAX, Back); // isize::MAX is odd
138        check(Back, isize::MAX, Hand);
139        check(Hand, isize::MIN, Hand); // isize::MIN is even
140        check(Back, isize::MIN, Back);
141    }
142
143    #[test]
144    fn not() {
145        assert_eq!(!Hand, Back);
146        assert_eq!(!Back, Hand);
147    }
148}