tm1637_embedded_hal/
options.rs

1//! High-level API for display operations.
2
3use crate::{
4    exact_size::ExactSizeChainExt, mappings::SegmentBits, maybe_flipped::MaybeFlipped, numbers,
5    str::StrParser, tokens::NotFlipped, TM1637,
6};
7
8pub mod circles;
9mod windows;
10
11mod clock;
12mod repeat;
13mod scroll;
14
15pub use clock::*;
16pub use repeat::*;
17pub use scroll::*;
18
19/// High-level API for display operations.
20#[derive(Debug)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22pub struct DisplayOptions<'d, const N: usize, T, CLK, DIO, DELAY, I, M> {
23    device: &'d mut TM1637<N, T, CLK, DIO, DELAY>,
24    position: usize,
25    iter: I,
26    _flip: M,
27}
28
29impl<'d, const N: usize, T, CLK, DIO, DELAY>
30    DisplayOptions<'d, N, T, CLK, DIO, DELAY, ::core::iter::Empty<u8>, NotFlipped>
31{
32    /// Create a new empty [`DisplayOptions`] instance.
33    pub const fn empty(device: &'d mut TM1637<N, T, CLK, DIO, DELAY>) -> Self {
34        DisplayOptions {
35            device,
36            position: 0,
37            iter: ::core::iter::empty(),
38            _flip: NotFlipped,
39        }
40    }
41}
42
43impl<'d, 'b, const N: usize, T, CLK, DIO, DELAY, I, M>
44    DisplayOptions<'d, N, T, CLK, DIO, DELAY, I, M>
45{
46    /// Map the iter using the provided function.
47    pub fn map_iter<U, F: FnMut(I) -> U>(
48        self,
49        mut f: F,
50    ) -> DisplayOptions<'d, N, T, CLK, DIO, DELAY, U, M> {
51        DisplayOptions {
52            device: self.device,
53            position: self.position,
54            iter: f(self.iter),
55            _flip: self._flip,
56        }
57    }
58
59    /// Set the position on the display from which to start displaying the bytes.
60    pub const fn position(mut self, position: usize) -> Self {
61        self.position = position;
62        self
63    }
64
65    /// Add a slice of bytes.
66    pub fn slice(
67        self,
68        bytes: &'b [u8],
69    ) -> DisplayOptions<
70        'd,
71        N,
72        T,
73        CLK,
74        DIO,
75        DELAY,
76        impl DoubleEndedIterator<Item = u8> + ExactSizeIterator + 'b,
77        M,
78    >
79    where
80        I: DoubleEndedIterator<Item = u8> + ExactSizeIterator + 'b,
81    {
82        DisplayOptions {
83            device: self.device,
84            position: self.position,
85            iter: self.iter.exact_size_chain(bytes.iter().copied()),
86            _flip: self._flip,
87        }
88    }
89
90    /// Add a string.
91    pub fn str(
92        self,
93        str: &'b str,
94    ) -> DisplayOptions<
95        'd,
96        N,
97        T,
98        CLK,
99        DIO,
100        DELAY,
101        impl DoubleEndedIterator<Item = u8> + ExactSizeIterator + 'b,
102        M,
103    >
104    where
105        I: DoubleEndedIterator<Item = u8> + ExactSizeIterator + 'b,
106    {
107        DisplayOptions {
108            device: self.device,
109            position: self.position,
110            iter: self.iter.exact_size_chain(StrParser::new(str)),
111            _flip: self._flip,
112        }
113    }
114
115    /// Add an iterator of bytes.
116    ///
117    /// # Example
118    ///
119    /// Manually map each byte in a slice into a human readable character and set the dot at the 2nd position.
120    ///
121    /// ```rust
122    /// use tm1637_embedded_hal::{mappings::SegmentBits, mock::Noop, str::StrParser, TM1637Builder};
123    ///
124    /// let mut tm = TM1637Builder::new(Noop, Noop, Noop).build_blocking::<4>();
125    ///
126    /// tm.options()
127    ///     .iter(StrParser::new("HELLO").enumerate().map(move |(i, b)| {
128    ///         if i == 1 {
129    ///             b | SegmentBits::Dot as u8
130    ///         } else {
131    ///             b
132    ///         }
133    ///     }))
134    ///     .display()
135    ///     .ok();
136    ///
137    /// // Equivalent to
138    ///
139    /// tm.options()
140    ///    .str("HELLO")
141    ///    .dot(1)
142    ///    .display()
143    ///    .ok();
144    /// ```
145    pub fn iter<It>(
146        self,
147        iter: It,
148    ) -> DisplayOptions<
149        'd,
150        N,
151        T,
152        CLK,
153        DIO,
154        DELAY,
155        impl DoubleEndedIterator<Item = u8> + ExactSizeIterator,
156        M,
157    >
158    where
159        I: DoubleEndedIterator<Item = u8> + ExactSizeIterator,
160        It: DoubleEndedIterator<Item = u8> + ExactSizeIterator,
161    {
162        DisplayOptions {
163            device: self.device,
164            position: self.position,
165            iter: self.iter.exact_size_chain(iter),
166            _flip: self._flip,
167        }
168    }
169
170    /// Prepare to display a digital clock.
171    ///
172    /// See [`ClockDisplayOptions`].
173    pub const fn clock(self) -> ClockDisplayOptions<'d, N, T, CLK, DIO, DELAY, I, M> {
174        ClockDisplayOptions::new(self)
175    }
176
177    /// Use scroll animation options.
178    pub const fn scroll(self) -> ScrollDisplayOptions<'d, N, T, CLK, DIO, DELAY, I, M> {
179        ScrollDisplayOptions::new_with_defaults(self)
180    }
181
182    /// Use repeat animation options.
183    ///
184    /// Display all bytes of the given iterator on the same position.
185    ///
186    /// See [`RepeatDisplayOptions`].
187    pub const fn repeat(self) -> RepeatDisplayOptions<'d, N, T, CLK, DIO, DELAY, I, M> {
188        RepeatDisplayOptions::new_with_defaults(self)
189    }
190
191    /// Add a dynamic dot to the display at the specified position.
192    ///
193    /// ## Dynamic
194    ///
195    /// The dot is tied to the byte at the specified position and will move with it.
196    pub fn dot(
197        self,
198        position: usize,
199    ) -> DisplayOptions<
200        'd,
201        N,
202        T,
203        CLK,
204        DIO,
205        DELAY,
206        impl DoubleEndedIterator<Item = u8> + ExactSizeIterator,
207        M,
208    >
209    where
210        I: DoubleEndedIterator<Item = u8> + ExactSizeIterator,
211    {
212        DisplayOptions {
213            device: self.device,
214            position: self.position,
215            iter: self.iter.enumerate().map(move |(i, b)| {
216                if i == position {
217                    b | SegmentBits::Dot as u8
218                } else {
219                    b
220                }
221            }),
222            _flip: self._flip,
223        }
224    }
225
226    /// Remove the dot from the display at the specified position.
227    pub fn remove_dot(
228        self,
229        position: usize,
230    ) -> DisplayOptions<
231        'd,
232        N,
233        T,
234        CLK,
235        DIO,
236        DELAY,
237        impl DoubleEndedIterator<Item = u8> + ExactSizeIterator,
238        M,
239    >
240    where
241        I: DoubleEndedIterator<Item = u8> + ExactSizeIterator,
242    {
243        DisplayOptions {
244            device: self.device,
245            position: self.position,
246            iter: self.iter.enumerate().map(move |(i, b)| {
247                if i == position {
248                    b & !(SegmentBits::Dot as u8)
249                } else {
250                    b
251                }
252            }),
253            _flip: self._flip,
254        }
255    }
256
257    /// Set the dot at the specified position.
258    pub fn set_dot(
259        self,
260        position: usize,
261        dot: bool,
262    ) -> DisplayOptions<
263        'd,
264        N,
265        T,
266        CLK,
267        DIO,
268        DELAY,
269        impl DoubleEndedIterator<Item = u8> + ExactSizeIterator,
270        M,
271    >
272    where
273        I: DoubleEndedIterator<Item = u8> + ExactSizeIterator,
274    {
275        DisplayOptions {
276            device: self.device,
277            position: self.position,
278            iter: self.iter.enumerate().map(move |(i, b)| {
279                if i == position {
280                    if dot {
281                        b | SegmentBits::Dot as u8
282                    } else {
283                        b & !(SegmentBits::Dot as u8)
284                    }
285                } else {
286                    b
287                }
288            }),
289            _flip: self._flip,
290        }
291    }
292
293    /// Add dots to all positions in the display.
294    pub fn dots(
295        self,
296    ) -> DisplayOptions<
297        'd,
298        N,
299        T,
300        CLK,
301        DIO,
302        DELAY,
303        impl DoubleEndedIterator<Item = u8> + ExactSizeIterator,
304        M,
305    >
306    where
307        I: DoubleEndedIterator<Item = u8> + ExactSizeIterator,
308    {
309        DisplayOptions {
310            device: self.device,
311            position: self.position,
312            iter: self.iter.map(|b| b | SegmentBits::Dot as u8),
313            _flip: self._flip,
314        }
315    }
316
317    /// Remove dots from all positions in the display.
318    pub fn remove_dots(
319        self,
320    ) -> DisplayOptions<
321        'd,
322        N,
323        T,
324        CLK,
325        DIO,
326        DELAY,
327        impl DoubleEndedIterator<Item = u8> + ExactSizeIterator,
328        M,
329    >
330    where
331        I: DoubleEndedIterator<Item = u8> + ExactSizeIterator,
332    {
333        DisplayOptions {
334            device: self.device,
335            position: self.position,
336            iter: self.iter.map(|b| b & !(SegmentBits::Dot as u8)),
337            _flip: self._flip,
338        }
339    }
340
341    /// Map the bytes using the provided function.
342    ///
343    /// # Example
344    ///
345    /// Manually map each byte in a slice into a human readable character.
346    ///
347    /// ```rust
348    /// use tm1637_embedded_hal::{mappings::from_ascii_byte, mock::Noop, TM1637Builder};
349    ///
350    /// let mut tm = TM1637Builder::new(Noop, Noop, Noop).build_blocking::<4>();
351    ///
352    /// tm.options()
353    ///     .slice(b"HELLO")
354    ///     .map(from_ascii_byte)
355    ///     .display()
356    ///     .ok();
357    ///
358    /// // Equivalent** to
359    ///
360    /// tm.options()
361    ///    .str("HELLO")
362    ///    .display()
363    ///    .ok();
364    /// ```
365    /// ** The [`DisplayOptions::str`] method uses [`StrParser`] internally.
366    pub fn map<F: FnMut(u8) -> u8>(
367        self,
368        f: F,
369    ) -> DisplayOptions<
370        'd,
371        N,
372        T,
373        CLK,
374        DIO,
375        DELAY,
376        impl DoubleEndedIterator<Item = u8> + ExactSizeIterator,
377        M,
378    >
379    where
380        I: DoubleEndedIterator<Item = u8> + ExactSizeIterator,
381    {
382        DisplayOptions {
383            device: self.device,
384            position: self.position,
385            iter: self.iter.map(f),
386            _flip: self._flip,
387        }
388    }
389
390    /// Flip the display.
391    pub fn flip(
392        self,
393    ) -> DisplayOptions<
394        'd,
395        N,
396        T,
397        CLK,
398        DIO,
399        DELAY,
400        impl DoubleEndedIterator<Item = u8> + ExactSizeIterator,
401        impl MaybeFlipped<N>,
402    >
403    where
404        I: DoubleEndedIterator<Item = u8> + ExactSizeIterator,
405        M: MaybeFlipped<N>,
406    {
407        DisplayOptions {
408            device: self.device,
409            position: self.position,
410            iter: self.iter,
411            _flip: M::flip(),
412        }
413    }
414}
415
416#[::duplicate::duplicate_item(
417    module        async     await               Token                     DelayTrait                             ScrollIter;
418    [asynch]      [async]   [await.identity()]  [crate::tokens::Async]    [::embedded_hal_async::delay::DelayNs] [::futures::Stream];
419    [blocking]    []        [identity()]        [crate::tokens::Blocking] [::embedded_hal::delay::DelayNs]       [Iterator];
420)]
421mod module {
422    use ::embedded_hal::digital::OutputPin;
423    #[allow(unused_imports)]
424    use ::futures::StreamExt as _;
425
426    use crate::{
427        align::{Align, Aligned},
428        maybe_flipped::MaybeFlipped,
429        options::DisplayOptions,
430        ConditionalInputPin, Error, Identity,
431    };
432
433    #[::duplicate::duplicate_item(
434        NUM_POS ;
435        [4] ;
436        [6] ;
437    )]
438    impl<CLK, DIO, DELAY, ERR, I, M> DisplayOptions<'_, NUM_POS, Token, CLK, DIO, DELAY, I, M>
439    where
440        CLK: OutputPin<Error = ERR>,
441        DIO: OutputPin<Error = ERR> + ConditionalInputPin<ERR>,
442        DELAY: DelayTrait,
443        I: DoubleEndedIterator<Item = u8> + ExactSizeIterator,
444        M: MaybeFlipped<NUM_POS>,
445    {
446        /// Release the `device` and return the calculated position and bytes.
447        pub fn calculate(self) -> (usize, impl Iterator<Item = u8>) {
448            let (position, bytes) = M::calculate(self.position, self.iter);
449
450            Align::<NUM_POS>::align(position, bytes)
451        }
452
453        /// Display the bytes on a `flipped` or `non-flipped` display.
454        pub async fn display(self) -> Result<(), Error<ERR>> {
455            let (position, bytes) = M::calculate(self.position, self.iter);
456
457            let (position, bytes) = Align::<NUM_POS>::align(position, bytes);
458
459            self.device.display(position, bytes).await
460        }
461    }
462}
463
464#[::duplicate::duplicate_item(
465    function    type_   link;
466    [u8]        [u8]    ["[`u8`](crate::numbers::u8)"];
467    [u8_2]      [u8]    ["[`u8_2`](crate::numbers::u8_2)"];
468    [r_u8_2]    [u8]    ["[`r_u8_2`](crate::numbers::r_u8_2)"];
469    [u16_3]     [u16]   ["[`u16_3`](crate::numbers::u16_3)"];
470    [r_u16_3]   [u16]   ["[`r_u16_3`](crate::numbers::r_u16_3)"];
471    [u16_4]     [u16]   ["[`u16_4`](crate::numbers::u16_4)"];
472    [r_u16_4]   [u16]   ["[`r_u16_4`](crate::numbers::r_u16_4)"];
473    [u32_5]     [u32]   ["[`u32_5`](crate::numbers::u32_5)"];
474    [r_u32_5]   [u32]   ["[`r_u32_5`](crate::numbers::r_u32_5)"];
475    [u32_6]     [u32]   ["[`u32_6`](crate::numbers::u32_6)"];
476    [r_u32_6]   [u32]   ["[`r_u32_6`](crate::numbers::r_u32_6)"];
477    [i8_2]      [i8]    ["[`i8_2`](crate::numbers::i8_2)"];
478    [i16_3]     [i16]   ["[`i16_3`](crate::numbers::i16_3)"];
479    [r_i16_3]   [i16]   ["[`r_i16_3`](crate::numbers::r_i16_3)"];
480    [i16_4]     [i16]   ["[`i16_4`](crate::numbers::i16_4)"];
481    [r_i16_4]   [i16]   ["[`r_i16_4`](crate::numbers::r_i16_4)"];
482    [i32_5]     [i32]   ["[`i32_5`](crate::numbers::i32_5)"];
483    [r_i32_5]   [i32]   ["[`r_i32_5`](crate::numbers::r_i32_5)"];
484    [i32_6]     [i32]   ["[`i32_6`](crate::numbers::i32_6)"];
485    [r_i32_6]   [i32]   ["[`r_i32_6`](crate::numbers::r_i32_6)"];
486)]
487impl<'d, const N: usize, T, CLK, DIO, DELAY, I, M> DisplayOptions<'d, N, T, CLK, DIO, DELAY, I, M>
488where
489    I: DoubleEndedIterator<Item = u8> + ExactSizeIterator,
490{
491    #[doc = "See "]
492    #[doc = link]
493    pub fn function(
494        self,
495        n: type_,
496    ) -> DisplayOptions<
497        'd,
498        N,
499        T,
500        CLK,
501        DIO,
502        DELAY,
503        impl DoubleEndedIterator<Item = u8> + ExactSizeIterator,
504        M,
505    > {
506        DisplayOptions {
507            device: self.device,
508            position: self.position,
509            iter: self.iter.exact_size_chain(numbers::function(n).into_iter()),
510            _flip: self._flip,
511        }
512    }
513}
514
515#[cfg(test)]
516mod tests {
517    extern crate std;
518    use std::vec;
519    use std::vec::Vec;
520
521    use crate::{mappings::str_from_byte, mock::Noop, TM1637Builder};
522
523    #[test]
524    fn dot_is_dynamically_tied_to_byte() {
525        let mut tm = TM1637Builder::new(Noop, Noop, Noop).build_blocking::<4>();
526
527        let (_, iter) = tm.options().str("HELLO").dot(1).dot(3).calculate();
528        let collected = iter.map(str_from_byte).collect::<Vec<_>>();
529
530        assert_eq!(vec!["H", "E.", "L", "L."], collected);
531
532        let (_, iter) = tm.options().str("HELLO").dot(1).dot(3).flip().calculate();
533        let collected = iter.map(str_from_byte).collect::<Vec<_>>();
534
535        assert_eq!(vec!["7.", "7", "3.", "H"], collected);
536    }
537}