1use super::{is_not_valid_ascii_byte, ArrangementError};
7use crate::{Defaults, OtToolsIoError};
8use ot_tools_io_derive::IsDefaultCheck;
9use ot_tools_io_derive::{ArrayDefaults, AsMutDerive, AsRefDerive, BoxedArrayDefaults};
10use serde::{Deserialize, Serialize};
11use std::array::from_fn;
12
13#[derive(
32 Copy,
33 Clone,
34 Debug,
35 Eq,
36 Hash,
37 Ord,
38 PartialEq,
39 PartialOrd,
40 Deserialize,
41 Serialize,
42 ArrayDefaults,
43 BoxedArrayDefaults,
44 AsRefDerive,
45 AsMutDerive,
46 IsDefaultCheck,
47)]
48#[serde(rename_all = "snake_case")]
49pub enum ArrangeRow {
50 PatternRow(PatternRow),
52 LoopOrJumpOrHaltRow(LoopOrJumpOrHaltRow),
54 ReminderRow(ReminderRow),
56 EmptyRow([u8; 22]),
59}
60
61impl Default for ArrangeRow {
62 fn default() -> Self {
63 Self::EmptyRow(from_fn(|_| 0))
64 }
65}
66
67impl ArrangeRow {
68 pub fn validate(&self) -> Result<(), OtToolsIoError> {
94 match self {
95 Self::EmptyRow(_) => Ok(()),
96 Self::LoopOrJumpOrHaltRow(x) => x.validate(),
97 Self::PatternRow(x) => x.validate(),
98 Self::ReminderRow(x) => x.validate(),
99 }
100 }
101
102 pub(super) fn parse_to_bytes(&self) -> Result<Vec<u8>, OtToolsIoError> {
103 let mut v = vec![];
104 match self {
105 Self::EmptyRow(x) => v.append(&mut x.to_vec()),
106 Self::PatternRow(pattern_row) => {
107 pattern_row.validate()?;
108 let PatternRow {
109 pattern_id,
110 repetitions,
111 unused_1,
112 mute_mask,
113 unused_2,
114 tempo_1,
115 tempo_2,
116 scene_a,
117 scene_b,
118 unused_3,
119 offset,
120 unused_4,
121 length,
122 midi_transpose,
123 } = *pattern_row;
124
125 v.append(&mut vec![0]); v.append(&mut vec![pattern_id]);
127 v.append(&mut vec![repetitions]);
128 v.append(&mut vec![unused_1]);
129 v.append(&mut vec![mute_mask]);
130 v.append(&mut vec![unused_2]);
131 v.append(&mut vec![tempo_1]);
132 v.append(&mut vec![tempo_2]);
133 v.append(&mut vec![scene_a]);
134 v.append(&mut vec![scene_b]);
135 v.append(&mut vec![unused_3]);
136 v.append(&mut vec![offset]);
137 v.append(&mut vec![unused_4]);
138 v.append(&mut vec![length]);
139 v.append(&mut midi_transpose.to_vec());
140 }
141 Self::LoopOrJumpOrHaltRow(loop_row) => {
142 loop_row.validate()?;
143 let LoopOrJumpOrHaltRow {
144 loop_count,
145 row_target,
146 unused,
147 } = *loop_row;
148 v.append(&mut vec![1]); v.append(&mut vec![loop_count]);
150 v.append(&mut vec![row_target]);
151 v.append(&mut unused.to_vec());
152 }
153 Self::ReminderRow(reminder_row) => {
154 reminder_row.validate()?;
155 let ReminderRow { reminder, unused } = *reminder_row;
156 v.append(&mut vec![2]); v.append(&mut reminder.to_vec());
158 v.append(&mut unused.to_vec());
159 }
160 }
161 Ok(v)
162 }
163
164 pub(super) fn parse_from_bytes(bytes: &[u8; 22]) -> Result<Self, OtToolsIoError> {
165 let (row_type, row_data) = bytes.split_first().unwrap();
166
167 match row_type {
171 0 => {
173 let pattern_id = row_data[0];
174 let repetitions = row_data[1];
175 let unused_1 = row_data[2];
176 let mute_mask = row_data[3];
178 let unused_2 = row_data[4];
179 let tempo_1 = row_data[5];
181 let tempo_2 = row_data[6];
182 let scene_a = row_data[7];
183 let scene_b = row_data[8];
184 let unused_3 = row_data[9];
185 let offset = row_data[10];
187 let unused_4 = row_data[11];
188 let length = row_data[12];
193 let midi_transpose: [u8; 8] = from_fn(|x| row_data[x + 13]);
194
195 let pattern = PatternRow {
196 pattern_id,
197 repetitions,
198 mute_mask,
199 tempo_1,
200 tempo_2,
201 scene_a,
202 scene_b,
203 offset,
204 length,
205 midi_transpose,
206 unused_1,
207 unused_2,
208 unused_3,
209 unused_4,
210 };
211
212 pattern.validate()?;
213 Ok(ArrangeRow::PatternRow(pattern))
214 }
215 1 => {
217 let loop_count = row_data[0];
218 let row_target = row_data[1];
219 let unused: [u8; 19] = from_fn(|x| row_data[x + 2]);
220
221 let loop_row = LoopOrJumpOrHaltRow {
222 loop_count,
223 row_target,
224 unused,
225 };
226 loop_row.validate()?;
227
228 Ok(ArrangeRow::LoopOrJumpOrHaltRow(loop_row))
229 }
230 2 => {
232 let reminder = from_fn(|x| row_data[x]);
233 let unused: [u8; 6] = from_fn(|x| row_data[x + 15]);
234 let reminder_row = ReminderRow { reminder, unused };
235 reminder_row.validate()?;
236 Ok(ArrangeRow::ReminderRow(reminder_row))
237 }
238 _ => Err(ArrangementError::RowType(*row_type).into()),
240 }
241 }
242}
243
244#[derive(
250 Copy,
251 Clone,
252 Debug,
253 Eq,
254 Hash,
255 Ord,
256 PartialEq,
257 PartialOrd,
258 Serialize,
259 Deserialize,
260 AsRefDerive,
261 AsMutDerive,
262)]
263pub struct PatternRow {
264 pub pattern_id: u8,
270 pub repetitions: u8,
276 pub unused_1: u8,
282 pub mute_mask: u8,
288 pub unused_2: u8,
294 pub tempo_1: u8,
301 pub tempo_2: u8,
308 pub scene_a: u8,
314 pub scene_b: u8,
320 pub unused_3: u8,
326 pub offset: u8,
333 pub unused_4: u8,
339 pub length: u8,
348 pub midi_transpose: [u8; 8],
357}
358
359impl PatternRow {
360 pub fn validate(&self) -> Result<(), OtToolsIoError> {
363 if self.repetitions > 63 {
364 Err(ArrangementError::RepetitionCount(self.repetitions).into())
365 } else if self.scene_a > 15 && self.scene_a != 255 {
366 Err(ArrangementError::Scene(self.scene_a).into())
367 } else if self.scene_b > 15 && self.scene_b != 255 {
368 Err(ArrangementError::Scene(self.scene_b).into())
369 } else if self.midi_transpose.iter().any(|x| (49..208).contains(x)) {
370 Err(ArrangementError::MidiTranspose(self.midi_transpose).into())
371 } else {
372 Ok(())
373 }
374 }
375}
376
377#[cfg(test)]
378mod pattern_row {
379 mod validate {
380 use crate::arrangements::{ArrangementError, PatternRow};
381 use crate::OtToolsIoError;
382
383 #[test]
384 fn ok_maxish_values() {
385 let x = PatternRow {
386 pattern_id: 255,
387 repetitions: 63,
388 unused_1: 0,
389 mute_mask: 0, unused_2: 0,
391 tempo_1: 0, tempo_2: 0, scene_a: 15,
394 scene_b: 15,
395 unused_3: 0,
396 offset: 0, unused_4: 0,
398 length: 0, midi_transpose: [255, 255, 255, 255, 255, 255, 255, 255],
400 };
401 assert!(x.validate().is_ok());
402 }
403
404 #[test]
405 fn invalid_repetitions() {
406 let x = PatternRow {
407 pattern_id: 0,
408 repetitions: 64,
409 unused_1: 0,
410 mute_mask: 0,
411 unused_2: 0,
412 tempo_1: 0,
413 tempo_2: 0,
414 scene_a: 0,
415 scene_b: 0,
416 unused_3: 0,
417 offset: 0,
418 unused_4: 0,
419 length: 0,
420 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
421 };
422 assert!(x.validate().is_err());
423 assert_eq!(
424 x.validate().err().unwrap().to_string(),
425 OtToolsIoError::Arrangement(ArrangementError::RepetitionCount(64)).to_string(),
426 );
427 }
428
429 #[test]
430 fn valid_scene_a_off() {
431 let x = PatternRow {
432 pattern_id: 0,
433 repetitions: 0,
434 unused_1: 0,
435 mute_mask: 0,
436 unused_2: 0,
437 tempo_1: 0,
438 tempo_2: 0,
439 scene_a: 255,
440 scene_b: 0,
441 unused_3: 0,
442 offset: 0,
443 unused_4: 0,
444 length: 0,
445 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
446 };
447 assert!(x.validate().is_ok());
448 }
449
450 #[test]
451 fn valid_scene_b_off() {
452 let x = PatternRow {
453 pattern_id: 0,
454 repetitions: 0,
455 unused_1: 0,
456 mute_mask: 0,
457 unused_2: 0,
458 tempo_1: 0,
459 tempo_2: 0,
460 scene_a: 0,
461 scene_b: 255,
462 unused_3: 0,
463 offset: 0,
464 unused_4: 0,
465 length: 0,
466 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
467 };
468 assert!(x.validate().is_ok());
469 }
470
471 #[test]
472 fn valid_scene_a_max() {
473 let x = PatternRow {
474 pattern_id: 0,
475 repetitions: 0,
476 unused_1: 0,
477 mute_mask: 0,
478 unused_2: 0,
479 tempo_1: 0,
480 tempo_2: 0,
481 scene_a: 15,
482 scene_b: 0,
483 unused_3: 0,
484 offset: 0,
485 unused_4: 0,
486 length: 0,
487 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
488 };
489 assert!(x.validate().is_ok());
490 }
491
492 #[test]
493 fn valid_scene_b_max() {
494 let x = PatternRow {
495 pattern_id: 0,
496 repetitions: 0,
497 unused_1: 0,
498 mute_mask: 0,
499 unused_2: 0,
500 tempo_1: 0,
501 tempo_2: 0,
502 scene_a: 0,
503 scene_b: 15,
504 unused_3: 0,
505 offset: 0,
506 unused_4: 0,
507 length: 0,
508 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
509 };
510 assert!(x.validate().is_ok());
511 }
512
513 #[test]
514 fn invalid_scene_a() {
515 let x = PatternRow {
516 pattern_id: 0,
517 repetitions: 0,
518 unused_1: 0,
519 mute_mask: 0,
520 unused_2: 0,
521 tempo_1: 0,
522 tempo_2: 0,
523 scene_a: 16,
524 scene_b: 0,
525 unused_3: 0,
526 offset: 0,
527 unused_4: 0,
528 length: 0,
529 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
530 };
531 assert!(x.validate().is_err());
532 assert_eq!(
533 x.validate().err().unwrap().to_string(),
534 OtToolsIoError::Arrangement(ArrangementError::Scene(16)).to_string(),
535 );
536 }
537
538 #[test]
539 fn invalid_scene_b() {
540 let x = PatternRow {
541 pattern_id: 0,
542 repetitions: 0,
543 unused_1: 0,
544 mute_mask: 0,
545 unused_2: 0,
546 tempo_1: 0,
547 tempo_2: 0,
548 scene_a: 0,
549 scene_b: 16,
550 unused_3: 0,
551 offset: 0,
552 unused_4: 0,
553 length: 0,
554 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
555 };
556 assert!(x.validate().is_err());
557 assert_eq!(
558 x.validate().err().unwrap().to_string(),
559 OtToolsIoError::Arrangement(ArrangementError::Scene(16)).to_string(),
560 );
561 }
562
563 #[test]
564 fn invalid_midi_transpose_lower() {
565 let x = PatternRow {
566 pattern_id: 0,
567 repetitions: 0,
568 unused_1: 0,
569 mute_mask: 0,
570 unused_2: 0,
571 tempo_1: 0,
572 tempo_2: 0,
573 scene_a: 0,
574 scene_b: 0,
575 unused_3: 0,
576 offset: 0,
577 unused_4: 0,
578 length: 0,
579 midi_transpose: [49, 49, 49, 49, 49, 49, 49, 49],
580 };
581 assert!(x.validate().is_err());
582 assert_eq!(
583 x.validate().err().unwrap().to_string(),
584 OtToolsIoError::Arrangement(ArrangementError::MidiTranspose([
585 49, 49, 49, 49, 49, 49, 49, 49
586 ]))
587 .to_string(),
588 );
589 }
590
591 #[test]
592 fn invalid_midi_transpose_upper() {
593 let x = PatternRow {
594 pattern_id: 0,
595 repetitions: 0,
596 unused_1: 0,
597 mute_mask: 0,
598 unused_2: 0,
599 tempo_1: 0,
600 tempo_2: 0,
601 scene_a: 0,
602 scene_b: 0,
603 unused_3: 0,
604 offset: 0,
605 unused_4: 0,
606 length: 0,
607 midi_transpose: [207, 207, 207, 207, 207, 207, 207, 207],
608 };
609 assert!(x.validate().is_err());
610 assert_eq!(
611 x.validate().err().unwrap().to_string(),
612 OtToolsIoError::Arrangement(ArrangementError::MidiTranspose([
613 207, 207, 207, 207, 207, 207, 207, 207
614 ]))
615 .to_string(),
616 );
617 }
618 }
619}
620
621#[derive(
626 Copy,
627 Clone,
628 Debug,
629 Eq,
630 Hash,
631 Ord,
632 PartialEq,
633 PartialOrd,
634 Serialize,
635 Deserialize,
636 AsRefDerive,
637 AsMutDerive,
638)]
639pub struct LoopOrJumpOrHaltRow {
640 pub loop_count: u8,
646 pub row_target: u8,
652 pub unused: [u8; 19],
658}
659
660impl LoopOrJumpOrHaltRow {
661 pub fn new(loop_count: u8, row_target: u8, unused_bytes: [u8; 19]) -> Self {
663 Self {
664 loop_count,
665 row_target,
666 unused: unused_bytes,
667 }
668 }
669
670 pub fn new_without_unused(loop_count: u8, row_target: u8) -> Self {
673 Self::new(loop_count, row_target, Default::default())
674 }
675
676 pub fn validate(&self) -> Result<(), OtToolsIoError> {
701 if self.loop_count > 100 {
702 Err(ArrangementError::LoopCount(self.loop_count).into())
703 } else {
704 Ok(())
705 }
706 }
707}
708
709#[cfg(test)]
710mod loop_or_jump_or_halt_row {
711 mod new {
712 use crate::arrangements::LoopOrJumpOrHaltRow;
713
714 #[test]
715 fn valid() {
716 let x = LoopOrJumpOrHaltRow::new(1, 1, Default::default());
717 assert_eq!(x.loop_count, 1);
718 assert_eq!(x.row_target, 1);
719 assert_eq!(x.unused, std::array::from_fn(|_| 0));
720 }
721 }
722
723 mod new_without_unused {
724 use crate::arrangements::LoopOrJumpOrHaltRow;
725
726 #[test]
727 fn valid() {
728 let x = LoopOrJumpOrHaltRow::new_without_unused(1, 1);
729 assert_eq!(x.loop_count, 1);
730 assert_eq!(x.row_target, 1);
731 assert_eq!(x.unused, std::array::from_fn(|_| 0));
732 }
733 }
734
735 mod validate {
736 use crate::arrangements::{ArrangementError, LoopOrJumpOrHaltRow};
737 use crate::OtToolsIoError;
738
739 #[test]
740 fn valid() {
741 let x = LoopOrJumpOrHaltRow {
742 loop_count: 1,
743 row_target: 1,
744 unused: std::array::from_fn(|_| 0),
745 };
746 let r = x.validate();
747 assert!(r.is_ok());
748 }
749
750 #[test]
751 fn invalid_loop_count() {
752 let x = LoopOrJumpOrHaltRow {
753 loop_count: 101,
754 row_target: 1,
755 unused: std::array::from_fn(|_| 0),
756 };
757 assert!(x.validate().is_err());
758 assert_eq!(
759 x.validate().err().unwrap().to_string(),
760 OtToolsIoError::Arrangement(ArrangementError::LoopCount(101)).to_string(),
761 );
762 }
763 }
764}
765
766#[derive(
778 Copy,
779 Clone,
780 Debug,
781 Eq,
782 Hash,
783 Ord,
784 PartialEq,
785 PartialOrd,
786 Serialize,
787 Deserialize,
788 AsRefDerive,
789 AsMutDerive,
790)]
791pub struct ReminderRow {
792 pub reminder: [u8; 15],
799 pub unused: [u8; 6],
805}
806
807impl TryFrom<String> for ReminderRow {
808 type Error = OtToolsIoError;
809 fn try_from(value: String) -> Result<Self, Self::Error> {
810 let string = value.clone();
811 let bytes = string
812 .as_bytes()
813 .as_array::<15>()
814 .ok_or(ArrangementError::ReminderStrLen(value))?;
815
816 if bytes.iter().any(|x| is_not_valid_ascii_byte(*x)) {
817 Err(ArrangementError::ReminderAscii(*bytes).into())
818 } else {
819 Ok(Self {
820 reminder: *bytes,
821 unused: Default::default(),
822 })
823 }
824 }
825}
826
827impl std::fmt::Display for ReminderRow {
829 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
830 write!(f, "{}", super::bytes_to_ascii_string(&self.reminder))
832 }
833}
834
835impl ReminderRow {
836 pub fn new(reminder: [u8; 15], unused: [u8; 6]) -> Self {
838 Self { reminder, unused }
839 }
840 pub fn new_without_unused(reminder: [u8; 15]) -> Self {
843 Self::new(reminder, Default::default())
844 }
845
846 pub fn new_from_string<S: AsRef<str>>(reminder: S) -> Result<Self, OtToolsIoError> {
875 Self::try_from(reminder.as_ref().to_string())
876 }
877
878 pub fn validate(&self) -> Result<(), OtToolsIoError> {
881 Ok(())
882 }
883}
884
885#[cfg(test)]
886mod reminder_row {
887
888 const HELLO_WORLD_BYTES: [u8; 15] =
890 [72, 69, 76, 76, 79, 32, 87, 79, 82, 76, 68, 32, 32, 32, 32];
891 const ONLY_SPACES_BYTES: [u8; 15] =
893 [32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32];
894
895 const INVALID_BYTES: [u8; 15] = [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9];
896
897 mod new_without_unused {
898 use crate::arrangements::ReminderRow;
899 #[test]
900 fn ok_hello_world() {
901 let rem_row = ReminderRow::new_without_unused(super::HELLO_WORLD_BYTES);
902 assert_eq!(super::HELLO_WORLD_BYTES, rem_row.reminder);
903 assert_eq!(std::array::from_fn(|_| 0), rem_row.unused);
904 }
905
906 #[test]
907 fn ok_only_spaces() {
908 let rem_row = ReminderRow::new_without_unused(super::ONLY_SPACES_BYTES);
909 assert_eq!(super::ONLY_SPACES_BYTES, rem_row.reminder);
910 assert_eq!(std::array::from_fn(|_| 0), rem_row.unused);
911 }
912
913 #[test]
914 fn ok_invalid() {
915 let rem_row = ReminderRow::new_without_unused(super::INVALID_BYTES);
916 assert_eq!(super::INVALID_BYTES, rem_row.reminder);
917 assert_eq!(std::array::from_fn(|_| 0), rem_row.unused);
918 }
919 }
920
921 mod new_from_string {
922 use crate::arrangements::ReminderRow;
923 use crate::OtToolsIoError;
924
925 #[test]
926 fn ok_hello_world() -> Result<(), OtToolsIoError> {
927 let rem = String::from("HELLO WORLD ");
928 let rem_row = ReminderRow::new_from_string(rem)?;
929 assert_eq!(super::HELLO_WORLD_BYTES, rem_row.reminder);
930 assert_eq!(std::array::from_fn(|_| 0), rem_row.unused);
931 Ok(())
932 }
933 #[test]
934 fn ok_only_spaces() -> Result<(), OtToolsIoError> {
935 let rem = String::from(" ");
936 let rem_row = ReminderRow::new_from_string(rem)?;
937 assert_eq!(super::ONLY_SPACES_BYTES, rem_row.reminder);
938 assert_eq!(std::array::from_fn(|_| 0), rem_row.unused);
939 Ok(())
940 }
941 }
942
943 mod validate {
944 use crate::arrangements::ReminderRow;
945 #[test]
946 fn valid_string() {
947 let x = ReminderRow {
948 reminder: super::HELLO_WORLD_BYTES,
949 unused: [0, 0, 0, 0, 0, 0],
950 };
951 assert!(x.validate().is_ok())
952 }
953
954 #[test]
955 fn empty_string() {
956 let x = ReminderRow {
957 reminder: super::ONLY_SPACES_BYTES,
958 unused: [0, 0, 0, 0, 0, 0],
959 };
960 assert!(x.validate().is_ok())
961 }
962
963 #[test]
964 fn invalid_ascii() {
965 let x = ReminderRow {
966 reminder: super::INVALID_BYTES,
967 unused: [0, 0, 0, 0, 0, 0],
968 };
969 assert!(x.validate().is_ok())
970 }
971 }
972
973 mod display {
974 use crate::arrangements::ReminderRow;
975
976 #[test]
977 fn hello_world() {
978 let x = ReminderRow {
979 reminder: super::HELLO_WORLD_BYTES,
980 unused: [0, 0, 0, 0, 0, 0],
981 };
982 assert_eq!(format!["{x}"], "HELLO WORLD ");
983 }
984
985 #[test]
986 fn empty_string() {
987 let x = ReminderRow {
988 reminder: super::ONLY_SPACES_BYTES,
989 unused: [0, 0, 0, 0, 0, 0],
990 };
991 assert_eq!(format!["{x}"], " ");
992 }
993
994 #[test]
995 fn invalid_ascii() {
996 let x = ReminderRow {
997 reminder: super::INVALID_BYTES,
998 unused: [0, 0, 0, 0, 0, 0],
999 };
1000 assert_eq!(format!["{x}"], "");
1001 }
1002 }
1003}
1004
1005#[cfg(test)]
1006mod parse_to_bytes {
1007 mod pattern {
1008 use crate::arrangements::{ArrangeRow, PatternRow};
1009
1010 #[test]
1011 fn valid() {
1012 let x = ArrangeRow::PatternRow(PatternRow {
1013 pattern_id: 0,
1014 repetitions: 0,
1015 unused_1: 0,
1016 mute_mask: 0,
1017 unused_2: 0,
1018 tempo_1: 0,
1019 tempo_2: 0,
1020 scene_a: 0,
1021 scene_b: 0,
1022 unused_3: 0,
1023 offset: 0,
1024 unused_4: 0,
1025 length: 0,
1026 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
1027 });
1028 let r = x.parse_to_bytes();
1029 assert!(r.is_ok());
1030 assert_eq!(r.unwrap().len(), 22);
1031 }
1032 }
1033 mod loop_or_jump_or_halt {
1034 use crate::arrangements::{ArrangeRow, LoopOrJumpOrHaltRow};
1035 #[test]
1036 fn valid() {
1037 let x = ArrangeRow::LoopOrJumpOrHaltRow(LoopOrJumpOrHaltRow {
1038 loop_count: 1,
1039 row_target: 1,
1040 unused: std::array::from_fn(|_| 0),
1041 });
1042 let r = x.parse_to_bytes();
1043 assert!(r.is_ok());
1044 }
1045 }
1046
1047 mod reminder {
1048 use crate::arrangements::{ArrangeRow, ReminderRow};
1049
1050 #[test]
1051 fn valid_string() {
1052 let x = ArrangeRow::ReminderRow(ReminderRow {
1053 reminder: [72, 69, 76, 76, 79, 32, 87, 79, 82, 76, 68, 20, 20, 20, 20],
1055 unused: [0, 0, 0, 0, 0, 0],
1056 });
1057 let r = x.parse_to_bytes();
1058 println!("{r:?}");
1059 assert!(r.is_ok());
1060 println!("{r:?}");
1061 assert_eq!(r.unwrap().len(), 22);
1062 }
1063
1064 #[test]
1065 fn empty_string() {
1066 let x = ArrangeRow::ReminderRow(ReminderRow {
1067 reminder: [20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20],
1069 unused: [0, 0, 0, 0, 0, 0],
1070 });
1071 let r = x.parse_to_bytes();
1072 println!("{r:?}");
1073 assert!(r.is_ok());
1074 assert_eq!(r.unwrap().len(), 22);
1075 }
1076 }
1077 mod empty_row {
1078 use crate::arrangements::ArrangeRow;
1079
1080 #[test]
1081 fn valid() {
1082 let x = ArrangeRow::default();
1083 let r = x.parse_to_bytes();
1084 println!("{r:?}");
1085 assert!(r.is_ok());
1086 assert_eq!(r.unwrap().len(), 22);
1087 }
1088 }
1089}
1090
1091#[cfg(test)]
1092mod parse_from_bytes {
1093 use crate::arrangements::ArrangeRow;
1094
1095 #[test]
1096 fn invalid_row_type() {
1097 let b = [
1098 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1099 ];
1100 let r = ArrangeRow::parse_from_bytes(&b);
1101 assert!(r.is_err());
1102 }
1103
1104 mod empty_row {
1105 use crate::arrangements::{ArrangeRow, PatternRow};
1115
1116 #[test]
1117 fn row_index_matters() {
1118 let not_expected = ArrangeRow::default();
1119 let expected = ArrangeRow::PatternRow(PatternRow {
1120 pattern_id: 0,
1121 repetitions: 0,
1122 unused_1: 0,
1123 mute_mask: 0,
1124 unused_2: 0,
1125 tempo_1: 0,
1126 tempo_2: 0,
1127 scene_a: 0,
1128 scene_b: 0,
1129 unused_3: 0,
1130 offset: 0,
1131 unused_4: 0,
1132 length: 0,
1133 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
1134 });
1135
1136 let b = [
1137 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1138 ];
1139 let r = ArrangeRow::parse_from_bytes(&b);
1140 println!("{r:?}");
1141 assert!(r.is_ok());
1142 let x = r.unwrap();
1143 assert_ne!(not_expected, x);
1144 assert_eq!(expected, x);
1145 }
1146 }
1147
1148 mod reminder_row {
1149 use crate::arrangements::{ArrangeRow, ReminderRow};
1150
1151 #[test]
1152 fn valid() {
1153 let expected = ArrangeRow::ReminderRow(ReminderRow {
1154 reminder: [43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43],
1156 unused: [0, 0, 0, 0, 0, 0],
1157 });
1158 let b = [
1159 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 0, 0, 0, 0, 0, 0,
1160 ];
1161 let r = ArrangeRow::parse_from_bytes(&b);
1162 assert_eq!(expected, r.unwrap());
1163 }
1164
1165 #[test]
1166 fn valid_drop_excess_characters() {
1167 let expected = ArrangeRow::ReminderRow(ReminderRow {
1168 reminder: [43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43],
1170 unused: [0, 0, 0, 0, 0, 0],
1171 });
1172 let b = [
1173 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 0, 0, 0, 0, 0, 0,
1174 ];
1175 let r = ArrangeRow::parse_from_bytes(&b);
1176 assert_eq!(expected, r.unwrap());
1177 }
1178 }
1179
1180 mod loop_jump_or_halt_row {
1181 use crate::arrangements::{ArrangeRow, LoopOrJumpOrHaltRow};
1182
1183 #[test]
1184 fn valid() {
1185 let expected = ArrangeRow::LoopOrJumpOrHaltRow(LoopOrJumpOrHaltRow {
1186 loop_count: 1,
1187 row_target: 1,
1188 unused: std::array::from_fn(|_| 0),
1189 });
1190 let b = [
1191 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1192 ];
1193 let r = ArrangeRow::parse_from_bytes(&b);
1194 assert_eq!(expected, r.unwrap());
1195 }
1196
1197 #[test]
1198 fn invalid_loop_count() {
1199 let b = [
1200 1, 101, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1201 ];
1202 let r = ArrangeRow::parse_from_bytes(&b);
1203 assert!(r.is_err());
1204 }
1205 }
1206
1207 mod pattern_row {
1208 use crate::arrangements::{ArrangeRow, PatternRow};
1209
1210 #[test]
1211 fn valid_pattern_id_only() {
1212 let expected = ArrangeRow::PatternRow(PatternRow {
1213 pattern_id: 1,
1214 repetitions: 0,
1215 unused_1: 0,
1216 mute_mask: 0,
1217 unused_2: 0,
1218 tempo_1: 0,
1219 tempo_2: 0,
1220 scene_a: 0,
1221 scene_b: 0,
1222 unused_3: 0,
1223 offset: 0,
1224 unused_4: 0,
1225 length: 0,
1226 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
1227 });
1228 let b = [
1229 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1230 ];
1231 let r = ArrangeRow::parse_from_bytes(&b);
1232 assert_eq!(expected, r.unwrap());
1233 }
1234
1235 #[test]
1236 fn valid_last_midi_transpose_only() {
1237 let expected = ArrangeRow::PatternRow(PatternRow {
1238 pattern_id: 0,
1239 repetitions: 0,
1240 unused_1: 0,
1241 mute_mask: 0,
1242 unused_2: 0,
1243 tempo_1: 0,
1244 tempo_2: 0,
1245 scene_a: 0,
1246 scene_b: 0,
1247 unused_3: 0,
1248 offset: 0,
1249 unused_4: 0,
1250 length: 0,
1251 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 8],
1252 });
1253 let b = [
1254 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
1255 ];
1256 let r = ArrangeRow::parse_from_bytes(&b);
1257 assert_eq!(expected, r.unwrap());
1258 }
1259
1260 #[test]
1261 fn invalid_too_many_repetitions() {
1262 let b = [
1263 0, 1, 64, 1, 1, 1, 1, 1, 15, 15, 1, 1, 1, 64, 3, 3, 3, 3, 3, 3, 3, 3,
1264 ];
1265 let r = ArrangeRow::parse_from_bytes(&b);
1266 assert!(r.is_err());
1267 }
1268
1269 #[test]
1270 fn valid_all() {
1271 let expected = ArrangeRow::PatternRow(PatternRow {
1272 pattern_id: 1,
1273 repetitions: 10,
1274 unused_1: 55,
1275 mute_mask: 1,
1276 unused_2: 55,
1277 tempo_1: 1,
1278 tempo_2: 1,
1279 scene_a: 15,
1280 scene_b: 15,
1281 unused_3: 55,
1282 offset: 1,
1283 unused_4: 55,
1284 length: 64,
1285 midi_transpose: [3, 3, 3, 3, 3, 3, 3, 3],
1286 });
1287 let b = [
1288 0, 1, 10, 55, 1, 55, 1, 1, 15, 15, 55, 1, 55, 64, 3, 3, 3, 3, 3, 3, 3, 3,
1289 ];
1290 let r = ArrangeRow::parse_from_bytes(&b);
1291 assert_eq!(expected, r.unwrap());
1292 }
1293
1294 #[test]
1295 fn zero_valued_is_still_pattern_row() {
1296 let expected = ArrangeRow::PatternRow(PatternRow {
1297 pattern_id: 0,
1298 repetitions: 0,
1299 unused_1: 0,
1300 mute_mask: 0,
1301 unused_2: 0,
1302 tempo_1: 0,
1303 tempo_2: 0,
1304 scene_a: 0,
1305 scene_b: 0,
1306 unused_3: 0,
1307 offset: 0,
1308 unused_4: 0,
1309 length: 0,
1310 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
1311 });
1312 let b = [
1313 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1314 ];
1315 let r = ArrangeRow::parse_from_bytes(&b);
1316 assert_eq!(expected, r.unwrap());
1317 }
1318 }
1319}
1320
1321#[cfg(test)]
1322mod deserialize {
1323
1324 mod pattern {
1325 }
1327
1328 mod empty {
1329 use crate::arrangements::ArrangeRow;
1330
1331 #[test]
1332 fn valid_yaml() {
1333 let expected = ArrangeRow::default();
1334 let s = "!empty_row\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n";
1335 let r = serde_norway::from_str::<ArrangeRow>(s);
1336 println!("{r:?}");
1337 assert!(r.is_ok());
1338 assert_eq!(expected, r.unwrap());
1339 }
1340
1341 #[test]
1342 fn valid_json() {
1343 let expected = ArrangeRow::default();
1344 let s =
1345 "{\"empty_row\":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}";
1346 let r = serde_json::from_str::<ArrangeRow>(s);
1347 println!("{r:?}");
1348 assert!(r.is_ok());
1349 assert_eq!(expected, r.unwrap());
1350 }
1351 }
1352
1353 mod loop_jump_or_halt {
1354 use crate::arrangements::{ArrangeRow, LoopOrJumpOrHaltRow};
1355
1356 #[test]
1357 fn valid_yaml() {
1358 let expected = ArrangeRow::LoopOrJumpOrHaltRow(LoopOrJumpOrHaltRow {
1359 loop_count: 1,
1360 row_target: 1,
1361 unused: std::array::from_fn(|_| 0),
1362 });
1363
1364 let s = "!loop_or_jump_or_halt_row\nloop_count: 1\nrow_target: 1\nunused:\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n";
1365 let r = serde_norway::from_str::<ArrangeRow>(s);
1366 assert_eq!(expected, r.unwrap());
1367 }
1368
1369 #[test]
1370 fn valid_json() {
1371 let expected = ArrangeRow::LoopOrJumpOrHaltRow(LoopOrJumpOrHaltRow {
1372 loop_count: 1,
1373 row_target: 1,
1374 unused: std::array::from_fn(|_| 0),
1375 });
1376
1377 let s = "{\"loop_or_jump_or_halt_row\":{\"loop_count\": 1, \"row_target\": 1, \"unused\": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}}";
1378 let r = serde_json::from_str::<ArrangeRow>(s);
1379 assert_eq!(expected, r.unwrap());
1380 }
1381 }
1382
1383 mod reminder {
1384 use crate::arrangements::{ArrangeRow, ReminderRow};
1385
1386 #[test]
1387 fn valid_json() {
1388 let expected = ArrangeRow::ReminderRow(ReminderRow {
1389 reminder: [67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67],
1390 unused: [0, 0, 0, 0, 0, 0],
1391 });
1392
1393 let s = "{\"reminder_row\":{\"reminder\":[67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67], \"unused\": [0, 0, 0, 0, 0, 0]}}";
1394 let r = serde_json::from_str::<ArrangeRow>(s);
1395 assert_eq!(expected, r.unwrap());
1396 }
1397
1398 #[test]
1399 fn valid_yaml() {
1400 let expected = ArrangeRow::ReminderRow(ReminderRow {
1401 reminder: [67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67],
1402 unused: [0, 0, 0, 0, 0, 0],
1403 });
1404
1405 let s = "!reminder_row\nreminder:\n- 67\n- 67\n- 67\n- 67\n- 67\n- 67\n- 67\n- 67\n- 67\n- 67\n- 67\n- 67\n- 67\n- 67\n- 67\nunused:\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n";
1406 let r = serde_norway::from_str::<ArrangeRow>(s);
1407 assert_eq!(expected, r.unwrap());
1408 }
1409 }
1410}
1411
1412#[cfg(test)]
1413mod arrange_row {
1414 mod pattern {
1415 use crate::arrangements::{ArrangeRow, PatternRow};
1416
1417 #[test]
1418 fn valid_yaml() {
1419 let x = ArrangeRow::PatternRow(PatternRow {
1420 pattern_id: 0,
1421 repetitions: 0,
1422 unused_1: 0,
1423 mute_mask: 0,
1424 unused_2: 0,
1425 tempo_1: 0,
1426 tempo_2: 0,
1427 scene_a: 0,
1428 scene_b: 0,
1429 unused_3: 0,
1430 offset: 0,
1431 unused_4: 0,
1432 length: 0,
1433 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
1434 });
1435 let r = serde_norway::to_string(&x);
1436 println!("{r:?}");
1437 assert!(r.is_ok());
1438 assert_eq!(r.unwrap(), "!pattern_row\npattern_id: 0\nrepetitions: 0\nunused_1: 0\nmute_mask: 0\nunused_2: 0\ntempo_1: 0\ntempo_2: 0\nscene_a: 0\nscene_b: 0\nunused_3: 0\noffset: 0\nunused_4: 0\nlength: 0\nmidi_transpose:\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n");
1439 }
1440
1441 #[test]
1442 fn valid_json() {
1443 let x = ArrangeRow::PatternRow(PatternRow {
1444 pattern_id: 0,
1445 repetitions: 0,
1446 unused_1: 0,
1447 mute_mask: 0,
1448 unused_2: 0,
1449 tempo_1: 0,
1450 tempo_2: 0,
1451 scene_a: 0,
1452 scene_b: 0,
1453 unused_3: 0,
1454 offset: 0,
1455 unused_4: 0,
1456 length: 0,
1457 midi_transpose: [0, 0, 0, 0, 0, 0, 0, 0],
1458 });
1459 let r = serde_json::to_string(&x);
1460 println!("{r:?}");
1461 assert!(r.is_ok());
1462 assert_eq!(r.unwrap(), "{\"pattern_row\":{\"pattern_id\":0,\"repetitions\":0,\"unused_1\":0,\"mute_mask\":0,\"unused_2\":0,\"tempo_1\":0,\"tempo_2\":0,\"scene_a\":0,\"scene_b\":0,\"unused_3\":0,\"offset\":0,\"unused_4\":0,\"length\":0,\"midi_transpose\":[0,0,0,0,0,0,0,0]}}");
1463 }
1464 }
1465
1466 mod loop_or_jump_or_halt {
1467 use crate::arrangements::{ArrangeRow, LoopOrJumpOrHaltRow};
1468 use std::array::from_fn;
1469
1470 #[test]
1471 fn valid_yaml() {
1472 let x = ArrangeRow::LoopOrJumpOrHaltRow(LoopOrJumpOrHaltRow {
1473 loop_count: 1,
1474 row_target: 1,
1475 unused: from_fn(|_| 0),
1476 });
1477 let r = serde_norway::to_string(&x);
1478 println!("{r:?}");
1479 assert!(r.is_ok());
1480 assert_eq!(r.unwrap(), "!loop_or_jump_or_halt_row\nloop_count: 1\nrow_target: 1\nunused:\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n");
1481 }
1482
1483 #[test]
1484 fn valid_json() {
1485 let x = ArrangeRow::LoopOrJumpOrHaltRow(LoopOrJumpOrHaltRow {
1486 loop_count: 1,
1487 row_target: 1,
1488 unused: from_fn(|_| 0),
1489 });
1490 let r = serde_json::to_string(&x);
1491 println!("{r:?}");
1492 assert!(r.is_ok());
1493 assert_eq!(r.unwrap(), "{\"loop_or_jump_or_halt_row\":{\"loop_count\":1,\"row_target\":1,\"unused\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}}");
1494 }
1495 }
1496
1497 mod reminder_row {
1498 use crate::arrangements::{ArrangeRow, ReminderRow};
1499
1500 #[test]
1501 fn valid_string() {
1502 let x = ArrangeRow::ReminderRow(ReminderRow {
1503 reminder: [72, 69, 76, 76, 79, 32, 87, 79, 82, 76, 68, 20, 20, 20, 20],
1505 unused: [0, 0, 0, 0, 0, 0],
1506 });
1507 let r = x.parse_to_bytes();
1508 println!("{r:?}");
1509 assert!(r.is_ok());
1510 println!("{r:?}");
1511 assert_eq!(r.unwrap().len(), 22);
1512 }
1513
1514 #[test]
1515 fn valid_string_yaml() {
1516 let x = ArrangeRow::ReminderRow(ReminderRow {
1517 reminder: [72, 69, 76, 76, 79, 32, 87, 79, 82, 76, 68, 20, 20, 20, 20],
1519 unused: [0, 0, 0, 0, 0, 0],
1520 });
1521 let r = serde_norway::to_string(&x);
1522 println!("{r:?}");
1523 assert!(r.is_ok());
1524 assert_eq!(r.unwrap(), "!reminder_row\nreminder:\n- 72\n- 69\n- 76\n- 76\n- 79\n- 32\n- 87\n- 79\n- 82\n- 76\n- 68\n- 20\n- 20\n- 20\n- 20\nunused:\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n");
1525 }
1526
1527 #[test]
1528 fn valid_string_json() {
1529 let x = ArrangeRow::ReminderRow(ReminderRow {
1530 reminder: [72, 69, 76, 76, 79, 32, 87, 79, 82, 76, 68, 20, 20, 20, 20],
1532 unused: [0, 0, 0, 0, 0, 0],
1533 });
1534 let r = serde_json::to_string(&x);
1535 println!("{r:?}");
1536 assert!(r.is_ok());
1537 assert_eq!(r.unwrap(), "{\"reminder_row\":{\"reminder\":[72,69,76,76,79,32,87,79,82,76,68,20,20,20,20],\"unused\":[0,0,0,0,0,0]}}");
1538 }
1539
1540 #[test]
1541 fn empty_string() {
1542 let x = ArrangeRow::ReminderRow(ReminderRow {
1543 reminder: [20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20],
1545 unused: [0, 0, 0, 0, 0, 0],
1546 });
1547 let r = x.parse_to_bytes();
1548 println!("{r:?}");
1549 assert!(r.is_ok());
1550 assert_eq!(r.unwrap().len(), 22);
1551 }
1552
1553 #[test]
1554 fn empty_string_yaml() {
1555 let x = ArrangeRow::ReminderRow(ReminderRow {
1556 reminder: [20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20],
1558 unused: [0, 0, 0, 0, 0, 0],
1559 });
1560 let r = serde_norway::to_string(&x);
1561 println!("{r:?}");
1562 assert!(r.is_ok());
1563 assert_eq!(r.unwrap(), "!reminder_row\nreminder:\n- 20\n- 20\n- 20\n- 20\n- 20\n- 20\n- 20\n- 20\n- 20\n- 20\n- 20\n- 20\n- 20\n- 20\n- 20\nunused:\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n");
1564 }
1565
1566 #[test]
1567 fn empty_string_json() {
1568 let x = ArrangeRow::ReminderRow(ReminderRow {
1569 reminder: [20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20],
1571 unused: [0, 0, 0, 0, 0, 0],
1572 });
1573 let r = serde_json::to_string(&x);
1574 println!("{r:?}");
1575 assert!(r.is_ok());
1576 assert_eq!(r.unwrap(), "{\"reminder_row\":{\"reminder\":[20,20,20,20,20,20,20,20,20,20,20,20,20,20,20],\"unused\":[0,0,0,0,0,0]}}");
1577 }
1578
1579 }
1590
1591 mod empty_row {
1592 use crate::arrangements::ArrangeRow;
1593
1594 #[test]
1595 fn valid_yaml() {
1596 let x = ArrangeRow::default();
1597 let r = serde_norway::to_string(&x);
1598 println!("{r:?}");
1599 assert!(r.is_ok());
1600 assert_eq!(r.unwrap(), "!empty_row\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n- 0\n");
1601 }
1602
1603 #[test]
1604 fn valid_json() {
1605 let x = ArrangeRow::default();
1606 let r = serde_json::to_string(&x);
1607 println!("{r:?}");
1608 assert!(r.is_ok());
1609 assert_eq!(
1610 r.unwrap(),
1611 "{\"empty_row\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}"
1612 );
1613 }
1614 }
1615}