1use crate::projects::parse_hashmap_string_value;
30use crate::projects::ProjectParseError;
31use crate::settings::SlotType;
32
33use crate::settings::{InvalidValueError, LoopMode, TimeStretchMode, TrigQuantizationMode};
34
35use itertools::Itertools;
36use ot_tools_io_derive::IsDefaultCheck;
37use serde::de::{self, Deserializer, MapAccess, Visitor};
38use serde::{Deserialize, Serialize};
39use serde_big_array::Array;
40use std::{collections::HashMap, fmt, path::PathBuf, str::FromStr};
41
42#[derive(Serialize, PartialEq, Debug, Clone, Eq, Hash)]
53pub struct SlotAttributes {
54 pub slot_type: SlotType,
56
57 pub slot_id: u8,
61
62 pub path: Option<PathBuf>,
67
68 pub timestrech_mode: TimeStretchMode,
71
72 pub loop_mode: LoopMode,
75
76 pub trig_quantization_mode: TrigQuantizationMode,
80
81 pub gain: u8,
83
84 pub bpm: u16,
90}
91
92#[allow(clippy::too_many_arguments)] impl SlotAttributes {
94 pub fn new(
95 slot_type: SlotType,
96 slot_id: u8,
97 path: Option<PathBuf>,
98 timestretch_mode: Option<TimeStretchMode>,
99 loop_mode: Option<LoopMode>,
100 trig_quantization_mode: Option<TrigQuantizationMode>,
101 gain: Option<u8>,
102 bpm: Option<u16>,
103 ) -> Result<Self, crate::samples::SampleSettingsError> {
104 if let Some(tempo) = bpm {
105 if !(720..=7200).contains(&tempo) {
106 return Err(crate::samples::SampleSettingsError::TempoOutOfBounds {
107 value: tempo as u32,
108 });
109 }
110 }
111 if let Some(amp) = gain {
112 if !(24..=120).contains(&) {
113 return Err(crate::samples::SampleSettingsError::GainOutOfBounds {
114 value: amp as u32,
115 });
116 }
117 }
118
119 Ok(Self {
120 slot_type,
121 slot_id,
122 path,
123 timestrech_mode: timestretch_mode.unwrap_or_default(),
124 loop_mode: loop_mode.unwrap_or_default(),
125 trig_quantization_mode: trig_quantization_mode.unwrap_or_default(),
126 gain: gain.unwrap_or(72),
127 bpm: bpm.unwrap_or(2880),
128 })
129 }
130}
131
132fn parse_id(hmap: &HashMap<String, String>) -> Result<u8, ProjectParseError> {
133 let x = parse_hashmap_string_value::<u8>(hmap, "slot", None)?;
134 Ok(x)
135}
136
137fn parse_loop_mode(hmap: &HashMap<String, String>) -> Result<LoopMode, InvalidValueError> {
138 let default = LoopMode::default() as u8;
139 let default_str = format!["{default}"];
140
141 let x = parse_hashmap_string_value::<u8>(hmap, "loopmode", Some(default_str.as_str()))
142 .unwrap_or(default);
143 LoopMode::try_from(&x)
144}
145
146fn parse_tstrech_mode(
147 hmap: &HashMap<String, String>,
148) -> Result<TimeStretchMode, InvalidValueError> {
149 let default = TimeStretchMode::default() as u8;
150 let default_str = format!["{default}"];
151
152 let x = parse_hashmap_string_value::<u8>(hmap, "tsmode", Some(default_str.as_str()))
153 .unwrap_or(default);
154 TimeStretchMode::try_from(&x)
155}
156
157fn parse_trig_quantize_mode(
158 hmap: &HashMap<String, String>,
159) -> Result<TrigQuantizationMode, InvalidValueError> {
160 let default = TrigQuantizationMode::default() as u8;
161 let default_str = format!["{default}"];
162 let x = parse_hashmap_string_value::<u8>(hmap, "trigquantization", Some(default_str.as_str()))
163 .unwrap_or(default);
164 TrigQuantizationMode::try_from(x)
165}
166
167fn parse_gain(hmap: &HashMap<String, String>) -> Result<u8, ProjectParseError> {
168 let x = parse_hashmap_string_value::<u8>(hmap, "gain", Some("72")).unwrap_or(72_u8);
169 Ok(x)
170}
171
172fn parse_tempo(hmap: &HashMap<String, String>) -> Result<u16, ProjectParseError> {
173 let x = parse_hashmap_string_value::<u16>(hmap, "bpmx24", Some("2880")).unwrap_or(2880_u16);
174 Ok(x)
175}
176
177impl TryFrom<&HashMap<String, String>> for SlotAttributes {
178 type Error = ProjectParseError;
179 fn try_from(value: &HashMap<String, String>) -> Result<Self, Self::Error> {
180 let slot_id = parse_id(value)?;
181
182 let sample_slot_type = value
183 .get("type")
184 .ok_or(ProjectParseError::HashMap)?
185 .to_string();
186 let slot_type = SlotType::try_from(sample_slot_type)?;
187
188 let path_str = value.get("path").ok_or(ProjectParseError::HashMap)?;
189 let path = PathBuf::from_str(path_str).map_err(|_| ProjectParseError::String)?;
190
191 let loop_mode = parse_loop_mode(value)?;
192 let timestrech_mode = parse_tstrech_mode(value)?;
193 let trig_quantization_mode = parse_trig_quantize_mode(value)?;
194 let gain = parse_gain(value)?;
195 let bpm = parse_tempo(value)?;
196
197 let sample_struct = Self {
198 slot_type,
199 slot_id,
200 path: if path != PathBuf::from("") {
201 Some(path)
202 } else {
203 None
204 },
205 timestrech_mode,
206 loop_mode,
207 trig_quantization_mode,
208 gain,
209 bpm,
210 };
211
212 Ok(sample_struct)
213 }
214}
215
216impl FromStr for SlotAttributes {
217 type Err = ProjectParseError;
218
219 fn from_str(s: &str) -> Result<Self, Self::Err> {
220 let k_v: Vec<Vec<&str>> = s
221 .strip_prefix("\r\n\r\n[SAMPLE]\r\n")
222 .ok_or(ProjectParseError::HashMap)?
223 .strip_suffix("\r\n")
224 .ok_or(ProjectParseError::HashMap)?
225 .split("\r\n")
226 .map(|x: &str| x.split('=').collect_vec())
227 .filter(|x: &Vec<&str>| x.len() == 2)
228 .collect_vec();
229
230 let mut hmap: HashMap<String, String> = HashMap::new();
231 for key_value_pair in k_v {
232 hmap.insert(
233 key_value_pair[0].to_string().to_lowercase(),
234 key_value_pair[1].to_string(),
235 );
236 }
237
238 let sample_struct = SlotAttributes::try_from(&hmap)?;
239 Ok(sample_struct)
240 }
241}
242
243impl fmt::Display for SlotAttributes {
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
245 let mut s = "[SAMPLE]\r\n".to_string();
246 s.push_str(&format!("TYPE={}", self.slot_type));
247 s.push_str("\r\n");
248 s.push_str(format!("SLOT={:0>3}", self.slot_id).as_str());
250 s.push_str("\r\n");
251 if let Some(path) = &self.path {
253 s.push_str(
265 format!("PATH={path:#?}")
266 .replace('"', "") .replace("\\", "") .as_str(),
269 );
270 } else {
271 s.push_str("PATH=");
272 }
273 s.push_str("\r\n");
274 s.push_str(format!("BPMx24={}", self.bpm).as_str());
275 s.push_str("\r\n");
276 s.push_str(format!("TSMODE={}", self.timestrech_mode as u8).as_str());
277 s.push_str("\r\n");
278 s.push_str(format!("LOOPMODE={}", self.loop_mode as u8).as_str());
279 s.push_str("\r\n");
280 s.push_str(format!("GAIN={}", self.gain).as_str());
281 s.push_str("\r\n");
282 s.push_str(format!("TRIGQUANTIZATION={}", self.trig_quantization_mode as u8).as_str());
283 s.push_str("\r\n[/SAMPLE]");
284 write!(f, "{s:#}")
285 }
286}
287
288impl<'de> Deserialize<'de> for SlotAttributes {
292 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
293 where
294 D: Deserializer<'de>,
295 {
296 enum Field {
297 SlotType,
298 SlotId,
299 Path,
300 Timestretch,
301 Loop,
302 Quant,
303 Gain,
304 Bpm,
305 }
306
307 const FIELDS: &[&str] = &[
309 "slot_type",
310 "slot_id",
311 "path",
312 "timestrech_mode",
313 "loop_mode",
314 "trig_quantization_mode",
315 "gain",
316 "bpm",
317 ];
318
319 impl<'de> Deserialize<'de> for Field {
320 fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
321 where
322 D: Deserializer<'de>,
323 {
324 struct FieldVisitor;
325
326 impl Visitor<'_> for FieldVisitor {
327 type Value = Field;
328
329 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
330 formatter.write_str(
331 FIELDS
332 .iter()
333 .map(|x| format!["`{x}`"])
334 .collect::<Vec<_>>()
335 .join(" or ")
336 .as_str(),
337 )
338 }
339
340 fn visit_str<E>(self, value: &str) -> Result<Field, E>
341 where
342 E: de::Error,
343 {
344 match value {
345 "slot_type" => Ok(Field::SlotType),
346 "slot_id" => Ok(Field::SlotId),
347 "path" => Ok(Field::Path),
348 "timestrech_mode" => Ok(Field::Timestretch),
349 "loop_mode" => Ok(Field::Loop),
350 "trig_quantization_mode" => Ok(Field::Quant),
351 "gain" => Ok(Field::Gain),
352 "bpm" => Ok(Field::Bpm),
353 _ => Err(de::Error::unknown_field(value, FIELDS)),
354 }
355 }
356 }
357
358 deserializer.deserialize_identifier(FieldVisitor)
359 }
360 }
361
362 struct SlotAttributesVisitor;
363
364 impl<'de> Visitor<'de> for SlotAttributesVisitor {
365 type Value = SlotAttributes;
366
367 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
368 formatter.write_str("struct SlotAttributes")
369 }
370
371 fn visit_map<V>(self, mut map: V) -> Result<SlotAttributes, V::Error>
372 where
373 V: MapAccess<'de>,
374 {
375 let mut slot_type = None;
376 let mut slot_id = None;
377 let mut path = None;
378 let mut timestretch_mode = None;
379 let mut loop_mode = None;
380 let mut trig_quantization_mode = None;
381 let mut gain = None;
382 let mut bpm = None;
383
384 while let Some(key) = map.next_key()? {
385 match key {
386 Field::SlotType => {
387 if slot_type.is_some() {
388 return Err(de::Error::duplicate_field("slot_type"));
389 }
390 slot_type = Some(map.next_value::<SlotType>()?);
391 }
392 Field::SlotId => {
393 if slot_id.is_some() {
394 return Err(de::Error::duplicate_field("slot_id"));
395 }
396 slot_id = Some(map.next_value::<u8>()?);
397 }
398 Field::Path => {
399 if path.is_some() {
400 return Err(de::Error::duplicate_field("path"));
401 }
402 path = Some(map.next_value::<PathBuf>()?);
403 }
404 Field::Timestretch => {
405 if timestretch_mode.is_some() {
406 return Err(de::Error::duplicate_field("timestretch_mode"));
407 }
408 timestretch_mode = Some(map.next_value::<TimeStretchMode>()?);
409 }
410 Field::Loop => {
411 if loop_mode.is_some() {
412 return Err(de::Error::duplicate_field("loop_mode"));
413 }
414 loop_mode = Some(map.next_value::<LoopMode>()?);
415 }
416 Field::Quant => {
417 if trig_quantization_mode.is_some() {
418 return Err(de::Error::duplicate_field("trig_quantization_mode"));
419 }
420 trig_quantization_mode =
421 Some(map.next_value::<TrigQuantizationMode>()?);
422 }
423 Field::Gain => {
424 if gain.is_some() {
425 return Err(de::Error::duplicate_field("gain"));
426 }
427 gain = Some(map.next_value::<u8>()?);
428 }
429 Field::Bpm => {
430 if bpm.is_some() {
431 return Err(de::Error::duplicate_field("bpm"));
432 }
433 bpm = Some(map.next_value::<u16>()?);
434 }
435 }
436 }
437
438 let slot = SlotAttributes {
439 slot_type: slot_type.ok_or_else(|| de::Error::missing_field("slot_type"))?,
440 slot_id: slot_id.ok_or_else(|| de::Error::missing_field("slot_type"))?,
441 path, timestrech_mode: timestretch_mode
443 .ok_or_else(|| de::Error::missing_field("trimstretch_mode"))?,
444 loop_mode: loop_mode.ok_or_else(|| de::Error::missing_field("loop_mode"))?,
445 trig_quantization_mode: trig_quantization_mode
446 .ok_or_else(|| de::Error::missing_field("trig_quantization_mode"))?,
447 gain: gain.ok_or_else(|| de::Error::missing_field("gain"))?,
448 bpm: bpm.ok_or_else(|| de::Error::missing_field("bpm"))?,
449 };
450
451 Ok(slot)
452 }
453 }
454
455 deserializer.deserialize_struct("SampleSlot", FIELDS, SlotAttributesVisitor)
456 }
457}
458
459#[derive(Eq, PartialEq, Clone, Debug, Serialize, IsDefaultCheck)]
488pub struct SlotsAttributes {
489 pub static_slots: Array<Option<SlotAttributes>, 128>,
490 pub flex_slots: Array<Option<SlotAttributes>, 136>,
491}
492
493impl<'de> Deserialize<'de> for SlotsAttributes {
497 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
498 where
499 D: Deserializer<'de>,
500 {
501 enum Field {
502 Static,
503 Flex,
504 }
505
506 impl<'de> Deserialize<'de> for Field {
507 fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
508 where
509 D: Deserializer<'de>,
510 {
511 struct FieldVisitor;
512
513 impl Visitor<'_> for FieldVisitor {
514 type Value = Field;
515
516 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
517 formatter.write_str("`static_slots` or `flex_slots`")
518 }
519
520 fn visit_str<E>(self, value: &str) -> Result<Field, E>
521 where
522 E: de::Error,
523 {
524 match value {
525 "static_slots" => Ok(Field::Static),
526 "flex_slots" => Ok(Field::Flex),
527 _ => Err(de::Error::unknown_field(value, FIELDS)),
528 }
529 }
530 }
531
532 deserializer.deserialize_identifier(FieldVisitor)
533 }
534 }
535
536 struct SampleSlotsVisitor;
537
538 impl<'de> Visitor<'de> for SampleSlotsVisitor {
539 type Value = SlotsAttributes;
540
541 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
542 formatter.write_str("struct SampleSlots")
543 }
544
545 fn visit_unit<E>(self) -> Result<SlotsAttributes, E> {
546 Ok(SlotsAttributes::default())
547 }
548 fn visit_map<V>(self, mut map: V) -> Result<SlotsAttributes, V::Error>
549 where
550 V: MapAccess<'de>,
551 {
552 let mut static_slots = None;
553 let mut flex_slots = None;
554 while let Some(key) = map.next_key()? {
555 match key {
556 Field::Static => {
557 if static_slots.is_some() {
558 return Err(de::Error::duplicate_field("static_slots"));
559 }
560 static_slots =
561 Some(map.next_value::<Array<Option<SlotAttributes>, 128>>()?);
562 }
563 Field::Flex => {
564 if flex_slots.is_some() {
565 return Err(de::Error::duplicate_field("flex_slots"));
566 }
567 flex_slots =
568 Some(map.next_value::<Array<Option<SlotAttributes>, 136>>()?);
569 }
570 }
571 }
572 let s_slots =
573 static_slots.ok_or_else(|| de::Error::missing_field("static_slots"))?;
574
575 let f_slots = flex_slots.ok_or_else(|| de::Error::missing_field("flex_slots"))?;
576
577 let slots = SlotsAttributes {
578 static_slots: s_slots,
579 flex_slots: f_slots,
580 };
581
582 Ok(slots)
583 }
584 }
585
586 const FIELDS: &[&str] = &["static_slots", "flex_slots"];
587 deserializer.deserialize_struct("SampleSlots", FIELDS, SampleSlotsVisitor)
588 }
589}
590
591fn flex_slot_default_case_switch(i: usize) -> Option<SlotAttributes> {
593 if i <= 127 {
594 None
595 } else {
596 Some(SlotAttributes {
597 slot_type: SlotType::Flex,
598 slot_id: i as u8 + 1,
600 path: None,
601 timestrech_mode: TimeStretchMode::default(),
602 loop_mode: LoopMode::default(),
603 trig_quantization_mode: TrigQuantizationMode::default(),
604 gain: 72,
605 bpm: 2880,
606 })
607 }
608}
609
610impl Default for SlotsAttributes {
611 fn default() -> Self {
612 Self {
613 static_slots: Array(std::array::from_fn(|_| None)),
614 flex_slots: Array(std::array::from_fn(flex_slot_default_case_switch)),
615 }
616 }
617}
618
619impl FromStr for SlotsAttributes {
620 type Err = ProjectParseError;
621
622 fn from_str(s: &str) -> Result<Self, Self::Err> {
623 let footer_stripped = s
624 .strip_suffix("\r\n\r\n############################\r\n\r\n")
625 .ok_or(ProjectParseError::Footer)?;
626
627 let data_window: Vec<&str> = footer_stripped
628 .split("############################\r\n# Samples\r\n############################")
629 .collect();
630
631 let mut samples_string: Vec<&str> = data_window[1].split("[/SAMPLE]").collect();
632 samples_string.pop();
634
635 let mut slots = Self::default();
637
638 for s in &samples_string {
639 let slot = SlotAttributes::from_str(s)?;
641 let zero_indexed_id = slot.slot_id as usize - 1;
642 match slot.slot_type {
643 SlotType::Static => {
644 slots.static_slots[zero_indexed_id] = Some(slot);
645 }
646 SlotType::Flex => {
647 slots.flex_slots[zero_indexed_id] = Some(slot);
648 }
649 }
650 }
651
652 Ok(slots)
653 }
654}
655
656impl fmt::Display for SlotsAttributes {
657 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
658 let mut string_slots: String = "".to_string();
659
660 let slots = vec![self.static_slots.to_vec(), self.flex_slots.to_vec()];
661
662 let slots_concat = itertools::concat(slots).into_iter().flatten();
663
664 for slot in slots_concat {
665 string_slots.push_str(&slot.to_string());
666 string_slots.push_str("\r\n\r\n");
667 }
668 string_slots = string_slots
669 .strip_suffix("\r\n\r\n")
670 .ok_or(fmt::Error)?
671 .to_string();
672 write!(f, "{string_slots:#}")
673 }
674}
675
676#[cfg(test)]
677#[allow(unused_imports)]
678mod test {
679
680 #[test]
681 fn parse_id_001_correct() {
682 let mut hmap = std::collections::HashMap::new();
683 hmap.insert("slot".to_string(), "001".to_string());
684
685 let slot_id = crate::projects::slots::parse_id(&hmap);
686
687 assert_eq!(1, slot_id.unwrap());
688 }
689
690 #[test]
691 fn parse_id_1_correct() {
692 let mut hmap = std::collections::HashMap::new();
693 hmap.insert("slot".to_string(), "1".to_string());
694
695 let slot_id = crate::projects::slots::parse_id(&hmap);
696
697 assert_eq!(1, slot_id.unwrap());
698 }
699
700 #[test]
701 fn parse_id_127_correct() {
702 let mut hmap = std::collections::HashMap::new();
703 hmap.insert("slot".to_string(), "127".to_string());
704
705 let slot_id = crate::projects::slots::parse_id(&hmap);
706
707 assert_eq!(127, slot_id.unwrap());
708 }
709
710 #[test]
711 fn parse_id_099_correct() {
712 let mut hmap = std::collections::HashMap::new();
713 hmap.insert("slot".to_string(), "099".to_string());
714
715 let slot_id = crate::projects::slots::parse_id(&hmap);
716
717 assert_eq!(99, slot_id.unwrap());
718 }
719
720 #[test]
721 fn parse_id_010_correct() {
722 let mut hmap = std::collections::HashMap::new();
723 hmap.insert("slot".to_string(), "010".to_string());
724
725 let slot_id = crate::projects::slots::parse_id(&hmap);
726
727 assert_eq!(10, slot_id.unwrap());
728 }
729
730 #[test]
731 fn test_parse_id_err_bad_value_type_err() {
732 let mut hmap = std::collections::HashMap::new();
733 hmap.insert("slot".to_string(), "AAAA".to_string());
734 let slot_id = crate::projects::slots::parse_id(&hmap);
735 assert!(slot_id.is_err());
736 }
737
738 #[test]
739 fn test_parse_tempo_correct_default() {
740 let mut hmap = std::collections::HashMap::new();
741 hmap.insert("bpmx24".to_string(), "2880".to_string());
742 let r = crate::projects::slots::parse_tempo(&hmap);
743 assert_eq!(2880_u16, r.unwrap());
744 }
745
746 #[test]
747 fn test_parse_tempo_correct_min() {
748 let mut hmap = std::collections::HashMap::new();
749 hmap.insert("bpmx24".to_string(), "720".to_string());
750 let r = crate::projects::slots::parse_tempo(&hmap);
751 assert_eq!(720_u16, r.unwrap());
752 }
753
754 #[test]
755 fn test_parse_tempo_correct_max() {
756 let mut hmap = std::collections::HashMap::new();
757 hmap.insert("bpmx24".to_string(), "7200".to_string());
758 let r = crate::projects::slots::parse_tempo(&hmap);
759 assert_eq!(7200_u16, r.unwrap());
760 }
761
762 #[test]
763 fn test_parse_tempo_bad_value_type_default_return() {
764 let mut hmap = std::collections::HashMap::new();
765 hmap.insert("bpmx24".to_string(), "AAAFSFSFSSFfssafAA".to_string());
766 let r = crate::projects::slots::parse_tempo(&hmap);
767 assert_eq!(r.unwrap(), 2880_u16);
768 }
769
770 #[test]
771 fn test_parse_gain_correct() {
772 let mut hmap = std::collections::HashMap::new();
773 hmap.insert("gain".to_string(), "72".to_string());
774 let r = crate::projects::slots::parse_gain(&hmap);
775 assert_eq!(72, r.unwrap());
776 }
777
778 #[test]
779 fn test_parse_gain_bad_value_type_default_return() {
780 let mut hmap = std::collections::HashMap::new();
781 hmap.insert("gain".to_string(), "AAAFSFSFSSFfssafAA".to_string());
782 let r = crate::projects::slots::parse_gain(&hmap);
783 assert_eq!(r.unwrap(), 72_u8);
784 }
785
786 #[test]
787 fn test_parse_loop_mode_correct_off() {
788 let mut hmap = std::collections::HashMap::new();
789 hmap.insert("loopmode".to_string(), "0".to_string());
790 let r = crate::projects::slots::parse_loop_mode(&hmap);
791 assert_eq!(r.unwrap(), crate::settings::LoopMode::Off);
792 }
793
794 #[test]
795 fn test_parse_loop_mode_correct_normal() {
796 let mut hmap = std::collections::HashMap::new();
797 hmap.insert("loopmode".to_string(), "1".to_string());
798 let r = crate::projects::slots::parse_loop_mode(&hmap);
799 assert_eq!(r.unwrap(), crate::settings::LoopMode::Normal);
800 }
801
802 #[test]
803 fn test_parse_loop_mode_correct_pingpong() {
804 let mut hmap = std::collections::HashMap::new();
805 hmap.insert("loopmode".to_string(), "2".to_string());
806 let r = crate::projects::slots::parse_loop_mode(&hmap);
807 assert_eq!(r.unwrap(), crate::settings::LoopMode::PingPong);
808 }
809
810 #[test]
811 fn test_parse_loop_mode_bad_value_type_default_return() {
812 let mut hmap = std::collections::HashMap::new();
813 hmap.insert("loopmode".to_string(), "AAAFSFSFSSFfssafAA".to_string());
814 let r = crate::projects::slots::parse_loop_mode(&hmap);
815 assert_eq!(r.unwrap(), crate::settings::LoopMode::default());
816 }
817
818 #[test]
819 fn test_parse_tstretch_correct_off() {
820 let mut hmap = std::collections::HashMap::new();
821 hmap.insert("tsmode".to_string(), "0".to_string());
822 let r = crate::projects::slots::parse_tstrech_mode(&hmap);
823 assert_eq!(crate::settings::TimeStretchMode::Off, r.unwrap());
824 }
825
826 #[test]
827 fn test_parse_tstretch_correct_normal() {
828 let mut hmap = std::collections::HashMap::new();
829 hmap.insert("tsmode".to_string(), "2".to_string());
830 let r = crate::projects::slots::parse_tstrech_mode(&hmap);
831 assert_eq!(crate::settings::TimeStretchMode::Normal, r.unwrap());
832 }
833
834 #[test]
835 fn test_parse_tstretch_correct_beat() {
836 let mut hmap = std::collections::HashMap::new();
837 hmap.insert("tsmode".to_string(), "3".to_string());
838 let r = crate::projects::slots::parse_tstrech_mode(&hmap);
839 assert_eq!(crate::settings::TimeStretchMode::Beat, r.unwrap());
840 }
841
842 #[test]
843 fn test_parse_tstretch_bad_value_type_default_return() {
844 let mut hmap = std::collections::HashMap::new();
845 hmap.insert("tsmode".to_string(), "AAAFSFSFSSFfssafAA".to_string());
846 let r = crate::projects::slots::parse_tstrech_mode(&hmap);
847 assert_eq!(r.unwrap(), crate::settings::TimeStretchMode::default());
848 }
849
850 #[test]
851 fn test_parse_tquantize_correct_off() {
852 let mut hmap = std::collections::HashMap::new();
853 hmap.insert("trigquantization".to_string(), "255".to_string());
854 let r = crate::projects::slots::parse_trig_quantize_mode(&hmap);
855 assert_eq!(crate::settings::TrigQuantizationMode::Direct, r.unwrap());
856 }
857
858 #[test]
859 fn test_parse_tquantize_correct_direct() {
860 let mut hmap = std::collections::HashMap::new();
861 hmap.insert("trigquantization".to_string(), "0".to_string());
862 let r = crate::projects::slots::parse_trig_quantize_mode(&hmap);
863 assert_eq!(
864 crate::settings::TrigQuantizationMode::PatternLength,
865 r.unwrap()
866 );
867 }
868
869 #[test]
870 fn test_parse_tquantize_correct_onestep() {
871 let mut hmap = std::collections::HashMap::new();
872 hmap.insert("trigquantization".to_string(), "1".to_string());
873 let r = crate::projects::slots::parse_trig_quantize_mode(&hmap);
874 assert_eq!(crate::settings::TrigQuantizationMode::OneStep, r.unwrap());
875 }
876
877 #[test]
878 fn test_parse_tquantize_correct_twostep() {
879 let mut hmap = std::collections::HashMap::new();
880 hmap.insert("trigquantization".to_string(), "2".to_string());
881 let r = crate::projects::slots::parse_trig_quantize_mode(&hmap);
882 assert_eq!(crate::settings::TrigQuantizationMode::TwoSteps, r.unwrap());
883 }
884
885 #[test]
886 fn test_parse_tquantize_correct_threestep() {
887 let mut hmap = std::collections::HashMap::new();
888 hmap.insert("trigquantization".to_string(), "3".to_string());
889 let r = crate::projects::slots::parse_trig_quantize_mode(&hmap);
890 assert_eq!(
891 crate::settings::TrigQuantizationMode::ThreeSteps,
892 r.unwrap()
893 );
894 }
895
896 #[test]
897 fn test_parse_tquantize_correct_fourstep() {
898 let mut hmap = std::collections::HashMap::new();
899 hmap.insert("trigquantization".to_string(), "4".to_string());
900 let r = crate::projects::slots::parse_trig_quantize_mode(&hmap);
901 assert_eq!(crate::settings::TrigQuantizationMode::FourSteps, r.unwrap());
902 }
903
904 #[test]
907 fn test_parse_tquantize_bad_value_type_default_return() {
908 let mut hmap = std::collections::HashMap::new();
909 hmap.insert(
910 "trigquantization".to_string(),
911 "AAAFSFSFSSFfssafAA".to_string(),
912 );
913 let r = crate::projects::slots::parse_trig_quantize_mode(&hmap);
914 assert_eq!(r.unwrap(), crate::settings::TrigQuantizationMode::default());
915 }
916}