megamorse_core/
word.rs

1use crate::{MorseCode, MorseSequence};
2
3/// A struct representing a single morse code sequence that maps
4/// to a single character. For example, 'a', '0' or 'G' all map
5/// to a single [MorseWord].
6///
7/// The [MorseWord] can contain up to 5 [MorseCode] values, and can
8/// thus represent any character in the Morse code alphabet (and no more)
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct MorseWord {
11    code: u8,
12}
13
14macro_rules! from_word {
15    ($num:literal) => {
16        impl From<[MorseCode; $num]> for MorseWord {
17            /// Creates a [MorseWord] from an array of [MorseCode] values.
18            fn from(codes: [MorseCode; $num]) -> Self {
19                MorseWord::new(codes)
20            }
21        }
22    };
23}
24
25from_word!(1);
26from_word!(2);
27from_word!(3);
28from_word!(4);
29from_word!(5);
30
31impl MorseWord {
32    const fn new<const N: usize>(codes: [MorseCode; N]) -> Self {
33        debug_assert!(N <= 5);
34
35        // The first three bits are used to store the word length
36        let mut code: u8 = (N as u8) & 0b0000_0111;
37
38        if N >= 1 {
39            code |= codes[0].to_bit() << 3;
40        }
41
42        if N >= 2 {
43            code |= codes[1].to_bit() << 4;
44        }
45
46        if N >= 3 {
47            code |= codes[2].to_bit() << 5;
48        }
49
50        if N >= 4 {
51            code |= codes[3].to_bit() << 6;
52        }
53
54        if N == 5 {
55            code |= codes[4].to_bit() << 7;
56        }
57
58        MorseWord { code }
59    }
60
61    /// Returns the [MorseWord] as an array of [MorseCode] values.
62    ///
63    /// The array will contain up to 5 [MorseCode] values, depending
64    /// on the length of the [MorseWord].
65    ///
66    /// # Returns
67    ///
68    /// A tuple containing the length of the MorseWord and an array
69    /// of [MorseCode] values.
70    ///
71    /// The returned array will be padded with [MorseCode::Dot] values if the
72    /// [MorseWord] is shorter than 5 characters.
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// let word = morse!(..-);
78    ///
79    /// let (len, codes) = word.to_array();
80    ///
81    /// assert_eq!(len, 3);
82    ///
83    /// assert_eq!(codes[0], MorseCode::Dot);
84    /// assert_eq!(codes[1], MorseCode::Dot);
85    /// assert_eq!(codes[2], MorseCode::Dash);
86    /// ````
87    pub const fn to_array(self) -> (usize, [MorseCode; 5]) {
88        let mut codes = [MorseCode::Dot; 5];
89        let n = self.len();
90
91        if n >= 1 {
92            codes[0] = if self.code & 0b0000_0001 == 0 {
93                MorseCode::Dot
94            } else {
95                MorseCode::Dash
96            };
97        }
98
99        if n >= 2 {
100            codes[1] = if self.code & 0b0000_0010 == 0 {
101                MorseCode::Dot
102            } else {
103                MorseCode::Dash
104            };
105        }
106
107        if n >= 3 {
108            codes[2] = if self.code & 0b0000_0100 == 0 {
109                MorseCode::Dot
110            } else {
111                MorseCode::Dash
112            };
113        }
114
115        if n >= 4 {
116            codes[3] = if self.code & 0b0000_1000 == 0 {
117                MorseCode::Dot
118            } else {
119                MorseCode::Dash
120            };
121        }
122
123        if n == 5 {
124            codes[4] = if self.code & 0b0001_0000 == 0 {
125                MorseCode::Dot
126            } else {
127                MorseCode::Dash
128            };
129        }
130
131        (n, codes)
132    }
133
134    /// Returns the amount of [MorseCode] values in the [MorseWord].
135    pub const fn len(&self) -> usize {
136        debug_assert!(self.code & 0b0000_0111 <= 5);
137        (self.code & 0b0000_0111) as usize
138    }
139
140    /// Returns true if the [MorseWord] is empty, i.e. contains no [MorseCode] values.
141    /// Should basically always return false, as a [MorseWord] with no [MorseCode] values
142    /// cannot be constructed without shenanigans.
143    pub const fn is_empty(&self) -> bool {
144        self.len() == 0
145    }
146
147    /// Returns the [MorseWord] as a [MorseSequence] array.
148    /// Used internally by the megamorse library to convert a [MorseWord] to a
149    /// sequence of playable [MorseSequence] values, with each
150    /// [MorseSequence] representing a single Morse code time unit.
151    ///
152    /// # Returns
153    ///
154    /// A tuple containing the length of the [MorseSequence] array and the
155    /// array itself, padded with [MorseSequence::Pause] values if the
156    /// [MorseWord] is shorter than 5 characters.
157    ///
158    /// Reading beyond the length given by the first element of the tuple is
159    /// not useful, as the array will be padded with [MorseSequence::Pause] values.
160    pub const fn to_sequence(self) -> (usize, [MorseSequence; 9]) {
161        let mut sequence = [MorseSequence::Pause; 9];
162
163        let (n, codes) = self.to_array();
164
165        debug_assert!(n <= 5);
166
167        if n >= 1 {
168            sequence[0] = MorseSequence::Code(codes[0]);
169        }
170
171        if n >= 2 {
172            sequence[2] = MorseSequence::Code(codes[1]);
173        }
174
175        if n >= 3 {
176            sequence[4] = MorseSequence::Code(codes[2]);
177        }
178
179        if n >= 4 {
180            sequence[6] = MorseSequence::Code(codes[3]);
181        }
182
183        if n == 5 {
184            sequence[8] = MorseSequence::Code(codes[4]);
185        }
186
187        let seq_len = 2 * n - 1;
188
189        debug_assert!(seq_len <= 9);
190
191        (seq_len, sequence)
192    }
193}
194
195/// Converts a single [char] to a [MorseWord].
196/// Is used both internally by the megamorse library and can be used
197/// by the user to convert a single character to a [MorseWord], which
198/// can then be played by a player.
199///
200/// Will return an error if the character has no Morse code representation.
201impl TryFrom<char> for MorseWord {
202    type Error = ();
203
204    fn try_from(value: char) -> Result<Self, Self::Error> {
205        let as_lower = value.to_ascii_lowercase();
206
207        let word = match as_lower {
208            'a' => MorseWord::from([MorseCode::Dot, MorseCode::Dash]),
209            'b' => MorseWord::from([
210                MorseCode::Dash,
211                MorseCode::Dot,
212                MorseCode::Dot,
213                MorseCode::Dot,
214            ]),
215            'c' => MorseWord::from([
216                MorseCode::Dash,
217                MorseCode::Dot,
218                MorseCode::Dash,
219                MorseCode::Dot,
220            ]),
221            'd' => MorseWord::from([MorseCode::Dash, MorseCode::Dot, MorseCode::Dot]),
222            'e' => MorseWord::from([MorseCode::Dot]),
223            'f' => MorseWord::from([
224                MorseCode::Dot,
225                MorseCode::Dot,
226                MorseCode::Dash,
227                MorseCode::Dot,
228            ]),
229            'g' => MorseWord::from([MorseCode::Dash, MorseCode::Dash, MorseCode::Dot]),
230            'h' => MorseWord::from([
231                MorseCode::Dot,
232                MorseCode::Dot,
233                MorseCode::Dot,
234                MorseCode::Dot,
235            ]),
236            'i' => MorseWord::from([MorseCode::Dot, MorseCode::Dot]),
237            'j' => MorseWord::from([
238                MorseCode::Dot,
239                MorseCode::Dash,
240                MorseCode::Dash,
241                MorseCode::Dash,
242            ]),
243            'k' => MorseWord::from([MorseCode::Dash, MorseCode::Dot, MorseCode::Dash]),
244            'l' => MorseWord::from([
245                MorseCode::Dot,
246                MorseCode::Dash,
247                MorseCode::Dot,
248                MorseCode::Dot,
249            ]),
250            'm' => MorseWord::from([MorseCode::Dash, MorseCode::Dash]),
251            'n' => MorseWord::from([MorseCode::Dash, MorseCode::Dot]),
252            'o' => MorseWord::from([MorseCode::Dash, MorseCode::Dash, MorseCode::Dash]),
253            'p' => MorseWord::from([
254                MorseCode::Dot,
255                MorseCode::Dash,
256                MorseCode::Dash,
257                MorseCode::Dot,
258            ]),
259            'q' => MorseWord::from([
260                MorseCode::Dash,
261                MorseCode::Dash,
262                MorseCode::Dot,
263                MorseCode::Dash,
264            ]),
265            'r' => MorseWord::from([MorseCode::Dot, MorseCode::Dash, MorseCode::Dot]),
266            's' => MorseWord::from([MorseCode::Dot, MorseCode::Dot, MorseCode::Dot]),
267            't' => MorseWord::from([MorseCode::Dash]),
268            'u' => MorseWord::from([MorseCode::Dot, MorseCode::Dot, MorseCode::Dash]),
269            'v' => MorseWord::from([
270                MorseCode::Dot,
271                MorseCode::Dot,
272                MorseCode::Dot,
273                MorseCode::Dash,
274            ]),
275            'w' => MorseWord::from([MorseCode::Dot, MorseCode::Dash, MorseCode::Dash]),
276            'x' => MorseWord::from([
277                MorseCode::Dash,
278                MorseCode::Dot,
279                MorseCode::Dot,
280                MorseCode::Dash,
281            ]),
282            'y' => MorseWord::from([
283                MorseCode::Dash,
284                MorseCode::Dot,
285                MorseCode::Dash,
286                MorseCode::Dash,
287            ]),
288            'z' => MorseWord::from([
289                MorseCode::Dash,
290                MorseCode::Dash,
291                MorseCode::Dot,
292                MorseCode::Dot,
293            ]),
294            '0' => MorseWord::from([
295                MorseCode::Dash,
296                MorseCode::Dash,
297                MorseCode::Dash,
298                MorseCode::Dash,
299                MorseCode::Dash,
300            ]),
301            '1' => MorseWord::from([
302                MorseCode::Dot,
303                MorseCode::Dash,
304                MorseCode::Dash,
305                MorseCode::Dash,
306                MorseCode::Dash,
307            ]),
308            '2' => MorseWord::from([
309                MorseCode::Dot,
310                MorseCode::Dot,
311                MorseCode::Dash,
312                MorseCode::Dash,
313                MorseCode::Dash,
314            ]),
315            '3' => MorseWord::from([
316                MorseCode::Dot,
317                MorseCode::Dot,
318                MorseCode::Dot,
319                MorseCode::Dash,
320                MorseCode::Dash,
321            ]),
322            '4' => MorseWord::from([
323                MorseCode::Dot,
324                MorseCode::Dot,
325                MorseCode::Dot,
326                MorseCode::Dot,
327                MorseCode::Dash,
328            ]),
329            '5' => MorseWord::from([
330                MorseCode::Dot,
331                MorseCode::Dot,
332                MorseCode::Dot,
333                MorseCode::Dot,
334                MorseCode::Dot,
335            ]),
336            '6' => MorseWord::from([
337                MorseCode::Dash,
338                MorseCode::Dot,
339                MorseCode::Dot,
340                MorseCode::Dot,
341                MorseCode::Dot,
342            ]),
343            '7' => MorseWord::from([
344                MorseCode::Dash,
345                MorseCode::Dash,
346                MorseCode::Dot,
347                MorseCode::Dot,
348                MorseCode::Dot,
349            ]),
350            '8' => MorseWord::from([
351                MorseCode::Dash,
352                MorseCode::Dash,
353                MorseCode::Dash,
354                MorseCode::Dot,
355                MorseCode::Dot,
356            ]),
357            '9' => MorseWord::from([
358                MorseCode::Dash,
359                MorseCode::Dash,
360                MorseCode::Dash,
361                MorseCode::Dash,
362                MorseCode::Dot,
363            ]),
364            _ => return Err(()),
365        };
366
367        Ok(word)
368    }
369}