ittech/
data.rs

1//! Data type definitions.
2
3
4use crate::error::OutOfRangeError;
5pub(crate) use bitflags::bitflags;
6use std::convert::TryFrom;
7use std::fmt::{self, Debug};
8
9
10macro_rules! ranged_u8_newtype {
11    ( $name: ident, $low: literal ..= $high: literal ) => {
12        #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
13        pub struct $name(u8);
14
15        impl $name {
16            pub(crate) fn as_u8(self) -> u8 {
17                self.0
18            }
19        }
20
21        impl TryFrom<u8> for $name {
22            type Error = OutOfRangeError<$low, $high>;
23
24            fn try_from(raw: u8) -> Result<Self, Self::Error> {
25                if ($low..=$high).contains(&raw) {
26                    Ok($name(raw))
27                } else {
28                    Err(OutOfRangeError(raw))
29                }
30            }
31        }
32
33        impl From<$name> for u8 {
34            fn from(ranged: $name) -> u8 {
35                ranged.as_u8()
36            }
37        }
38
39        impl Debug for $name {
40            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41                Debug::fmt(&self.as_u8(), f)
42            }
43        }
44    };
45}
46
47// TODO We're already using range-limited opaque numbers for the IDs, ideally we'd be also
48//      versioning them (like <https://crates.io/crates/slotmap>) to make sure we always catch
49//      nonsensical values both during parsing and during serialization.
50ranged_u8_newtype!(InstrumentId, 0..=98);
51ranged_u8_newtype!(PatternId, 0..=199);
52ranged_u8_newtype!(SampleId, 0..=98);
53
54
55pub trait Get<I> {
56    type Output;
57    fn get(&self, index: I) -> Option<&Self::Output>;
58}
59
60impl<I, C> Get<&I> for C
61where
62    C: Get<I>,
63    I: Copy,
64{
65    type Output = <C as Get<I>>::Output;
66    fn get(&self, index: &I) -> Option<&Self::Output> {
67        self.get(*index)
68    }
69}
70
71macro_rules! impl_index_from_get {
72    ( $for: ty, $idx: ty ) => {
73        impl ::std::ops::Index<$idx> for $for {
74            type Output = <$for as $crate::data::Get<$idx>>::Output;
75            fn index(&self, index: $idx) -> &Self::Output {
76                self.get(index)
77                    .unwrap_or_else(|| panic!("{} index {:?} out of range", ::std::any::type_name::<$idx>(), &index))
78            }
79        }
80
81        impl ::std::ops::Index<&$idx> for $for {
82            type Output = <$for as $crate::data::Get<$idx>>::Output;
83            fn index(&self, index: &$idx) -> &Self::Output {
84                self.get(index)
85                    .unwrap_or_else(|| panic!("{} index {:?} out of range", ::std::any::type_name::<$idx>(), &index))
86            }
87        }
88    };
89}
90
91
92mod channel;
93mod envelope;
94mod instrument;
95mod module;
96mod pattern;
97mod sample;
98mod util;
99
100pub use channel::*;
101pub use envelope::*;
102pub use instrument::*;
103pub use module::*;
104pub use pattern::*;
105pub use sample::*;
106pub use util::*;