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}