Skip to main content

ot_tools_io/
identifiers.rs

1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2024 Mike Robeson [dijksterhuis]
4*/
5
6//! Identifiers are used on generic collection types for panic-free access to elements of
7//! the internal collections via `id*()` methods.
8//!
9//! # Example: [`PatternId`]
10//!
11//! ```
12//! use ot_tools_io::identifiers::PatternId;
13//! use ot_tools_io::BankFile;
14//! use ot_tools_io::OtToolsIoError;
15//!
16//! fn main() -> Result<(), OtToolsIoError> {
17//!     // mutable so we can demonstrate using `id_mut` methods
18//!     let mut bank = BankFile::default();
19//!
20//!     // using variant by hand
21//!     // -- pattern 1 (reference)
22//!     bank.patterns.id_ref(&PatternId::One);
23//!     // -- pattern 16 (mutable reference)
24//!     bank.patterns.id_mut(&PatternId::Sixteen);
25//!
26//!     // lookup from *unsigned* integer values
27//!     // failed conversions return an `OtToolsIoError::InvalidIndex` error for you to handle
28//!     // -- pattern 1 (reference)
29//!     bank.patterns.id_ref(&PatternId::try_from(0_usize)?);
30//!     // -- pattern 16 (mutable reference)
31//!     bank.patterns.id_mut(&PatternId::try_from(15_usize)?);
32//!
33//!     // iterate over variants
34//!     for pattern_id in PatternId::iter() {
35//!         bank.patterns.id_ref(&pattern_id);
36//!     }
37//!
38//!     Ok(())
39//! }
40//! ```
41//!
42//! # Special Case: [`SavedState`]
43//!
44//! In this library the [`SavedState`] enum "identifies" which instance you want to retrieve data
45//! from.
46//! One could argue that's not necessarily true.
47//! But it feels right to me in terms of the way it gets used.
48//!
49//! Please read the documentation for [`SavedState`] for a more detailed explaination.
50//!
51//! # Special Case: [`PlaybackSlotId`]
52//!
53//! The identifier value for a slot is slightly different depending on which types you're using.
54//!
55//! Please read the documentation for [`PlaybackSlotId`] for a more detailed explaination.
56
57use crate::OtToolsIoError;
58use enum_iterator::{all, Sequence};
59use ot_tools_io_derive::enum_try_from_u32_to_unsigned_types;
60use std::cmp::Ordering;
61
62/// Identifier values for the saved state of some data / files.
63///
64/// Most "identifier" types in this module use a series of alphanumeric characters to
65/// identify an instance in the same manner that the Octatrack uses,
66/// e.g. [`TrackId::One`] or [`BankId::A`]
67///
68/// This [`SavedState`] "identifier" type is different.
69/// It will never be used with `id*()` methods to access the members of a collection.
70/// It is used in two locations to model Octatrack behaviour:
71/// - Accessing the saved or unsaved set of [`Parts`][parts] within a
72///   [`BankFile`][bank] via the `parts()` method.
73/// - Identifying which [`ProjectFile`][proj], [`BankFile`][bank], [`ArrangementFile`][arr] or
74///   [`MarkersFile`][marks] should be read from / written to.
75///
76/// [parts]: crate::generics::Parts
77/// [proj]: crate::ProjectFile
78/// [bank]: crate::BankFile
79/// [marks]: crate::MarkersFile
80/// [arr]: crate::ArrangementFile
81#[derive(Clone, Copy, PartialEq, Debug, Hash, PartialOrd, Ord, Eq)]
82pub enum SavedState {
83    /// `*.work` files -- working copy
84    ///
85    /// # Note on File Creates/Writes
86    ///
87    /// ## Whole Project
88    ///
89    /// Data is written to all `*.work` files when either a `SYNC TO CARD` or a `SAVE PROJECT`
90    /// operation is executed from the Octatrack's Project menu.
91    ///
92    // todo: check this
93    /// ## Save Bank
94    ///
95    /// Data is written to `bank<ID>.work` file each time the `SAVE BANK` operation is executed from
96    /// the Octatrack's Project menu.
97    ///
98    // todo: check this
99    /// ## Save Arrangement
100    ///
101    /// Data is written to `arr<ID>.work` file when the `SAVE ARRANGEMENT` operation is executed
102    /// from the Octatrack's Arranger sub-menu.
103    ///
104    Working,
105    /// `*.strd` files -- restorable, stored copy.
106    ///
107    /// # Note on File Creates/Writes
108    ///
109    /// ## Whole Project
110    ///
111    /// Data is written to new `*.strd` files on the first `SAVE PROJECT` operation from
112    /// the Octatrack's project menu.
113    /// Subsequent `SAVE PROJECT` operations then save to those same files.
114    ///
115    // todo: check this
116    /// ## Save Bank
117    ///
118    /// A `bank<ID>.strd` file is created when the `SAVE BANK` operation is first executed from
119    /// the Octatrack's Project menu.
120    /// Data is written to the `bank<ID>.strd` file each time the `SAVE BANK` operation is executed
121    /// from the Octatrack's Project menu.
122    ///
123    // todo: check this
124    /// ## Save Arrangement
125    ///
126    /// A `arr<ID>.strd` file is created when the `SAVE ARRANGEMENT` operation is first executed
127    /// from the Octatrack's Arranger sub-menu.
128    /// Data is written to the `arr<ID>.strd` file each time the `SAVE ARRANGEMENT` operation is
129    /// executed from the Octatrack's Arranger sub-menu.
130    Stored,
131}
132
133impl SavedState {
134    /// Returns the relevant file extension for the saved state, for the files that support it.
135    pub fn file_extension(&self) -> String {
136        match self {
137            SavedState::Working => "work".to_string(),
138            SavedState::Stored => "strd".to_string(),
139        }
140    }
141}
142
143impl std::fmt::Display for SavedState {
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        write!(f, "{self:#?} state")
146    }
147}
148
149/// Identifier values for [`ArrangementFiles`][arrs], allowing for panic-free indexing via `id*` methods.
150///
151/// [arrs]: crate::generics::Arrangements
152#[derive(Clone, Copy, PartialEq, Debug, Hash, PartialOrd, Ord, Eq, Sequence)]
153#[repr(u8)]
154pub enum ArrId {
155    One = 0,
156    Two = 1,
157    Three = 2,
158    Four = 3,
159    Five = 4,
160    Six = 5,
161    Seven = 6,
162    Eight = 7,
163}
164
165impl ArrId {
166    /// Get the identifier variant as a zero-indexed integer for raw array indexing
167    pub fn as_index(&self) -> usize {
168        *self as usize
169    }
170
171    /// Get the relevant file name path component for a [`crate::arrangements::ArrangementFile`]
172    /// given the identifier's value
173    pub fn file_name(&self) -> String {
174        format!["arr{:0>2}", *self as u8 + 1].to_string()
175    }
176
177    /// Get the relevant file stem path component for a [`crate::arrangements::ArrangementFile`]
178    /// given the identifier's value and some [`SavedState`]
179    pub fn file_stem(&self, state: &SavedState) -> String {
180        format![
181            "{name}.{ext}",
182            name = self.file_name(),
183            ext = state.file_extension()
184        ]
185        .to_string()
186    }
187
188    /// Iterate over all possible enum variants
189    pub fn iter() -> impl Iterator<Item = Self> {
190        all::<Self>()
191    }
192}
193
194impl std::fmt::Display for ArrId {
195    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196        write!(f, "Arrangement {self:#?}")
197    }
198}
199
200#[enum_try_from_u32_to_unsigned_types]
201impl TryFrom<u32> for ArrId {
202    type Error = OtToolsIoError;
203
204    fn try_from(value: u32) -> Result<Self, Self::Error> {
205        match value {
206            0 => Ok(Self::One),
207            1 => Ok(Self::Two),
208            2 => Ok(Self::Three),
209            3 => Ok(Self::Four),
210            4 => Ok(Self::Five),
211            5 => Ok(Self::Six),
212            6 => Ok(Self::Seven),
213            7 => Ok(Self::Eight),
214            _ => Err(OtToolsIoError::InvalidIndex),
215        }
216    }
217}
218
219/// Identifier values for [`Banks`][crate::generics::Banks], allowing for panic-free indexing via `id*` methods.
220// todo: example (load blank project)
221#[derive(Clone, Copy, PartialEq, Debug, Hash, PartialOrd, Ord, Eq, Sequence)]
222#[repr(u8)]
223pub enum BankId {
224    A = 0,
225    B = 1,
226    C = 2,
227    D = 3,
228    E = 4,
229    F = 5,
230    G = 6,
231    H = 7,
232    I = 8,
233    J = 9,
234    K = 10,
235    L = 11,
236    M = 12,
237    N = 13,
238    O = 14,
239    P = 15,
240}
241
242impl BankId {
243    /// Get the identifier variant as a zero-indexed integer for raw array indexing
244    pub fn as_index(&self) -> usize {
245        *self as usize
246    }
247
248    /// Get the relevant file name path component for a [`crate::banks::BankFile`] given the
249    /// identifier's value
250    pub fn file_name(&self) -> String {
251        format!["bank{:0>2}", self.as_index() + 1].to_string()
252    }
253
254    /// Get the relevant file stem path component for a [`crate::banks::BankFile`] given the
255    /// identifier's value and some [`SavedState`]
256    pub fn file_stem(&self, state: &SavedState) -> String {
257        format![
258            "{name}.{ext}",
259            name = self.file_name(),
260            ext = state.file_extension()
261        ]
262        .to_string()
263    }
264
265    /// Iterate over all possible enum variants
266    pub fn iter() -> impl Iterator<Item = Self> {
267        all::<Self>()
268    }
269}
270
271impl std::fmt::Display for BankId {
272    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273        write!(f, "Bank {self:#?}")
274    }
275}
276
277#[enum_try_from_u32_to_unsigned_types]
278impl TryFrom<u32> for BankId {
279    type Error = OtToolsIoError;
280
281    fn try_from(value: u32) -> Result<Self, Self::Error> {
282        match value {
283            0 => Ok(Self::A),
284            1 => Ok(Self::B),
285            2 => Ok(Self::C),
286            3 => Ok(Self::D),
287            4 => Ok(Self::E),
288            5 => Ok(Self::F),
289            6 => Ok(Self::G),
290            7 => Ok(Self::H),
291            8 => Ok(Self::I),
292            9 => Ok(Self::J),
293            10 => Ok(Self::K),
294            11 => Ok(Self::L),
295            12 => Ok(Self::M),
296            13 => Ok(Self::N),
297            14 => Ok(Self::O),
298            15 => Ok(Self::P),
299            _ => Err(OtToolsIoError::InvalidIndex),
300        }
301    }
302}
303
304/// Identifier values for [`Parts`][crate::generics::Parts], allowing for panic-free indexing via `id*` methods.
305#[derive(Clone, Copy, PartialEq, Debug, Hash, PartialOrd, Ord, Eq, Sequence)]
306#[repr(u8)]
307pub enum PartId {
308    One = 0,
309    Two = 1,
310    Three = 2,
311    Four = 3,
312}
313
314impl PartId {
315    /// Get the identifier variant as a zero-indexed integer for raw array indexing
316    pub fn as_index(&self) -> usize {
317        *self as usize
318    }
319
320    /// Iterate over all possible enum variants
321    pub fn iter() -> impl Iterator<Item = Self> {
322        all::<Self>()
323    }
324}
325
326impl std::fmt::Display for PartId {
327    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
328        write!(f, "Part {self:#?}")
329    }
330}
331
332#[enum_try_from_u32_to_unsigned_types]
333impl TryFrom<u32> for PartId {
334    type Error = OtToolsIoError;
335
336    fn try_from(value: u32) -> Result<Self, Self::Error> {
337        match value {
338            0 => Ok(Self::One),
339            1 => Ok(Self::Two),
340            2 => Ok(Self::Three),
341            3 => Ok(Self::Four),
342            _ => Err(OtToolsIoError::InvalidIndex),
343        }
344    }
345}
346
347/// Identifier values for [`Patterns`][crate::generics::Patterns], allowing for panic-free indexing via `id*` methods.
348///
349/// ```
350/// use ot_tools_io::identifiers::PatternId;
351/// use ot_tools_io::BankFile;
352/// # use ot_tools_io::OtToolsIoError;
353///
354/// # fn main() -> Result<(), OtToolsIoError> {
355/// // mutable so we can demonstrate using `id_mut` methods
356/// let mut bank = BankFile::default();
357///
358/// // using variant by hand
359/// // -- pattern 1 (reference)
360/// bank.patterns.id_ref(&PatternId::One);
361/// // -- pattern 16 (mutable reference)
362/// bank.patterns.id_mut(&PatternId::Sixteen);
363///
364/// // lookup from an unsigned integer value
365/// // -- pattern 1 (reference)
366/// bank.patterns.id_ref(&PatternId::try_from(0_usize)?);
367/// // -- pattern 16 (mutable reference)
368/// bank.patterns.id_mut(&PatternId::try_from(15_usize)?);
369///
370/// // iterate over variants
371/// for pattern_id in PatternId::iter() {
372///     bank.patterns.id_ref(&pattern_id);
373/// }
374///
375/// # Ok(())
376/// # }
377/// ```
378#[derive(Clone, Copy, PartialEq, Debug, Hash, PartialOrd, Ord, Eq, Sequence)]
379#[repr(u8)]
380pub enum PatternId {
381    One = 0,
382    Two = 1,
383    Three = 2,
384    Four = 3,
385    Five = 4,
386    Six = 5,
387    Seven = 6,
388    Eight = 7,
389    Nine = 8,
390    Ten = 9,
391    Eleven = 10,
392    Twelve = 11,
393    Thirteen = 12,
394    Fourteen = 13,
395    Fifteen = 14,
396    Sixteen = 15,
397}
398
399impl PatternId {
400    /// Get the identifier variant as a zero-indexed integer for raw array indexing
401    pub fn as_index(&self) -> usize {
402        *self as usize
403    }
404
405    /// Iterate over all possible enum variants
406    pub fn iter() -> impl Iterator<Item = Self> {
407        all::<Self>()
408    }
409}
410
411impl std::fmt::Display for PatternId {
412    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
413        write!(f, "Pattern {self:#?}")
414    }
415}
416
417#[enum_try_from_u32_to_unsigned_types]
418impl TryFrom<u32> for PatternId {
419    type Error = OtToolsIoError;
420
421    fn try_from(value: u32) -> Result<Self, Self::Error> {
422        match value {
423            0 => Ok(Self::One),
424            1 => Ok(Self::Two),
425            2 => Ok(Self::Three),
426            3 => Ok(Self::Four),
427            4 => Ok(Self::Five),
428            5 => Ok(Self::Six),
429            6 => Ok(Self::Seven),
430            7 => Ok(Self::Eight),
431            8 => Ok(Self::Nine),
432            9 => Ok(Self::Ten),
433            10 => Ok(Self::Eleven),
434            11 => Ok(Self::Twelve),
435            12 => Ok(Self::Thirteen),
436            13 => Ok(Self::Fourteen),
437            14 => Ok(Self::Fifteen),
438            15 => Ok(Self::Sixteen),
439            _ => Err(OtToolsIoError::InvalidIndex),
440        }
441    }
442}
443
444/// Identifier values for [`Tracks`][crate::generics::Tracks], allowing for panic-free indexing via `id*` methods.
445#[derive(Clone, Copy, PartialEq, Debug, Hash, PartialOrd, Ord, Eq, Sequence)]
446#[repr(u8)]
447pub enum TrackId {
448    One = 0,
449    Two = 1,
450    Three = 2,
451    Four = 3,
452    Five = 4,
453    Six = 5,
454    Seven = 6,
455    Eight = 7,
456}
457
458impl TrackId {
459    /// Get the identifier variant as a zero-indexed integer for raw array indexing
460    pub fn as_index(&self) -> usize {
461        *self as usize
462    }
463
464    /// Iterate over all possible enum variants
465    pub fn iter() -> impl Iterator<Item = Self> {
466        all::<Self>()
467    }
468}
469
470impl std::fmt::Display for TrackId {
471    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
472        write!(f, "Track {self:#?}")
473    }
474}
475
476#[enum_try_from_u32_to_unsigned_types]
477impl TryFrom<u32> for TrackId {
478    type Error = OtToolsIoError;
479
480    fn try_from(value: u32) -> Result<Self, Self::Error> {
481        match value {
482            0 => Ok(Self::One),
483            1 => Ok(Self::Two),
484            2 => Ok(Self::Three),
485            3 => Ok(Self::Four),
486            4 => Ok(Self::Five),
487            5 => Ok(Self::Six),
488            6 => Ok(Self::Seven),
489            7 => Ok(Self::Eight),
490            _ => Err(OtToolsIoError::InvalidIndex),
491        }
492    }
493}
494
495/// Identifier values for [`Scenes`][crate::generics::Scenes], allowing for panic-free indexing via `id*` methods.
496///
497/// ```
498/// use ot_tools_io::parts::{SceneXlvAssignments};
499/// use ot_tools_io::identifiers::{SavedState, PartId, SceneId};
500/// use ot_tools_io::BankFile;
501/// # use ot_tools_io::OtToolsIoError;
502///
503/// # fn main() -> Result<(), OtToolsIoError> {
504/// // mutable ref so we can demonstrate using `id_mut` methods
505/// let mut bank = BankFile::default();
506/// let part_one_working = bank
507///     .parts_mut(&SavedState::Working)
508///     .id_mut(&PartId::One);
509///
510/// // using variant by hand
511/// // -- scene 1 (reference)
512/// part_one_working.scene_xlvs.id_ref(&SceneId::One);
513/// // -- scene 16 (mutable reference)
514/// part_one_working.scene_xlvs.id_mut(&SceneId::Sixteen);
515///
516/// // lookup from an unsigned integer value
517/// // failed conversions return an `OtToolsIoError::InvalidIndex` error for you to handle
518/// // -- scene 1 (reference)
519/// part_one_working.scene_xlvs.id_ref(&SceneId::try_from(0_usize)?);
520/// // -- scene 16 (mutable reference)
521/// part_one_working.scene_xlvs.id_mut(&SceneId::try_from(15_usize)?);
522///
523/// // iterate over variants
524/// for scene_id in SceneId::iter() {
525///     part_one_working.scene_xlvs.id_ref(&scene_id);
526/// }
527///
528/// # Ok(())
529/// # }
530/// ```
531#[derive(Clone, Copy, PartialEq, Debug, Hash, PartialOrd, Ord, Eq, Sequence)]
532#[repr(u8)]
533pub enum SceneId {
534    One = 0,
535    Two = 1,
536    Three = 2,
537    Four = 3,
538    Five = 4,
539    Six = 5,
540    Seven = 6,
541    Eight = 7,
542    Nine = 8,
543    Ten = 9,
544    Eleven = 10,
545    Twelve = 11,
546    Thirteen = 12,
547    Fourteen = 13,
548    Fifteen = 14,
549    Sixteen = 15,
550}
551
552impl SceneId {
553    /// Get the identifier variant as a zero-indexed integer for raw array indexing
554    pub fn as_index(&self) -> usize {
555        *self as usize
556    }
557
558    /// Iterate over all possible enum variants
559    pub fn iter() -> impl Iterator<Item = Self> {
560        all::<Self>()
561    }
562}
563
564impl std::fmt::Display for SceneId {
565    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
566        write!(f, "Scene {self:#?}")
567    }
568}
569
570#[enum_try_from_u32_to_unsigned_types]
571impl TryFrom<u32> for SceneId {
572    type Error = OtToolsIoError;
573
574    fn try_from(value: u32) -> Result<Self, Self::Error> {
575        match value {
576            0 => Ok(Self::One),
577            1 => Ok(Self::Two),
578            2 => Ok(Self::Three),
579            3 => Ok(Self::Four),
580            4 => Ok(Self::Five),
581            5 => Ok(Self::Six),
582            6 => Ok(Self::Seven),
583            7 => Ok(Self::Eight),
584            8 => Ok(Self::Nine),
585            9 => Ok(Self::Ten),
586            10 => Ok(Self::Eleven),
587            11 => Ok(Self::Twelve),
588            12 => Ok(Self::Thirteen),
589            13 => Ok(Self::Fourteen),
590            14 => Ok(Self::Fifteen),
591            15 => Ok(Self::Sixteen),
592            _ => Err(OtToolsIoError::InvalidIndex),
593        }
594    }
595}
596
597/// Identifier values for [`Trigs`][crate::generics::Trigs], allowing panic-free access to elements of
598/// the internal collection via `id*()` methods.
599#[derive(Clone, Copy, PartialEq, Debug, Hash, PartialOrd, Ord, Eq, Sequence)]
600#[repr(u8)]
601pub enum TrigId {
602    One = 0,
603    Two = 1,
604    Three = 2,
605    Four = 3,
606    Five = 4,
607    Six = 5,
608    Seven = 6,
609    Eight = 7,
610    Nine = 8,
611    Ten = 9,
612    //
613    Eleven = 10,
614    Twelve = 11,
615    Thirteen = 12,
616    Fourteen = 13,
617    Fifteen = 14,
618    Sixteen = 15,
619    Seventeen = 16,
620    Eighteen = 17,
621    Nineteen = 18,
622    Twenty = 19,
623    //
624    TwentyOne = 20,
625    TwentyTwo = 21,
626    TwentyThree = 22,
627    TwentyFour = 23,
628    TwentyFive = 24,
629    TwentySix = 25,
630    TwentySeven = 26,
631    TwentyEight = 27,
632    TwentyNine = 28,
633    Thirty = 29,
634    //
635    ThirtyOne = 30,
636    ThirtyTwo = 31,
637    ThirtyThree = 32,
638    ThirtyFour = 33,
639    ThirtyFive = 34,
640    ThirtySix = 35,
641    ThirtySeven = 36,
642    ThirtyEight = 37,
643    ThirtyNine = 38,
644    Forty = 39,
645    //
646    FortyOne = 40,
647    FortyTwo = 41,
648    FortyThree = 42,
649    FortyFour = 43,
650    FortyFive = 44,
651    FortySix = 45,
652    FortySeven = 46,
653    FortyEight = 47,
654    FortyNine = 48,
655    Fifty = 49,
656    //
657    FiftyOne = 50,
658    FiftyTwo = 51,
659    FiftyThree = 52,
660    FiftyFour = 53,
661    FiftyFive = 54,
662    FiftySix = 55,
663    FiftySeven = 56,
664    FiftyEight = 57,
665    FiftyNine = 58,
666    Sixty = 59,
667    //
668    SixtyOne = 60,
669    SixtyTwo = 61,
670    SixtyThree = 62,
671    SixtyFour = 63,
672}
673
674impl TrigId {
675    /// Get the identifier variant as a zero-indexed integer for raw array indexing
676    pub fn as_index(&self) -> usize {
677        *self as usize
678    }
679
680    /// Iterate over all possible enum variants
681    pub fn iter() -> impl Iterator<Item = Self> {
682        all::<Self>()
683    }
684}
685
686#[enum_try_from_u32_to_unsigned_types]
687impl TryFrom<u32> for TrigId {
688    type Error = OtToolsIoError;
689
690    fn try_from(value: u32) -> Result<Self, Self::Error> {
691        match value {
692            0 => Ok(Self::One),
693            1 => Ok(Self::Two),
694            2 => Ok(Self::Three),
695            3 => Ok(Self::Four),
696            4 => Ok(Self::Five),
697            5 => Ok(Self::Six),
698            6 => Ok(Self::Seven),
699            7 => Ok(Self::Eight),
700            8 => Ok(Self::Nine),
701            9 => Ok(Self::Ten),
702            //
703            10 => Ok(Self::Eleven),
704            11 => Ok(Self::Twelve),
705            12 => Ok(Self::Thirteen),
706            13 => Ok(Self::Fourteen),
707            14 => Ok(Self::Fifteen),
708            15 => Ok(Self::Sixteen),
709            16 => Ok(Self::Seventeen),
710            17 => Ok(Self::Eighteen),
711            18 => Ok(Self::Nineteen),
712            19 => Ok(Self::Twenty),
713            //
714            20 => Ok(Self::TwentyOne),
715            21 => Ok(Self::TwentyTwo),
716            22 => Ok(Self::TwentyThree),
717            23 => Ok(Self::TwentyFour),
718            24 => Ok(Self::TwentyFive),
719            25 => Ok(Self::TwentySix),
720            26 => Ok(Self::TwentySeven),
721            27 => Ok(Self::TwentyEight),
722            28 => Ok(Self::TwentyNine),
723            29 => Ok(Self::Thirty),
724            //
725            30 => Ok(Self::ThirtyOne),
726            31 => Ok(Self::ThirtyTwo),
727            32 => Ok(Self::ThirtyThree),
728            33 => Ok(Self::ThirtyFour),
729            34 => Ok(Self::ThirtyFive),
730            35 => Ok(Self::ThirtySix),
731            36 => Ok(Self::ThirtySeven),
732            37 => Ok(Self::ThirtyEight),
733            38 => Ok(Self::ThirtyNine),
734            39 => Ok(Self::Forty),
735            //
736            40 => Ok(Self::FortyOne),
737            41 => Ok(Self::FortyTwo),
738            42 => Ok(Self::FortyThree),
739            43 => Ok(Self::FortyFour),
740            44 => Ok(Self::FortyFive),
741            45 => Ok(Self::FortySix),
742            46 => Ok(Self::FortySeven),
743            47 => Ok(Self::FortyEight),
744            48 => Ok(Self::FortyNine),
745            49 => Ok(Self::Fifty),
746            //
747            50 => Ok(Self::FiftyOne),
748            51 => Ok(Self::FiftyTwo),
749            52 => Ok(Self::FiftyThree),
750            53 => Ok(Self::FiftyFour),
751            54 => Ok(Self::FiftyFive),
752            55 => Ok(Self::FiftySix),
753            56 => Ok(Self::FiftySeven),
754            57 => Ok(Self::FiftyEight),
755            58 => Ok(Self::FiftyNine),
756            59 => Ok(Self::Sixty),
757            //
758            60 => Ok(Self::SixtyOne),
759            61 => Ok(Self::SixtyTwo),
760            62 => Ok(Self::SixtyThree),
761            63 => Ok(Self::SixtyFour),
762            //
763            _ => Err(OtToolsIoError::InvalidIndex),
764        }
765    }
766}
767
768/// Identifier values for [`Slices`][crate::generics::Slices], allowing panic-free access to
769/// elements of the internal collection via `id*()` methods.
770#[derive(Clone, Copy, PartialEq, Debug, Hash, PartialOrd, Ord, Eq, Sequence)]
771#[repr(u8)]
772pub enum SliceId {
773    One = 0,
774    Two = 1,
775    Three = 2,
776    Four = 3,
777    Five = 4,
778    Six = 5,
779    Seven = 6,
780    Eight = 7,
781    Nine = 8,
782    Ten = 9,
783    //
784    Eleven = 10,
785    Twelve = 11,
786    Thirteen = 12,
787    Fourteen = 13,
788    Fifteen = 14,
789    Sixteen = 15,
790    Seventeen = 16,
791    Eighteen = 17,
792    Nineteen = 18,
793    Twenty = 19,
794    //
795    TwentyOne = 20,
796    TwentyTwo = 21,
797    TwentyThree = 22,
798    TwentyFour = 23,
799    TwentyFive = 24,
800    TwentySix = 25,
801    TwentySeven = 26,
802    TwentyEight = 27,
803    TwentyNine = 28,
804    Thirty = 29,
805    //
806    ThirtyOne = 30,
807    ThirtyTwo = 31,
808    ThirtyThree = 32,
809    ThirtyFour = 33,
810    ThirtyFive = 34,
811    ThirtySix = 35,
812    ThirtySeven = 36,
813    ThirtyEight = 37,
814    ThirtyNine = 38,
815    Forty = 39,
816    //
817    FortyOne = 40,
818    FortyTwo = 41,
819    FortyThree = 42,
820    FortyFour = 43,
821    FortyFive = 44,
822    FortySix = 45,
823    FortySeven = 46,
824    FortyEight = 47,
825    FortyNine = 48,
826    Fifty = 49,
827    //
828    FiftyOne = 50,
829    FiftyTwo = 51,
830    FiftyThree = 52,
831    FiftyFour = 53,
832    FiftyFive = 54,
833    FiftySix = 55,
834    FiftySeven = 56,
835    FiftyEight = 57,
836    FiftyNine = 58,
837    Sixty = 59,
838    //
839    SixtyOne = 60,
840    SixtyTwo = 61,
841    SixtyThree = 62,
842    SixtyFour = 63,
843}
844
845impl SliceId {
846    /// Get the identifier variant as a zero-indexed integer for raw array indexing
847    pub fn as_index(&self) -> usize {
848        *self as usize
849    }
850
851    /// Iterate over all possible enum variants
852    pub fn iter() -> impl Iterator<Item = Self> {
853        all::<Self>()
854    }
855}
856
857#[enum_try_from_u32_to_unsigned_types]
858impl TryFrom<u32> for SliceId {
859    type Error = OtToolsIoError;
860
861    fn try_from(value: u32) -> Result<Self, Self::Error> {
862        match value {
863            0 => Ok(Self::One),
864            1 => Ok(Self::Two),
865            2 => Ok(Self::Three),
866            3 => Ok(Self::Four),
867            4 => Ok(Self::Five),
868            5 => Ok(Self::Six),
869            6 => Ok(Self::Seven),
870            7 => Ok(Self::Eight),
871            8 => Ok(Self::Nine),
872            9 => Ok(Self::Ten),
873            //
874            10 => Ok(Self::Eleven),
875            11 => Ok(Self::Twelve),
876            12 => Ok(Self::Thirteen),
877            13 => Ok(Self::Fourteen),
878            14 => Ok(Self::Fifteen),
879            15 => Ok(Self::Sixteen),
880            16 => Ok(Self::Seventeen),
881            17 => Ok(Self::Eighteen),
882            18 => Ok(Self::Nineteen),
883            19 => Ok(Self::Twenty),
884            //
885            20 => Ok(Self::TwentyOne),
886            21 => Ok(Self::TwentyTwo),
887            22 => Ok(Self::TwentyThree),
888            23 => Ok(Self::TwentyFour),
889            24 => Ok(Self::TwentyFive),
890            25 => Ok(Self::TwentySix),
891            26 => Ok(Self::TwentySeven),
892            27 => Ok(Self::TwentyEight),
893            28 => Ok(Self::TwentyNine),
894            29 => Ok(Self::Thirty),
895            //
896            30 => Ok(Self::ThirtyOne),
897            31 => Ok(Self::ThirtyTwo),
898            32 => Ok(Self::ThirtyThree),
899            33 => Ok(Self::ThirtyFour),
900            34 => Ok(Self::ThirtyFive),
901            35 => Ok(Self::ThirtySix),
902            36 => Ok(Self::ThirtySeven),
903            37 => Ok(Self::ThirtyEight),
904            38 => Ok(Self::ThirtyNine),
905            39 => Ok(Self::Forty),
906            //
907            40 => Ok(Self::FortyOne),
908            41 => Ok(Self::FortyTwo),
909            42 => Ok(Self::FortyThree),
910            43 => Ok(Self::FortyFour),
911            44 => Ok(Self::FortyFive),
912            45 => Ok(Self::FortySix),
913            46 => Ok(Self::FortySeven),
914            47 => Ok(Self::FortyEight),
915            48 => Ok(Self::FortyNine),
916            49 => Ok(Self::Fifty),
917            //
918            50 => Ok(Self::FiftyOne),
919            51 => Ok(Self::FiftyTwo),
920            52 => Ok(Self::FiftyThree),
921            53 => Ok(Self::FiftyFour),
922            54 => Ok(Self::FiftyFive),
923            55 => Ok(Self::FiftySix),
924            56 => Ok(Self::FiftySeven),
925            57 => Ok(Self::FiftyEight),
926            58 => Ok(Self::FiftyNine),
927            59 => Ok(Self::Sixty),
928            //
929            60 => Ok(Self::SixtyOne),
930            61 => Ok(Self::SixtyTwo),
931            62 => Ok(Self::SixtyThree),
932            63 => Ok(Self::SixtyFour),
933            //
934            _ => Err(OtToolsIoError::InvalidIndex),
935        }
936    }
937}
938
939/// Identifier values for [`PlaybackSlots`][crate::generics::PlaybackSlots].
940/// Please read the documentation to understand the special cases related to this type, why it
941/// currently exists, why it may not exist for much longer and examples etc.
942///
943/// # Why is [`PlaybackSlotId`] a `struct` newtype and not an `enum` like every other "ID" type?
944///
945/// Honestly, because typing out values `SlotId::One` through to `SlotId::OneTwentyEight` was just
946/// not something i have been remotely keen on doing.
947/// So, for now this is staying as a newtype `struct` and needs to be used slightly differently to
948/// the other ID types.
949///
950/// # Explainer
951///
952/// One of the frustrating things about slot data is the fact that there are three different
953/// indexing or "identifier" values for looking up or referencing a "slot".
954/// Making life more complicated -- each special case is related to a completely different data file.
955///
956/// The first usage is the value used by both [`AudioTrackMachineSlot`][2] and
957/// [`AudioTrackParameterLocks`][3] to indicate the slot currently in use by an audio track.
958/// This first value is a **ZERO**-indexed `u8` **INDEX** value.
959///
960/// The second usage is the array data storing [`SlotMarkers`][4] data.
961/// The "identifier" here is not a value in the actual data as such.
962/// Rather we do standard rust array element index lookup with a **ZERO**-indexed `usize` **INDEX**
963/// value.
964///
965/// The third usage is the 'setttings' data stored as a [`SlotAttributes`][5] in a
966/// [`crate::ProjectFile`].
967/// The raw data is a **ONE**-indexed `String` value (the whole file is string data),
968/// and this library currently parses this value into a `u8` **ID** value (_not_ an **INDEX** value!).
969///
970/// Just to summarise that to make the point absolutely clear -- the three different
971/// indexing/"identifier" types are
972/// 1. zero-index `u8` -- an INDEX value
973/// 2. zero-index `usize` -- an INDEX value
974/// 3. one-index `String` -> one-index `u8` -- an ID value (Slot ID in [`SlotAttributes`][5])
975///
976/// The net result of this is that you sometimes have to track THREE different index/ID values of a
977/// slot depending on what you're trying to do.
978/// ```compile_fail
979/// // some ProjectFile one-index slot id value
980/// let slot_id = slot_attr.slot_id;
981///
982/// // convert to bank slot data zero-index u8
983/// // -- btw, I hope you didn't accidentally set the `slot_id` field to zero by accident!
984/// // -- otherwise you'll panic here!
985/// let slot_bank_index = slot_id - 1;
986///
987/// // convert to a zero-index `usize` for accessing markers data
988/// // -- same panic issue applies!
989/// let slot_markers_index = (slot_id - 1) as usize;
990/// ```
991///
992/// This starts becoming a MASSIVE headache the more you come up against it more than once.
993/// It is VERY easy to lose track of which variable tracks which index if you're not super explicit
994/// with your naming conventions.
995/// I lost count of the number of times I've had to re-read a massive function to work out if
996/// `slot_idx` is zero-index or one-index.
997///
998/// So, the current implementation for [`PlaybackSlotId`] is a quick initial attempt to address this issue
999/// (the idea then spawned the rest of the "identifier" types).
1000///
1001/// # Using [`PlaybackSlotId`]
1002///
1003/// You are currently required to manually convert from one of the types mentioned above into a
1004/// [`PlaybackSlotId`] type, which means being aware of which type you are converting from.
1005///
1006/// There are two methods for converting from raw data values:
1007/// - `from_index` -- use this to get a [`PlaybackSlotId`] from a zero-index value
1008/// - `from_id` -- use this to get a [`PlaybackSlotId`] from a one-index value
1009///
1010/// Note that both methods return an `Option<SlotId>` to handle the case where you provide a bad
1011/// input value that exceeds the maximum possible index/id value
1012/// -- see the [`PlaybackSlotId::MAX_INDEX`] and [`PlaybackSlotId::MAX_ID`] const values.
1013///
1014/// Once you have a [`PlaybackSlotId`], there are two similar methods for converting back to a raw data
1015/// value:
1016/// - `as_index` -- use this to get a zero-index `usize` value
1017/// - `as_id` -- use this to get a one-index `u8` value
1018///
1019/// ```
1020/// # use ot_tools_io::identifiers::PlaybackSlotId;
1021/// // one-index ID from ProjectFile data
1022/// // into a zero-index INDEX for bank data/markers array indexing
1023/// assert_eq!(PlaybackSlotId::from_id(100).unwrap().as_index(), 99);
1024///
1025/// // zero-index index value from bank data or the markers array index
1026/// // into a one-index ID value for ProjectFile data
1027/// assert_eq!(PlaybackSlotId::from_index(99).unwrap().as_id(), 100);
1028/// ```
1029///
1030/// None of this is ideal, but it's definitely an improvement on what it was like before!
1031/// You at least end up with a standard [`PlaybackSlotId`] type once you've converted the appropriate value!
1032/// As explained above, previously you'd just have to remember if some variable was zero or one
1033/// indexed.
1034/// At least now you just need to know whether the function/method/specific code block where you're
1035/// passing/using the [`PlaybackSlotId`] type needs a one-index ID or a zero-index Index.
1036///
1037/// # Future Work
1038///
1039/// In future, I would like to implement [`serde::Serialize`] and [`serde::Deserialize`] more
1040/// fully for [`AudioTrackMachineSlot`][2], [`AudioTrackParameterLocks`][3] and [`SlotAttributes`][5];
1041/// with the intention being that the relevant slot ID field(s) of each type deserializes to this
1042/// [`PlaybackSlotId`] type without having to manually convert it in downstream code.
1043/// Additionally, I may follow on from the recent work on Recording Buffers
1044/// (using the [`crate::generics::Tracks`] generic collection helper instead of bundling them in with Flex Slots)
1045/// and break from the convention of trying to keep all data as close to raw binary values as
1046/// possible, parsing the `SLOT_ID` field in [`SlotAttributes`][5] data as zero-index.
1047///
1048/// However, that work is being left to the future for now, although it's hopefully going to come in
1049/// one of the next major releases (if not the next major release).
1050///
1051/// [2]: crate::parts::AudioTrackMachineSlot
1052/// [3]: crate::patterns::AudioTrackParameterLocks
1053/// [4]: crate::markers::MarkersFile
1054/// [5]: crate::projects::SlotAttributes
1055#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1056pub struct PlaybackSlotId(usize);
1057
1058impl PlaybackSlotId {
1059    // DOES NOT SUPPORT RECORDER SLOTS!
1060    pub const MAX_INDEX: usize = 127;
1061    pub const MAX_ID: usize = 128;
1062
1063    /// Read a zero-index INDEX value to get a [`PlaybackSlotId`].
1064    /// INDEX values are used in certain [`crate::banks::BankFile`] subtypes and as indices for the
1065    /// markers arrays in [`crate::markers::MarkersFile`]
1066    pub fn from_index(v: usize) -> Option<Self> {
1067        if v > Self::MAX_INDEX {
1068            None
1069        } else {
1070            Some(PlaybackSlotId(v))
1071        }
1072    }
1073
1074    /// Read a one-index ID value to get a [`PlaybackSlotId`].
1075    /// The ID values is used in [`crate::projects::SlotAttributes`].
1076    pub fn from_id(v: u8) -> Option<Self> {
1077        if v == 0 || v > Self::MAX_ID as u8 {
1078            None
1079        } else {
1080            Some(PlaybackSlotId(v as usize - 1))
1081        }
1082    }
1083
1084    /// Return a zero-index INDEX value for the [`PlaybackSlotId`], used in certain
1085    /// [`crate::banks::BankFile`] subtypes and to index the markers arrays in
1086    /// [`crate::markers::MarkersFile`]
1087    pub fn as_index(&self) -> usize {
1088        self.0
1089    }
1090
1091    /// Return a one-index ID value for the [`PlaybackSlotId`], used in [`crate::projects::SlotAttributes`]
1092    pub fn as_id(&self) -> u8 {
1093        (self.0 + 1) as u8
1094    }
1095
1096    /// Iterate over all possible INDEX values for a [`PlaybackSlotId`]
1097    pub fn iter_all() -> impl Iterator<Item = Self> {
1098        (0..(Self::MAX_INDEX + 1)).flat_map(Self::from_index)
1099    }
1100}
1101
1102impl std::fmt::Display for PlaybackSlotId {
1103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1104        write!(f, "index={:#?} id={:#?}", self.as_index(), self.as_id())
1105    }
1106}
1107
1108impl PartialOrd for PlaybackSlotId {
1109    fn partial_cmp(&self, other: &PlaybackSlotId) -> Option<Ordering> {
1110        Some(self.cmp(other))
1111    }
1112}
1113
1114impl Ord for PlaybackSlotId {
1115    fn cmp(&self, other: &Self) -> Ordering {
1116        if self.0 > other.0 {
1117            Ordering::Greater
1118        } else if self.0 < other.0 {
1119            Ordering::Less
1120        } else {
1121            Ordering::Equal
1122        }
1123    }
1124}
1125
1126/// Identifier values for [`RecordingBufferSlots`][crate::generics::RecordingBufferSlots],
1127/// allowing for panic-free indexing via `id*` methods.
1128#[derive(Clone, Copy, PartialEq, Debug, Hash, PartialOrd, Ord, Eq, Sequence)]
1129#[repr(u8)]
1130pub enum RecBufId {
1131    One = 0,
1132    Two = 1,
1133    Three = 2,
1134    Four = 3,
1135    Five = 4,
1136    Six = 5,
1137    Seven = 6,
1138    Eight = 7,
1139}
1140
1141impl RecBufId {
1142    /// Get the identifier variant as a zero-indexed integer for raw array indexing
1143    pub fn as_index(&self) -> usize {
1144        *self as usize
1145    }
1146
1147    /// Iterate over all possible enum variants
1148    pub fn iter() -> impl Iterator<Item = Self> {
1149        all::<Self>()
1150    }
1151}
1152
1153impl std::fmt::Display for RecBufId {
1154    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1155        write!(f, "Track {self:#?}")
1156    }
1157}
1158
1159#[enum_try_from_u32_to_unsigned_types]
1160impl TryFrom<u32> for RecBufId {
1161    type Error = OtToolsIoError;
1162
1163    fn try_from(value: u32) -> Result<Self, Self::Error> {
1164        match value {
1165            0 => Ok(Self::One),
1166            1 => Ok(Self::Two),
1167            2 => Ok(Self::Three),
1168            3 => Ok(Self::Four),
1169            4 => Ok(Self::Five),
1170            5 => Ok(Self::Six),
1171            6 => Ok(Self::Seven),
1172            7 => Ok(Self::Eight),
1173            _ => Err(OtToolsIoError::InvalidIndex),
1174        }
1175    }
1176}