Skip to main content

ot_tools_io/
generics.rs

1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2024 Mike Robeson [dijksterhuis]
4*/
5
6use crate::arrangements::ArrangeRow;
7use crate::identifiers::{
8    ArrId, BankId, PartId, PatternId, PlaybackSlotId, RecBufId, SavedState, SceneId, SliceId,
9    TrackId, TrigId,
10};
11use crate::macros::*;
12use crate::markers::SlotMarkers;
13use crate::parts::Part;
14use crate::patterns::Pattern;
15use crate::projects::SlotAttributes;
16use crate::{
17    ArrangementFile, BankFile, Defaults, HasHeaderField, OctatrackFileIO, OtToolsIoError,
18    SampleSettingsFile,
19};
20use serde::{Deserialize, Deserializer, Serialize, Serializer};
21use serde_big_array::BigArray;
22use std::array::from_fn;
23use std::ops::{Deref, DerefMut, Index, IndexMut};
24use std::path::PathBuf;
25
26/// Generic collection for [`ArrangementFile`]s.
27///
28/// # [`Serialize`]/[`Deserialize`]
29///
30/// Neither [`Serialize`] nor [`Deserialize`] are implemented for this type.
31///
32/// # `Box` (no `Copy`)
33///
34/// The inner array of this type is wrapped in a `Box`.
35/// -- we cannot fit all [`ArrangementFile`]s on the stack.
36/// Unfortunately this means the type does not implement `Copy`.
37///
38/// # Example: Extending in your own code with custom inner types
39/// ```
40/// use std::path::PathBuf;
41/// use ot_tools_io::types::Arrangements;
42///
43/// use ot_tools_io::Defaults;
44/// use ot_tools_io_derive::{ArrayDefaults, BoxedArrayDefaults};
45/// use std::array::from_fn;
46///
47/// #[derive(Debug, PartialEq, Default, ArrayDefaults, BoxedArrayDefaults)]
48/// pub struct BackupPath(PathBuf);
49///
50/// # fn main() -> Result<(), ()> {
51/// let mut my_backups = Arrangements::<BackupPath>::default();
52/// let my_new_backup = BackupPath(PathBuf::from("some_path"));
53/// let first = my_backups.first_mut().ok_or(())?;
54/// *first = my_new_backup;
55///
56/// // first one changed
57/// assert_eq!(my_backups[0], BackupPath(PathBuf::from("some_path")));
58/// // rest are default
59/// assert_eq!(my_backups[1], BackupPath::default());
60///
61/// // correct length
62/// assert_eq!(my_backups.len(), 8);
63///
64/// # Ok(()) }
65/// ```
66#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
67pub struct Arrangements<T>(Box<[T; 8]>);
68
69impl<T> Arrangements<T> {
70    pub fn new(value: [T; 8]) -> Self {
71        Self(Box::new(value))
72    }
73
74    pub fn new_boxed(value: Box<[T; 8]>) -> Self {
75        Self(value)
76    }
77}
78
79generic_newtype_id_lookups!(Arrangements, ArrId);
80generic_newtype_unbox!(Arrangements, 8);
81generic_newtype_asref!(Arrangements);
82generic_newtype_asmut!(Arrangements);
83generic_newtype_deref!(Arrangements, 8);
84generic_newtype_deref_mut!(Arrangements);
85
86impl<T: Defaults<Box<[T; 8]>> + Default> Default for Arrangements<T> {
87    fn default() -> Self {
88        Self(T::defaults())
89    }
90}
91
92impl HasHeaderField for Arrangements<ArrangementFile> {
93    fn check_header(&self) -> Result<bool, OtToolsIoError> {
94        Ok(self.iter().all(|x| x.check_header().is_ok_and(|x| x)))
95    }
96}
97
98/// Concrete implementation for [`ArrangementFile`] adds two methods for reading and writing a
99/// batch of files.
100impl Arrangements<ArrangementFile> {
101    /// Reads a collection of [`ArrangementFile`]s from a directory.
102    pub fn from_data_files<P>(dirpath: P, state: &SavedState) -> Result<Self, OtToolsIoError>
103    where
104        PathBuf: From<P>,
105    {
106        let mut new = Self::default();
107        let path = PathBuf::from(dirpath);
108        for id in ArrId::iter() {
109            let data = ArrangementFile::from_data_file(&path.join(id.file_stem(state)))?;
110            new[id.as_index()] = data;
111        }
112        Ok(new)
113    }
114
115    /// Writes a collection of [`ArrangementFile`]s to a directory.
116    ///
117    /// Will skip writing a particular [`ArrangementFile`] if the
118    /// `skip_unchanged` argument is `true` and the current [`ArrangementFile`]
119    /// matches the data in the existing file on the file system.
120    pub fn to_data_files<P>(
121        &self,
122        dirpath: P,
123        state: &SavedState,
124        skip_unchanged: bool,
125    ) -> Result<(), OtToolsIoError>
126    where
127        PathBuf: From<P>,
128    {
129        let path = PathBuf::from(dirpath);
130        for id in ArrId::iter() {
131            let arr = self.id_ref(&id);
132            if skip_unchanged {
133                if let Ok(exists) = ArrangementFile::from_data_file(&path.join(id.file_stem(state)))
134                {
135                    if &exists != arr {
136                        arr.to_data_file(&path.join(id.file_stem(state)))?;
137                    }
138                } else {
139                    // probably doesn't exist
140                    arr.to_data_file(&path.join(id.file_stem(state)))?;
141                }
142            } else {
143                arr.to_data_file(&path.join(id.file_stem(state)))?;
144            }
145        }
146        Ok(())
147    }
148}
149
150/// Generic array wrapper for [`ArrangeRow`]s.
151///
152/// # Example: Extending in your own code with custom inner types
153/// ```
154/// use ot_tools_io::types::ArrangeRows;
155///
156/// use ot_tools_io::Defaults;
157/// use ot_tools_io_derive::{ArrayDefaults, BoxedArrayDefaults};
158/// use std::array::from_fn;
159///
160/// #[derive(Debug, PartialEq, Default, ArrayDefaults, BoxedArrayDefaults)]
161/// pub struct CustomReminder(String);
162///
163/// # fn main() -> Result<(), ()> {
164/// let mut my_arr_reminders = ArrangeRows::<CustomReminder>::default();
165/// let first = my_arr_reminders.first_mut().ok_or(())?;
166/// *first = CustomReminder(
167///     String::from(
168///         "imagine this is a long reminder value that goes on for longer than normally allowed"
169///     )
170/// );
171///
172/// // first one changed
173/// assert_eq!(
174///     my_arr_reminders[0],
175///     CustomReminder(
176///         String::from(
177///             "imagine this is a long reminder value that goes on for longer than normally allowed"
178///         )
179///     )
180/// );
181/// // rest are default
182/// assert_eq!(my_arr_reminders[1], CustomReminder::default());
183///
184/// // correct length
185/// assert_eq!(my_arr_reminders.len(), 256);
186///
187/// # Ok(()) }
188/// ```
189#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
190pub struct ArrangeRows<T>([T; 256]);
191
192impl<T> ArrangeRows<T> {
193    pub fn new(value: [T; 256]) -> Self {
194        Self(value)
195    }
196}
197
198generic_newtype_asref!(ArrangeRows);
199generic_newtype_asmut!(ArrangeRows);
200generic_newtype_deref!(ArrangeRows, 256);
201generic_newtype_deref_mut!(ArrangeRows);
202
203impl<T: Defaults<[T; 256]> + Default> Default for ArrangeRows<T> {
204    fn default() -> Self {
205        Self(T::defaults())
206    }
207}
208
209impl<'de> Deserialize<'de> for ArrangeRows<ArrangeRow> {
210    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
211    where
212        D: Deserializer<'de>,
213    {
214        Ok(Self(
215            <[ArrangeRow; 256] as BigArray<ArrangeRow>>::deserialize(deserializer)?,
216        ))
217    }
218}
219
220impl Serialize for ArrangeRows<ArrangeRow> {
221    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
222    where
223        S: Serializer,
224    {
225        <[ArrangeRow; 256] as BigArray<ArrangeRow>>::serialize(&self.0, serializer)
226    }
227}
228
229/// Generic collection for [`BankFile`]s.
230///
231/// # [`Serialize`]/[`Deserialize`]
232///
233/// Neither [`Serialize`] nor [`Deserialize`] are implemented for this type.
234///
235/// # `Box` (no `Copy`)
236///
237/// The inner array of this type is wrapped in a `Box`.
238/// -- we cannot fit all [`BankFile`]s on the stack.
239/// Unfortunately this means the type does not implement `Copy`.
240///
241/// # Example: Extending in your own code with custom inner types
242/// ```
243/// use ot_tools_io::types::Banks;
244/// use std::path::PathBuf;
245///
246/// use ot_tools_io::Defaults;
247/// use ot_tools_io_derive::{ArrayDefaults, BoxedArrayDefaults};
248/// use std::array::from_fn;
249///
250/// #[derive(Debug, PartialEq, Default, ArrayDefaults, BoxedArrayDefaults)]
251/// pub struct YamlPath(PathBuf);
252///
253/// # fn main() -> Result<(), ()> {
254/// let mut my_bank_yamls = Banks::<YamlPath>::default();
255/// let first = my_bank_yamls.first_mut().ok_or(())?;
256/// *first = YamlPath(PathBuf::from("some_path"));
257///
258/// // first one changed
259/// assert_eq!(my_bank_yamls[0], YamlPath(PathBuf::from("some_path")));
260/// // rest are default
261/// assert_eq!(my_bank_yamls[1], YamlPath::default());
262///
263/// // correct length
264/// assert_eq!(my_bank_yamls.len(), 16);
265///
266/// # Ok(()) }
267/// ```
268#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
269pub struct Banks<T>(Box<[T; 16]>);
270
271impl<T> Banks<T> {
272    pub fn new(value: [T; 16]) -> Self {
273        Self(Box::new(value))
274    }
275
276    pub fn new_boxed(value: Box<[T; 16]>) -> Self {
277        Self(value)
278    }
279}
280
281generic_newtype_id_lookups!(Banks, BankId);
282generic_newtype_unbox!(Banks, 16);
283generic_newtype_asref!(Banks);
284generic_newtype_asmut!(Banks);
285generic_newtype_deref!(Banks, 16);
286generic_newtype_deref_mut!(Banks);
287
288impl<T: Defaults<Box<[T; 16]>> + Default> Default for Banks<T> {
289    fn default() -> Self {
290        Self(T::defaults())
291    }
292}
293
294impl HasHeaderField for Banks<BankFile> {
295    fn check_header(&self) -> Result<bool, OtToolsIoError> {
296        Ok(self.iter().all(|x| x.check_header().is_ok_and(|x| x)))
297    }
298}
299
300/// Concrete implementation for [`BankFile`]s.
301/// Implements methods for reading and writing all [`BankFile`]s within an octatrack project directory.
302impl Banks<BankFile> {
303    /// Reads a collection of [`BankFile`]s from a directory.
304    pub fn from_data_files<P>(dirpath: P, state: &SavedState) -> Result<Self, OtToolsIoError>
305    where
306        PathBuf: From<P>,
307    {
308        let mut new = Self::default();
309        let path = PathBuf::from(dirpath);
310        for id in BankId::iter() {
311            let bank = BankFile::from_data_file(&path.join(id.file_stem(state)))?;
312            new[id.as_index()] = bank;
313        }
314        Ok(new)
315    }
316
317    /// Writes a collection of [`BankFile`]s to a directory
318    ///
319    /// Will skip writing a particular [`BankFile`] if the `skip_unchanged`
320    /// argument is `true` and the current [`BankFile`] matches the data in the
321    /// existing file on the file system.
322    pub fn to_data_files<P>(
323        &self,
324        dirpath: P,
325        state: &SavedState,
326        skip_unchanged: bool,
327    ) -> Result<(), OtToolsIoError>
328    where
329        PathBuf: From<P>,
330    {
331        let path = PathBuf::from(dirpath);
332        for id in BankId::iter() {
333            let bank = self.id_ref(&id);
334            if skip_unchanged {
335                if let Ok(exists) = BankFile::from_data_file(&path.join(id.file_stem(state))) {
336                    if &exists != bank {
337                        bank.to_data_file(&path.join(id.file_stem(state)))?;
338                    }
339                } else {
340                    // probably doesn't exist
341                    bank.to_data_file(&path.join(id.file_stem(state)))?;
342                }
343            } else {
344                bank.to_data_file(&path.join(id.file_stem(state)))?;
345            }
346        }
347        Ok(())
348    }
349}
350
351/// Generic collection for [`Part`]s
352///
353/// # [`Serialize`]/[`Deserialize`]
354///
355/// [`Serialize`] and [`Deserialize`] are implemented for this type.
356///
357/// # `Box` (no `Copy`)
358///
359/// The inner array of this type is wrapped in a `Box`.
360/// Fields within a [`Part`] already use a `Box` so we are forced to wrap the data in a `Box` here.
361///
362/// # Example: Extending in your own code with custom inner types
363/// ```
364/// use ot_tools_io::types::Parts;
365/// use std::path::PathBuf;
366///
367/// use ot_tools_io::Defaults;
368/// use ot_tools_io_derive::{ArrayDefaults, BoxedArrayDefaults};
369/// use std::array::from_fn;
370///
371/// #[derive(Debug, PartialEq, Default, ArrayDefaults, BoxedArrayDefaults)]
372/// pub struct LongPartName(String);
373///
374/// # fn main() -> Result<(), ()> {
375/// let mut my_part_names = Parts::<LongPartName>::default();
376/// let first = my_part_names.first_mut().ok_or(())?;
377/// *first = LongPartName(String::from("My awesome part name that is too long on the octatrack"));
378///
379/// // first one changed
380/// assert_eq!(
381///     my_part_names[0],
382///     LongPartName(
383///         String::from("My awesome part name that is too long on the octatrack")
384///     )
385/// );
386/// // rest are default
387/// assert_eq!(my_part_names[1], LongPartName::default());
388///
389///
390/// // correct length
391/// assert_eq!(my_part_names.len(), 4);
392///
393/// # Ok(()) }
394/// ```
395#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
396pub struct Parts<T>(Box<[T; 4]>);
397
398impl<T> Parts<T> {
399    pub fn new(value: [T; 4]) -> Self {
400        Self(Box::new(value))
401    }
402
403    pub fn new_boxed(value: Box<[T; 4]>) -> Self {
404        Self(value)
405    }
406}
407
408generic_newtype_id_lookups!(Parts, PartId);
409generic_newtype_unbox!(Parts, 4);
410generic_newtype_asref!(Parts);
411generic_newtype_asmut!(Parts);
412generic_newtype_deref!(Parts, 4);
413generic_newtype_deref_mut!(Parts);
414
415impl<T: Defaults<Box<[T; 4]>> + Default> Default for Parts<T> {
416    fn default() -> Self {
417        Self(T::defaults())
418    }
419}
420
421impl<'de> Deserialize<'de> for Parts<Part> {
422    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
423    where
424        D: Deserializer<'de>,
425    {
426        let boxed = Box::new(<[Part; 4] as BigArray<Part>>::deserialize(deserializer)?);
427        Ok(Self(boxed))
428    }
429}
430
431impl Serialize for Parts<Part> {
432    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
433    where
434        S: Serializer,
435    {
436        <[Part; 4] as BigArray<Part>>::serialize(&self.0, serializer)
437    }
438}
439
440impl HasHeaderField for Parts<Part> {
441    fn check_header(&self) -> Result<bool, OtToolsIoError> {
442        Ok(self.iter().all(|x| x.check_header().is_ok_and(|x| x)))
443    }
444}
445
446/// Generic collection for [`Pattern`]s
447///
448/// # [`Serialize`]/[`Deserialize`]
449///
450/// [`Serialize`] and [`Deserialize`] are implemented for this type.
451///
452/// # `Box` (no `Copy`)
453///
454/// Unfortunately serde requires that we wrap the inner array of `Patterns` in a `Box`.
455/// So `Patterns`, and any type that contains a `Patterns` type, cannot implement the `Copy`
456/// trait.
457///
458/// # Example: Extending in your own code with custom inner types
459/// ```
460/// use ot_tools_io::types::Patterns;
461/// use std::path::PathBuf;
462///
463/// use ot_tools_io::Defaults;
464/// use ot_tools_io_derive::{ArrayDefaults, BoxedArrayDefaults};
465/// use std::array::from_fn;
466///
467/// #[derive(Debug, PartialEq, Default, ArrayDefaults, BoxedArrayDefaults)]
468/// pub struct SomeString(String);
469///
470/// # fn main() -> Result<(), ()> {
471/// let mut my_pattern_descriptions = Patterns::<SomeString>::default();
472/// let first = my_pattern_descriptions.first_mut().ok_or(())?;
473/// *first = SomeString(String::from("My awesome pattern"));
474///
475/// // first one changed
476/// assert_eq!(
477///     my_pattern_descriptions[0],
478///     SomeString(
479///         String::from("My awesome pattern")
480///     )
481/// );
482/// // rest are default
483/// assert_eq!(my_pattern_descriptions[1], SomeString::default());
484///
485/// // correct length
486/// assert_eq!(my_pattern_descriptions.len(), 16);
487///
488/// # Ok(()) }
489/// ```
490#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
491pub struct Patterns<T>(Box<[T; 16]>);
492
493impl<T> Patterns<T> {
494    pub fn new(value: [T; 16]) -> Self {
495        Self(Box::new(value))
496    }
497
498    pub fn new_boxed(value: Box<[T; 16]>) -> Self {
499        Self(value)
500    }
501}
502
503generic_newtype_id_lookups!(Patterns, PatternId);
504generic_newtype_unbox!(Patterns, 16);
505generic_newtype_asref!(Patterns);
506generic_newtype_asmut!(Patterns);
507generic_newtype_deref!(Patterns, 16);
508generic_newtype_deref_mut!(Patterns);
509
510impl<T: Defaults<Box<[T; 16]>> + Default> Default for Patterns<T> {
511    fn default() -> Self {
512        Self(T::defaults())
513    }
514}
515
516impl<'de> Deserialize<'de> for Patterns<Pattern> {
517    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
518    where
519        D: Deserializer<'de>,
520    {
521        let boxed = Box::new(<[Pattern; 16] as BigArray<Pattern>>::deserialize(
522            deserializer,
523        )?);
524        Ok(Self(boxed))
525    }
526}
527
528impl Serialize for Patterns<Pattern> {
529    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
530    where
531        S: Serializer,
532    {
533        <[Pattern; 16] as BigArray<Pattern>>::serialize(&self.0, serializer)
534    }
535}
536
537impl HasHeaderField for Patterns<Pattern> {
538    fn check_header(&self) -> Result<bool, OtToolsIoError> {
539        Ok(self.iter().all(|x| x.check_header().is_ok_and(|x| x)))
540    }
541}
542
543/// Generic collection for data related to tracks, e.g. for Recording Buffers, Audio Track Trigs,
544/// MIDI Track Trigs and the like.
545/// Used by a lot of fields on the [`Part`] and [`Pattern`] types.
546///
547/// # `Copy` (no `Box`)
548///
549/// This generic collection helper does not use `Box` and can be `Copy`.
550///
551/// # Example: Using in your own code with custom inner types
552///
553/// ```
554/// use ot_tools_io::types::Tracks;
555/// use ot_tools_io::Defaults;
556/// use ot_tools_io_derive::ArrayDefaults;
557/// use std::array::from_fn;
558///
559/// #[derive(Debug, PartialEq, Default, ArrayDefaults)]
560/// pub struct SomeNumber(u8);
561///
562/// fn main() -> Result<(), ()> {
563///     let mut my_tracks = Tracks::<SomeNumber>::default();
564///     let first = my_tracks.first_mut().ok_or(())?;
565///     *first = SomeNumber(100);
566///
567///     // first one changed
568///     assert_eq!(my_tracks[0], SomeNumber(100));
569///     // rest are default
570///     assert_eq!(my_tracks[1], SomeNumber(0));
571///
572///     // correct length
573///     assert_eq!(my_tracks.len(), 8);
574///
575///     Ok(())
576/// }
577/// ```
578#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
579pub struct Tracks<T>([T; 8]);
580
581generic_newtype_id_lookups!(Tracks, TrackId);
582generic_newtype_asref!(Tracks);
583generic_newtype_asmut!(Tracks);
584generic_newtype_deref!(Tracks, 8);
585generic_newtype_deref_mut!(Tracks);
586generic_newtype_index!(Tracks);
587generic_newtype_index_mut!(Tracks);
588
589impl<T: Defaults<[T; 8]> + Default> Default for Tracks<T> {
590    fn default() -> Self {
591        Self(T::defaults())
592    }
593}
594
595impl<const N: usize, T: Default> Defaults<[Self; N]> for Tracks<T> {
596    fn defaults() -> [Self; N]
597    where
598        Self: Default,
599    {
600        from_fn(|_| Self::default())
601    }
602}
603
604impl<T> Tracks<T> {
605    pub fn new(value: [T; 8]) -> Self {
606        Self(value)
607    }
608}
609
610impl<'de, T: Deserialize<'de>> Deserialize<'de> for Tracks<T> {
611    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
612    where
613        D: Deserializer<'de>,
614    {
615        Ok(Self(<[T; 8] as BigArray<T>>::deserialize(deserializer)?))
616    }
617}
618
619impl<T: Serialize> Serialize for Tracks<T> {
620    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
621    where
622        S: Serializer,
623    {
624        <[T; 8] as BigArray<T>>::serialize(&self.0, serializer)
625    }
626}
627
628/// Generic collection for scene data.
629/// Used in the `scenes` and `scene_xlvs` fields on [`Part`].
630///
631/// # `Copy` (no `Box`)
632///
633/// This generic collection helper does not use `Box` and can be `Copy`.
634///
635/// # Example: Using in your own code with custom inner types
636///
637/// ```
638/// use ot_tools_io::types::Scenes;
639/// use ot_tools_io::Defaults;
640/// use ot_tools_io_derive::ArrayDefaults;
641/// use std::array::from_fn;
642///
643/// #[derive(Debug, PartialEq, Default, ArrayDefaults)]
644/// pub struct SomeNumber(u8);
645///
646/// fn main() -> Result<(), ()> {
647///     let mut my_scenes = Scenes::<SomeNumber>::default();
648///     let first = my_scenes.first_mut().ok_or(())?;
649///     *first = SomeNumber(100);
650///
651///     // first one changed
652///     assert_eq!(my_scenes[0], SomeNumber(100));
653///     // rest are default
654///     assert_eq!(my_scenes[1], SomeNumber(0));
655///
656///     // correct length
657///     assert_eq!(my_scenes.len(), 16);
658///
659///     Ok(())
660/// }
661/// ```
662#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
663pub struct Scenes<T>([T; 16]);
664
665generic_newtype_id_lookups!(Scenes, SceneId);
666generic_newtype_asref!(Scenes);
667generic_newtype_asmut!(Scenes);
668generic_newtype_deref!(Scenes, 16);
669generic_newtype_deref_mut!(Scenes);
670generic_newtype_index!(Scenes);
671generic_newtype_index_mut!(Scenes);
672
673impl<T: Defaults<[T; 16]> + Default> Default for Scenes<T> {
674    fn default() -> Self {
675        Self(T::defaults())
676    }
677}
678
679impl<T> Scenes<T> {
680    pub fn new(value: [T; 16]) -> Self {
681        Self(value)
682    }
683}
684
685impl<'de, T: Deserialize<'de>> Deserialize<'de> for Scenes<T> {
686    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
687    where
688        D: Deserializer<'de>,
689    {
690        Ok(Self(<[T; 16] as BigArray<T>>::deserialize(deserializer)?))
691    }
692}
693
694impl<T: Serialize> Serialize for Scenes<T> {
695    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
696    where
697        S: Serializer,
698    {
699        <[T; 16] as BigArray<T>>::serialize(&self.0, serializer)
700    }
701}
702
703/// Generic collection for slice data.
704///
705/// # Example: Using in your own code with custom inner types
706///
707/// ```
708/// use ot_tools_io::types::Slices;
709/// use ot_tools_io::Defaults;
710/// use ot_tools_io_derive::ArrayDefaults;
711/// use std::array::from_fn;
712///
713/// #[derive(Debug, PartialEq, Default, ArrayDefaults)]
714/// pub struct SomeNumber(u8);
715///
716/// fn main() -> Result<(), ()> {
717///     let mut my_slices = Slices::<SomeNumber>::default();
718///     let first = my_slices.first_mut().ok_or(())?;
719///     *first = SomeNumber(100);
720///
721///     // first one changed
722///     assert_eq!(my_slices[0], SomeNumber(100));
723///     // rest are default
724///     assert_eq!(my_slices[1], SomeNumber(0));
725///
726///     // correct length
727///     assert_eq!(my_slices.len(), 64);
728///
729///     Ok(())
730/// }
731/// ```
732#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
733pub struct Slices<T>([T; 64]);
734
735generic_newtype_id_lookups!(Slices, SliceId);
736generic_newtype_asref!(Slices);
737generic_newtype_asmut!(Slices);
738generic_newtype_deref!(Slices, 64);
739generic_newtype_deref_mut!(Slices);
740generic_newtype_index!(Slices);
741generic_newtype_index_mut!(Slices);
742generic_newtype_into_iter!(Slices, 64);
743
744impl<T: Defaults<[T; 64]> + Default> Default for Slices<T> {
745    fn default() -> Self {
746        Self(T::defaults())
747    }
748}
749
750impl<T> Slices<T> {
751    pub fn new(value: [T; 64]) -> Self {
752        Self(value)
753    }
754}
755
756impl<'de, T: Deserialize<'de>> Deserialize<'de> for Slices<T> {
757    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
758    where
759        D: Deserializer<'de>,
760    {
761        Ok(Self(<[T; 64] as BigArray<T>>::deserialize(deserializer)?))
762    }
763}
764
765impl<T: Serialize> Serialize for Slices<T> {
766    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
767    where
768        S: Serializer,
769    {
770        <[T; 64] as BigArray<T>>::serialize(&self.0, serializer)
771    }
772}
773
774/// Generic collection for data related to trigs.
775/// Used in [`Pattern`]s with parameter lock trig assignments and trig offset/repeat/conditions data.
776///
777/// # `Box` (no-`Copy`)
778///
779/// Unfortunately serde requires that we wrap the inner array type of [`Trigs`] in a `Box`.
780/// As a result, [`Trigs`] and any type that contains a [`Trigs`] type cannot implement the `Copy` trait.
781///
782/// # Example: Using in your own code with custom inner types
783///
784/// ```
785/// use ot_tools_io::types::Trigs;
786/// use ot_tools_io::Defaults;
787/// use ot_tools_io_derive::{ArrayDefaults, BoxedArrayDefaults};
788/// use std::array::from_fn;
789///
790/// #[derive(Debug, PartialEq, Default, ArrayDefaults, BoxedArrayDefaults)]
791/// pub struct SomeNumber(u8);
792///
793/// fn main() -> Result<(), ()> {
794///     let mut my_trigs = Trigs::<SomeNumber>::default();
795///     let first = my_trigs.first_mut().ok_or(())?;
796///     *first = SomeNumber(100);
797///
798///     // first one changed
799///     assert_eq!(my_trigs[0], SomeNumber(100));
800///     // rest are default
801///     assert_eq!(my_trigs[1], SomeNumber(0));
802///
803///     // correct length
804///     assert_eq!(my_trigs.len(), 64);
805///
806///     Ok(())
807/// }
808/// ```
809#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
810pub struct Trigs<T>(Box<[T; 64]>);
811
812generic_newtype_id_lookups!(Trigs, TrigId);
813generic_newtype_unbox!(Trigs, 64);
814generic_newtype_asref!(Trigs);
815generic_newtype_asmut!(Trigs);
816generic_newtype_deref!(Trigs, 64);
817generic_newtype_deref_mut!(Trigs);
818
819impl<T: Defaults<Box<[T; 64]>> + Default> Default for Trigs<T> {
820    fn default() -> Self {
821        Self(T::defaults())
822    }
823}
824
825impl<T> Trigs<T> {
826    pub fn new(value: [T; 64]) -> Self {
827        Self(Box::new(value))
828    }
829
830    pub fn new_boxed(value: Box<[T; 64]>) -> Self {
831        Self(value)
832    }
833}
834
835impl<'de, T: Deserialize<'de>> Deserialize<'de> for Trigs<T> {
836    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
837    where
838        D: Deserializer<'de>,
839    {
840        Ok(Self(Box::new(<[T; 64] as BigArray<T>>::deserialize(
841            deserializer,
842        )?)))
843    }
844}
845
846impl<T: Serialize> Serialize for Trigs<T> {
847    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
848    where
849        S: Serializer,
850    {
851        <[T; 64] as BigArray<T>>::serialize(&self.0, serializer)
852    }
853}
854
855impl<T, I> Index<I> for Trigs<T>
856where
857    [T]: Index<I>,
858{
859    type Output = <[T] as Index<I>>::Output;
860    #[inline]
861    fn index(&self, index: I) -> &Self::Output {
862        Index::index(&*self.0 as &[T], index)
863    }
864}
865
866impl<T, I> IndexMut<I> for Trigs<T>
867where
868    [T]: IndexMut<I>,
869{
870    #[inline]
871    fn index_mut(&mut self, index: I) -> &mut Self::Output {
872        IndexMut::index_mut(&mut *self.0 as &mut [T], index)
873    }
874}
875
876#[allow(rustdoc::invalid_rust_codeblocks)]
877/// Generic collection for **playback slot** items, e.g. Static and Flex slot data.
878///
879/// # Extending: Custom New Types
880///
881/// As this type is generic you can use custom new type structs in downstream applications /
882/// libraries, e.g. to use your own custom 'ApplicationSlot' type which has extra methods/fields.
883///
884/// ```
885/// use ot_tools_io::types::PlaybackSlots;
886/// use ot_tools_io::Defaults;
887/// use std::path::PathBuf;
888///
889/// #[derive(Debug, PartialEq)]
890/// pub struct PathOnlySlotType(PathBuf);
891///
892/// impl Default for PathOnlySlotType {
893///     fn default() -> Self {
894///         Self(PathBuf::from("default/path/to/some/file.ext"))
895///     }
896/// }
897///
898/// impl Defaults<Box<[Self; 128]>> for PathOnlySlotType {
899///     fn defaults() -> Box<[Self; 128]> where Self: Default {
900///         Box::new(std::array::from_fn(|_| Self::default()))
901///     }
902/// }
903///
904/// # fn main() -> Result<(), ()> {
905/// let mut my_slots = PlaybackSlots::<PathOnlySlotType>::default();
906/// let first = my_slots.first_mut().ok_or(())?;
907/// *first = PathOnlySlotType(PathBuf::from("some_path"));
908///
909/// // first one changed
910/// assert_eq!(my_slots[0], PathOnlySlotType(PathBuf::from("some_path")));
911/// // rest are default
912/// assert_eq!(my_slots[1], PathOnlySlotType::default());
913/// # Ok(()) }
914/// ```
915///
916/// # Note: Name of the type
917///
918/// This type should only hold elements related to static or flex slots.
919/// i.e. no recording buffer slots, ever (slot_type = flex, slot_id > 128 etc.).
920///
921/// Admittedly the name [`PlaybackSlots`] is somewhat of a misnomer
922/// - Flex slots can be recorded to
923/// - Sample data stored in Recording Buffers can be played back on audio tracks
924///
925/// However, those are *special cases* of Octatrack usage:
926/// - The designed intent of Recording Buffers is to **record to the slot**.
927/// - The designed intent of Flex Slots is to **edit the audio data and playback**.
928/// - The designed intent of Static Slots is to **only playback**.
929///
930/// # Explainer: Why does this type exist?
931///
932/// > **note**: the examples in this section may not compile.
933/// > they are here to demonstrate problems when using previous versions of slot data types.
934/// > they are not here as examples to actually implement yourself!
935///
936/// Recording Buffers are Flex slots with a Slot ID > 128 under the hood (or more literally the
937/// metal case) of the Octatrack.
938/// I originally decided to represent this in this "io" library.
939/// Recording buffers are actually Flex slots.
940/// That was it.
941/// Done.
942///
943/// The point of the library was to load data from the binary files at the lowest granularity
944/// possible
945/// -- representing how data is stored in each binary file.
946/// The thinking behind this decision was that it would allow more flexibility when using the
947/// library
948/// -- access to data at the lowest granularity means the ability to do "more stuff", right?
949///
950/// Having worked with the data in that format for a while
951/// -- it is a massive pain in the arse dealing with the "recording buffers are actually flex slots"
952/// special case.
953///
954/// The library now treats Recording Buffers as a different special type of slot during
955/// Serialization/Deserialization
956/// -- separate to Flex and Static slots which are considered **Playback Slots**.
957///
958/// ## first example of the pain
959/// - simple matches need an extra case where you need to be aware of the `slot_id > 128` rule.
960/// - users needing to be aware of some hidden logic/rule == bad.
961/// - my first draft of this example got the rule wrong! i used `slot_id > 129`! and i'm the author
962///   of this library!
963/// ```compile_fail
964/// match slot.slot_type {
965///     SlotType::Static => {}, // do something to static slots
966///     SlotType::Flex => {}, // do something to flex slots
967///     SlotType::Flex if slot.slot_id > 128 => _, // this one
968/// }
969/// ```
970/// ## second example of the pain
971/// - our `flex_slots` variable contains `Option<SlotAttributes>` instances with the `slot_type` field
972///   equal to `SlotType::Flex` -- so, only flex slot data, right?
973/// - not necessarily ... we often still need to check whether an element returned from the
974///   collection is actually a flex playback slot and not a recording buffer slot.
975/// - so we have to doa manual check on the `slot_id` field
976/// - again, users needing to be aware of some hidden logic/rule == bad.
977/// ```compile_fail
978/// // (note: rust 2024 required for let chains)
979/// let flex_slot_maybe = flex_slots
980///     .get(some_index)
981///     .map(|x| {
982///         if let Some(slot_attrs) = x
983///             && slot_attrs.slot_type == SlotType::Flex
984///             && slot_attrs.slot_id <= 128 // our "ignore recording buffers" rule
985///             { Some(possible_flex_slot) }
986///         } else {
987///             None
988///         }
989///     });
990///
991/// let actual_flex_slot = flex_slot_maybe.ok_or(SomeErr)?;
992/// ```
993///
994/// ## third example of the pain
995/// - can't do this with raw data -- the underlying arrays are different sizes so they are different
996///   types!
997/// - [`as_array`](https://doc.rust-lang.org/std/primitive.slice.html#method.as_array) will return
998///   `None` for our flex slots!
999///
1000/// > `pub const fn as_array<const N: usize>(&self) -> Option<&[T; N]>`
1001///
1002/// > If `N` is not exactly equal to the length of `self`, then this method returns `None`.
1003/// ```compile_fail
1004/// fn get_slot_attrs_by_type(
1005///     project_file: &ProjectFile,
1006///     slot_type: SlotType,
1007/// ) -> Option<&[Option<SlotAttributes>; 128]> {
1008///     match slot_type {
1009///         SlotType::Static => project_file.slots.static_slots.as_array(),
1010///         SlotType::Flex => project_file.slots.flex_slots.as_array(),
1011///     }
1012/// }
1013///
1014/// // always `None` due to the size issue
1015/// assert_eq!(get_slot_attrs_for_type(&file, SlotType::Flex), None)
1016/// ```
1017/// ## possible solutions to the pain?
1018///
1019/// one possible solution is making `get_slot_attrs_by_type` accept a generic size argument ...
1020/// ```compile_fail
1021/// fn get_slot_attrs_by_type<const N: usize>(
1022///     project_file: &ProjectFile,
1023///     slot_type: SlotType,
1024/// ) -> &[Option<SlotAttributes>; N];
1025/// ```
1026///
1027/// ... but this creates additional problems downstream
1028/// -- you need to create different types for different slot types with different lengths.
1029/// you then end up having to recreate this library's "Slots" array/collection data type(s) for your
1030/// own library and maintain them!
1031/// not ideal for a library!
1032/// our types are supposed to be re-usable!
1033///
1034/// another solution is just to give up!
1035/// stick everything in a `Vec` with cloned data!
1036/// forget references to arrays in the original file!
1037/// ... right?
1038///
1039/// ```compile_fail
1040/// fn get_slot_attrs_by_type(
1041///     project_file: &ProjectFile,
1042///     slot_type: SlotType,
1043/// ) -> Vec<Option<SlotAttributes>> {
1044///     let mut slots = vec![];
1045///
1046///     match slot_type {
1047///         SlotType::Static => for slot in project_file.slots.static_slots { slots.push(slot.clone()) },
1048///         SlotType::Flex => for slot in project_file.slots.flex_slots { slots.push(slot.clone()) },
1049///     };
1050///
1051///     slots
1052/// }
1053/// ```
1054///
1055/// but this means we can add more than 128 (or 136) slots to the `Vec` ...
1056/// either we check each time we push to this `Vec` that we haven't exceeded the size limit
1057/// (we're manually tracking size again!)
1058/// or we make this function generic -- which means tracking size in types downstream again!
1059/// oh, and because we have to `clone()` to escape the shared reference
1060/// -- we can have multiple owned versions of slots data which we can modify independently of each
1061/// other!
1062///
1063/// great!
1064/// now we can absolutely and completely bork someone's project files for sure!
1065///
1066/// ## the real solution to the pain
1067///
1068/// split recording buffers out separately with a different type -- [`RecordingBufferSlots<T>`] --
1069/// when we parse [`ProjectFile`][pf]s ...
1070///
1071/// so that's what happens now.
1072/// See the [`Slots<T>`] for more information.
1073///
1074/// [pf]: crate::types::ProjectFile
1075#[derive(Clone, PartialEq, Debug, Hash, PartialOrd, Ord, Eq)]
1076pub struct PlaybackSlots<T>(Box<[T; 128]>);
1077
1078generic_newtype_id_lookups!(PlaybackSlots, PlaybackSlotId);
1079generic_newtype_asref!(PlaybackSlots);
1080generic_newtype_asmut!(PlaybackSlots);
1081generic_newtype_deref!(PlaybackSlots, 128);
1082generic_newtype_deref_mut!(PlaybackSlots);
1083
1084impl<T: Defaults<Box<[T; 128]>> + Default> Default for PlaybackSlots<T> {
1085    fn default() -> Self {
1086        Self(T::defaults())
1087    }
1088}
1089
1090impl<T> PlaybackSlots<T> {
1091    pub fn new(value: [T; 128]) -> Self {
1092        Self(Box::new(value))
1093    }
1094
1095    pub fn new_boxed(value: Box<[T; 128]>) -> Self {
1096        Self(value)
1097    }
1098}
1099
1100impl PlaybackSlots<Option<ActiveSlot<SlotAttributes, SlotMarkers>>> {
1101    /// Insert or replace the slot in the collection based on the `slot_id` field  in the provided
1102    /// [`ActiveSlot<SlotAttributes, SlotMarkers>`] instance.
1103    /// Will return
1104    /// - the element of the [`PlaybackSlots`] which is replaced
1105    /// - `None` if the slot is inserted into an empty slot
1106    /// - `None` if the `slot_id` field value is invalid
1107    pub fn insert_or_replace_by_slot_id_field(
1108        &mut self,
1109        value: ActiveSlot<SlotAttributes, SlotMarkers>,
1110    ) -> Option<ActiveSlot<SlotAttributes, SlotMarkers>> {
1111        if let Some(slot_id) = PlaybackSlotId::from_id(value.attrs_ref().slot_id) {
1112            let to_replace = self.id_mut(&slot_id);
1113            let old = to_replace.clone();
1114            *to_replace = Some(value);
1115            old
1116        } else {
1117            None
1118        }
1119    }
1120
1121    pub fn take_by_id(
1122        &mut self,
1123        id: &PlaybackSlotId,
1124    ) -> Option<ActiveSlot<SlotAttributes, SlotMarkers>> {
1125        if let Some(slot) = self.get_mut(id.as_index()) {
1126            slot.take()
1127        } else {
1128            None
1129        }
1130    }
1131}
1132
1133impl Default for PlaybackSlots<Option<ActiveSlot<SlotAttributes, SlotMarkers>>> {
1134    fn default() -> Self {
1135        Self(Box::new(from_fn(|_| None)))
1136    }
1137}
1138
1139impl<'de, T: Deserialize<'de>> Deserialize<'de> for PlaybackSlots<T> {
1140    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1141    where
1142        D: Deserializer<'de>,
1143    {
1144        Ok(Self(Box::new(<[T; 128] as BigArray<T>>::deserialize(
1145            deserializer,
1146        )?)))
1147    }
1148}
1149
1150impl<T: Serialize> Serialize for PlaybackSlots<T> {
1151    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1152    where
1153        S: Serializer,
1154    {
1155        <[T; 128] as BigArray<T>>::serialize(&self.0, serializer)
1156    }
1157}
1158
1159/// Generic collection for data related to Recording Buffers.
1160/// Used in the [`Slots`] type.
1161///
1162/// Recording buffers are always 8x slots -- we could re-use the [`Tracks`] generic collection
1163/// type instead of creating a special case, but it feels strange to me to use [`TrackId`]
1164/// to access slot data.
1165///
1166/// It is simple enough to convert from a [`TrackId`] to a [`RecBufId`]
1167/// ```
1168/// use ot_tools_io::identifiers::{TrackId, RecBufId};
1169/// # use ot_tools_io::OtToolsIoError;
1170/// # fn main() -> Result<(), OtToolsIoError> {
1171/// RecBufId::try_from(TrackId::One as u8)?;
1172/// # Ok(()) }
1173/// ```
1174///
1175/// # `Copy` (no `Box`)
1176///
1177/// This generic collection helper does not use `Box` and can be `Copy`.
1178///
1179/// # Example: Using in your own code with custom inner types
1180///
1181/// ```
1182/// use ot_tools_io::types::RecordingBufferSlots;
1183/// use ot_tools_io::Defaults;
1184/// use ot_tools_io_derive::ArrayDefaults;
1185/// use std::array::from_fn;
1186///
1187/// #[derive(Debug, PartialEq, Default, ArrayDefaults)]
1188/// pub struct SomeNumber(u8);
1189///
1190/// fn main() -> Result<(), ()> {
1191///     let mut my_recbufs = RecordingBufferSlots::<SomeNumber>::default();
1192///     let first = my_recbufs.first_mut().ok_or(())?;
1193///     *first = SomeNumber(100);
1194///
1195///     // first one changed
1196///     assert_eq!(my_recbufs[0], SomeNumber(100));
1197///     // rest are default
1198///     assert_eq!(my_recbufs[1], SomeNumber(0));
1199///
1200///     // correct length
1201///     assert_eq!(my_recbufs.len(), 8);
1202///
1203///     Ok(())
1204/// }
1205/// ```
1206#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1207pub struct RecordingBufferSlots<T>([T; 8]);
1208
1209generic_newtype_id_lookups!(RecordingBufferSlots, RecBufId);
1210generic_newtype_asref!(RecordingBufferSlots);
1211generic_newtype_asmut!(RecordingBufferSlots);
1212generic_newtype_deref!(RecordingBufferSlots, 8);
1213generic_newtype_deref_mut!(RecordingBufferSlots);
1214generic_newtype_index!(RecordingBufferSlots);
1215generic_newtype_index_mut!(RecordingBufferSlots);
1216
1217impl<T: Defaults<[T; 8]> + Default> Default for RecordingBufferSlots<T> {
1218    fn default() -> Self {
1219        Self(T::defaults())
1220    }
1221}
1222
1223impl<const N: usize, T: Default> Defaults<[Self; N]> for RecordingBufferSlots<T> {
1224    fn defaults() -> [Self; N]
1225    where
1226        Self: Default,
1227    {
1228        from_fn(|_| Self::default())
1229    }
1230}
1231
1232impl<T> RecordingBufferSlots<T> {
1233    pub fn new(value: [T; 8]) -> Self {
1234        Self(value)
1235    }
1236}
1237
1238impl<'de, T: Deserialize<'de>> Deserialize<'de> for RecordingBufferSlots<T> {
1239    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1240    where
1241        D: Deserializer<'de>,
1242    {
1243        Ok(Self(<[T; 8] as BigArray<T>>::deserialize(deserializer)?))
1244    }
1245}
1246
1247impl<T: Serialize> Serialize for RecordingBufferSlots<T> {
1248    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1249    where
1250        S: Serializer,
1251    {
1252        <[T; 8] as BigArray<T>>::serialize(&self.0, serializer)
1253    }
1254}
1255
1256/// Generic collection for all slots data -- flex, static and recording buffers.
1257///
1258/// See the explanation in [`PlaybackSlots`] for why [`RecordingBufferSlots`] are handled
1259/// differently to [`PlaybackSlots`].
1260///
1261/// # [`Serialize`] / [`Deserialize`]
1262///
1263/// The `project.*` text file(s) are parsed differently to binary `markers.*` file(s).
1264/// As a result, this type does not have a generic [`Serialize`] / [`Deserialize`] implementations.
1265/// Rather, it has TODO
1266#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1267pub struct Slots<T> {
1268    // order is on purpose -- this is the order in the MarkersFile, which will be more authoritative
1269    // regarding slot data ordering than ProjectFile.
1270    pub flex_slots: PlaybackSlots<T>,
1271    pub recording_buffers: RecordingBufferSlots<T>,
1272    pub static_slots: PlaybackSlots<T>,
1273}
1274
1275impl<T> Slots<T> {
1276    pub fn new(
1277        flex_slots: PlaybackSlots<T>,
1278        recording_buffers: RecordingBufferSlots<T>,
1279        static_slots: PlaybackSlots<T>,
1280    ) -> Self {
1281        Self {
1282            flex_slots,
1283            recording_buffers,
1284            static_slots,
1285        }
1286    }
1287}
1288
1289/// Convenience type for combining [`SlotAttributes`] and [`SlotMarkers`],
1290/// or types which have the respective [`AsRef`] trait implemented.
1291/// This essentially gives you a type representative of the Octatrack's slot editing pages
1292/// -- all markers data and all attributes data.
1293#[derive(Default, Clone, Copy, PartialEq, Debug, Hash, PartialOrd, Ord, Eq)]
1294pub struct ActiveSlot<A, M>
1295where
1296    A: AsRef<SlotAttributes>,
1297    M: AsRef<SlotMarkers>,
1298{
1299    pub attrs: A,
1300    pub marks: M,
1301}
1302
1303impl AsRef<ActiveSlot<SlotAttributes, SlotMarkers>> for ActiveSlot<SlotAttributes, SlotMarkers> {
1304    fn as_ref(&self) -> &ActiveSlot<SlotAttributes, SlotMarkers> {
1305        self
1306    }
1307}
1308
1309impl<A, M> ActiveSlot<A, M>
1310where
1311    A: AsRef<SlotAttributes>,
1312    M: AsRef<SlotMarkers>,
1313{
1314    /// Creates a new [`ActiveSlot`] instance from the two component slots data types.
1315    ///
1316    /// # `AsRef`
1317    ///
1318    /// This method accepts any input type where, for each respective argument,
1319    /// `AsRef<SlotSlotAttributes>` or `AsRef<SlotSlotMarkers>` has been implemented appropriately.
1320    /// Therefore, any type that can be `AsRef`'d into one of these types is accepted as the
1321    /// relevant argument.
1322    ///
1323    /// # [`SlotAttributes`] example
1324    ///
1325    /// ```
1326    /// use ot_tools_io::types::ActiveSlot;
1327    /// use ot_tools_io::types::SlotAttributes;
1328    /// use ot_tools_io::types::SlotMarkers;
1329    /// # use ot_tools_io::types::SlotType;
1330    ///
1331    /// // example data
1332    /// let attrs = SlotAttributes {
1333    ///     slot_type: SlotType::Static,
1334    ///     slot_id: 1,
1335    ///     path: None,
1336    ///     timestrech_mode: Default::default(),
1337    ///     loop_mode: Default::default(),
1338    ///     trig_quantization_mode: Default::default(),
1339    ///     gain: 48,
1340    ///     bpm: 2880
1341    /// };
1342    ///
1343    /// pub struct AttrsNewType(SlotAttributes);
1344    ///
1345    /// impl AsRef<SlotAttributes> for AttrsNewType {
1346    ///     fn as_ref(&self) -> &SlotAttributes {
1347    ///         &self.0
1348    ///     }
1349    /// }
1350    ///
1351    /// ActiveSlot::new(AttrsNewType(attrs), SlotMarkers::default());
1352    /// ```
1353    ///
1354    /// # [`SlotMarkers`] example
1355    ///
1356    /// ```
1357    /// use ot_tools_io::types::SlotMarkers;
1358    /// use ot_tools_io::types::ActiveSlot;
1359    /// # use ot_tools_io::types::SlotType;
1360    /// # use ot_tools_io::types::SlotAttributes;
1361    /// #
1362    /// # let attrs = SlotAttributes {
1363    /// #     slot_type: SlotType::Static,
1364    /// #     slot_id: 1,
1365    /// #     path: None,
1366    /// #     timestrech_mode: Default::default(),
1367    /// #     loop_mode: Default::default(),
1368    /// #     trig_quantization_mode: Default::default(),
1369    /// #     gain: 48,
1370    /// #     bpm: 2880
1371    /// # };
1372    ///
1373    /// let marks = SlotMarkers::default();
1374    ///
1375    /// pub struct MarksNewType(SlotMarkers);
1376    ///
1377    /// impl AsRef<SlotMarkers> for MarksNewType {
1378    ///     fn as_ref(&self) -> &SlotMarkers {
1379    ///         &self.0
1380    ///     }
1381    /// }
1382    ///
1383    /// // can use the type as an input here
1384    /// ActiveSlot::new(attrs, MarksNewType(marks));
1385    /// ```
1386    pub fn new(attrs: A, marks: M) -> Self {
1387        Self { attrs, marks }
1388    }
1389
1390    /// getter for attributes data
1391    pub fn attrs(self) -> A {
1392        self.attrs
1393    }
1394
1395    pub fn attrs_ref(&self) -> &A {
1396        &self.attrs
1397    }
1398
1399    /// reference getter for markers data
1400    pub fn marks(self) -> M {
1401        self.marks
1402    }
1403
1404    /// reference getter for markers data
1405    pub fn marks_ref(&self) -> &M {
1406        &self.marks
1407    }
1408}
1409
1410impl<A, M> ActiveSlot<A, M>
1411where
1412    A: AsRef<SlotAttributes> + AsMut<SlotAttributes>,
1413    M: AsRef<SlotMarkers> + AsMut<SlotMarkers>,
1414{
1415    /// mutable reference getter for attributes data
1416    pub fn attrs_mut(&mut self) -> &mut A {
1417        &mut self.attrs
1418    }
1419
1420    /// mutable reference getter for markers data
1421    pub fn marks_mut(&mut self) -> &mut M {
1422        &mut self.marks
1423    }
1424}
1425
1426// todo: ActiveSlot<A, M> -> ActiveSlot<Attrs, Marks>
1427//       ... apparently From<T> -> T exists in core
1428//       ... so how should this be called? do i even need to implement it?
1429
1430impl<A, M> From<ActiveSlot<A, M>> for SampleSettingsFile
1431where
1432    A: AsRef<SlotAttributes>,
1433    M: AsRef<SlotMarkers>,
1434{
1435    fn from(value: ActiveSlot<A, M>) -> Self {
1436        SampleSettingsFile::from((value.attrs_ref(), value.marks_ref()))
1437    }
1438}
1439
1440impl<A, M> TryFrom<Option<ActiveSlot<A, M>>> for SampleSettingsFile
1441where
1442    A: AsRef<SlotAttributes>,
1443    M: AsRef<SlotMarkers>,
1444{
1445    type Error = OtToolsIoError;
1446    fn try_from(value: Option<ActiveSlot<A, M>>) -> Result<Self, Self::Error> {
1447        // todo: error
1448        let value = value.ok_or(OtToolsIoError::InvalidIndex)?;
1449        Ok(SampleSettingsFile::from((
1450            value.attrs_ref(),
1451            value.marks_ref(),
1452        )))
1453    }
1454}