1use crate::fields::Library;
12use crate::formats::{
13 nc2, nc2d, nd2, nd3, ne3, ne4, ne5, ne6, ne7, ng2, nl4, nla1, no3, np, np2, np3, np4, np5,
14 npip, npno, ns2, ns3, ns4, nsclassic, nsmp, nw, nw2,
15};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum Family {
29 Electro3,
30 Electro4,
31 Electro5,
32 Electro6,
33 Electro7,
34 StageClassic,
35 Stage2,
36 Stage3,
37 Stage4,
38 Piano,
39 Piano2,
40 Piano3,
41 Piano4,
42 Piano5,
43 Grand,
44 Wave,
45 Wave2,
46 C2,
47 C2D,
48 Organ3,
49 Lead4,
50 LeadA1,
51 Drum2,
52 Drum3,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
62pub enum Slot {
63 Piano,
64 Sample,
65 Program,
66 SetList,
67 Live,
69 Settings,
71}
72
73impl From<Library> for Slot {
74 fn from(library: Library) -> Slot {
75 match library {
76 Library::Piano => Slot::Piano,
77 Library::Sample => Slot::Sample,
78 Library::Program => Slot::Program,
79 Library::SetList => Slot::SetList,
80 }
81 }
82}
83
84impl Slot {
85 pub const ALL: [Slot; 6] = [
86 Slot::Piano,
87 Slot::Sample,
88 Slot::Program,
89 Slot::SetList,
90 Slot::Live,
91 Slot::Settings,
92 ];
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum Acceptance {
98 Confirmed,
100 Inferred,
103 Refused,
106 Unknown,
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113enum Evidence {
114 Hardware,
116 Specimens,
118}
119
120impl Evidence {
121 fn acceptance(self) -> Acceptance {
122 match self {
123 Evidence::Hardware => Acceptance::Confirmed,
124 Evidence::Specimens => Acceptance::Inferred,
125 }
126 }
127}
128
129const TAKES: &[(Family, Slot, &str, Evidence)] = &[
137 (
138 Family::Electro3,
139 Slot::Program,
140 ne3::program::FORMAT,
141 Evidence::Specimens,
142 ),
143 (
144 Family::Electro4,
145 Slot::Program,
146 ne4::program::FORMAT,
147 Evidence::Specimens,
148 ),
149 (
150 Family::Electro4,
151 Slot::Live,
152 ne4::live::FORMAT,
153 Evidence::Specimens,
154 ),
155 (
156 Family::Electro4,
157 Slot::Settings,
158 ne4::settings::FORMAT,
159 Evidence::Specimens,
160 ),
161 (
162 Family::Electro5,
163 Slot::Program,
164 ne5::program::FORMAT,
165 Evidence::Hardware,
166 ),
167 (
168 Family::Electro5,
169 Slot::SetList,
170 ne5::song::FORMAT,
171 Evidence::Hardware,
172 ),
173 (
174 Family::Electro5,
175 Slot::Live,
176 ne5::live::FORMAT,
177 Evidence::Hardware,
178 ),
179 (
180 Family::Electro5,
181 Slot::Settings,
182 ne5::settings::FORMAT,
183 Evidence::Hardware,
184 ),
185 (
186 Family::Electro5,
187 Slot::Sample,
188 nsmp::FORMAT,
189 Evidence::Hardware,
190 ),
191 (
192 Family::Electro5,
193 Slot::Piano,
194 npno::FORMAT,
195 Evidence::Hardware,
196 ),
197 (
198 Family::Electro6,
199 Slot::Program,
200 ne6::program::FORMAT,
201 Evidence::Specimens,
202 ),
203 (
204 Family::Electro6,
205 Slot::Live,
206 ne6::live::FORMAT,
207 Evidence::Specimens,
208 ),
209 (
210 Family::Electro6,
211 Slot::Settings,
212 ne6::settings::FORMAT,
213 Evidence::Specimens,
214 ),
215 (
216 Family::Electro7,
217 Slot::Program,
218 ne7::program::FORMAT,
219 Evidence::Specimens,
220 ),
221 (
222 Family::Electro7,
223 Slot::Live,
224 ne7::live::FORMAT,
225 Evidence::Specimens,
226 ),
227 (
228 Family::Electro7,
229 Slot::Settings,
230 ne7::settings::FORMAT,
231 Evidence::Specimens,
232 ),
233 (
234 Family::StageClassic,
235 Slot::Program,
236 nsclassic::program::FORMAT,
237 Evidence::Specimens,
238 ),
239 (
240 Family::StageClassic,
241 Slot::Piano,
242 nsclassic::piano_library::FORMAT,
243 Evidence::Specimens,
244 ),
245 (
246 Family::Stage2,
247 Slot::Program,
248 ns2::program::FORMAT,
249 Evidence::Specimens,
250 ),
251 (
252 Family::Stage2,
253 Slot::Live,
254 ns2::live::FORMAT,
255 Evidence::Specimens,
256 ),
257 (
258 Family::Stage2,
259 Slot::Settings,
260 ns2::settings::FORMAT,
261 Evidence::Specimens,
262 ),
263 (
264 Family::Stage3,
265 Slot::Program,
266 ns3::program::FORMAT,
267 Evidence::Specimens,
268 ),
269 (
270 Family::Stage3,
271 Slot::Live,
272 ns3::live::FORMAT,
273 Evidence::Specimens,
274 ),
275 (
276 Family::Stage3,
277 Slot::SetList,
278 ns3::song::FORMAT,
279 Evidence::Specimens,
280 ),
281 (
282 Family::Stage3,
283 Slot::Settings,
284 ns3::settings::FORMAT,
285 Evidence::Specimens,
286 ),
287 (
288 Family::Stage4,
289 Slot::Program,
290 ns4::program::FORMAT,
291 Evidence::Specimens,
292 ),
293 (
294 Family::Stage4,
295 Slot::Live,
296 ns4::live::FORMAT,
297 Evidence::Specimens,
298 ),
299 (
300 Family::Stage4,
301 Slot::Settings,
302 ns4::settings::FORMAT,
303 Evidence::Specimens,
304 ),
305 (
306 Family::Piano,
307 Slot::Program,
308 np::program::FORMAT,
309 Evidence::Specimens,
310 ),
311 (
312 Family::Piano,
313 Slot::Live,
314 np::live::FORMAT,
315 Evidence::Specimens,
316 ),
317 (
318 Family::Piano,
319 Slot::Settings,
320 np::settings::FORMAT,
321 Evidence::Specimens,
322 ),
323 (
324 Family::Piano2,
325 Slot::Program,
326 np2::program::FORMAT,
327 Evidence::Specimens,
328 ),
329 (
330 Family::Piano2,
331 Slot::Live,
332 np2::live::FORMAT,
333 Evidence::Specimens,
334 ),
335 (
336 Family::Piano2,
337 Slot::Settings,
338 np2::settings::FORMAT,
339 Evidence::Specimens,
340 ),
341 (
342 Family::Piano3,
343 Slot::Program,
344 np3::program::FORMAT,
345 Evidence::Specimens,
346 ),
347 (
348 Family::Piano3,
349 Slot::Live,
350 np3::live::FORMAT,
351 Evidence::Specimens,
352 ),
353 (
354 Family::Piano3,
355 Slot::Settings,
356 np3::settings::FORMAT,
357 Evidence::Specimens,
358 ),
359 (
360 Family::Piano4,
361 Slot::Program,
362 np4::program::FORMAT,
363 Evidence::Specimens,
364 ),
365 (
366 Family::Piano4,
367 Slot::Live,
368 np4::live::FORMAT,
369 Evidence::Specimens,
370 ),
371 (
372 Family::Piano4,
373 Slot::Settings,
374 np4::settings::FORMAT,
375 Evidence::Specimens,
376 ),
377 (
378 Family::Piano5,
379 Slot::Program,
380 np5::program::FORMAT,
381 Evidence::Specimens,
382 ),
383 (
384 Family::Piano5,
385 Slot::Live,
386 np5::live::FORMAT,
387 Evidence::Specimens,
388 ),
389 (
390 Family::Piano5,
391 Slot::Settings,
392 np5::settings::FORMAT,
393 Evidence::Specimens,
394 ),
395 (
396 Family::Grand,
397 Slot::Program,
398 ng2::program::FORMAT,
399 Evidence::Specimens,
400 ),
401 (
402 Family::Grand,
403 Slot::Live,
404 ng2::live::FORMAT,
405 Evidence::Specimens,
406 ),
407 (
408 Family::Grand,
409 Slot::Settings,
410 ng2::settings::FORMAT,
411 Evidence::Specimens,
412 ),
413 (
414 Family::Wave,
415 Slot::Program,
416 nw::program::FORMAT,
417 Evidence::Specimens,
418 ),
419 (
420 Family::Wave,
421 Slot::Settings,
422 nw::settings::FORMAT,
423 Evidence::Specimens,
424 ),
425 (
426 Family::Wave2,
427 Slot::Program,
428 nw2::program::FORMAT,
429 Evidence::Specimens,
430 ),
431 (
432 Family::Wave2,
433 Slot::Live,
434 nw2::live::FORMAT,
435 Evidence::Specimens,
436 ),
437 (
438 Family::Wave2,
439 Slot::Settings,
440 nw2::settings::FORMAT,
441 Evidence::Specimens,
442 ),
443 (
444 Family::C2,
445 Slot::Program,
446 nc2::program::FORMAT,
447 Evidence::Specimens,
448 ),
449 (
450 Family::C2,
451 Slot::Settings,
452 nc2::settings::FORMAT,
453 Evidence::Specimens,
454 ),
455 (
456 Family::C2D,
457 Slot::Program,
458 nc2d::program::FORMAT,
459 Evidence::Specimens,
460 ),
461 (
462 Family::C2D,
463 Slot::Settings,
464 nc2d::settings::FORMAT,
465 Evidence::Specimens,
466 ),
467 (
468 Family::Organ3,
469 Slot::Program,
470 no3::program::FORMAT,
471 Evidence::Specimens,
472 ),
473 (
474 Family::Organ3,
475 Slot::Settings,
476 no3::settings::FORMAT,
477 Evidence::Specimens,
478 ),
479 (
480 Family::Lead4,
481 Slot::Program,
482 nl4::program::FORMAT,
483 Evidence::Specimens,
484 ),
485 (
486 Family::Lead4,
487 Slot::Settings,
488 nl4::settings::FORMAT,
489 Evidence::Specimens,
490 ),
491 (
492 Family::LeadA1,
493 Slot::Program,
494 nla1::program::FORMAT,
495 Evidence::Specimens,
496 ),
497 (
498 Family::LeadA1,
499 Slot::Settings,
500 nla1::settings::FORMAT,
501 Evidence::Specimens,
502 ),
503 (
504 Family::Drum2,
505 Slot::Program,
506 nd2::program::FORMAT,
507 Evidence::Specimens,
508 ),
509 (
510 Family::Drum3,
511 Slot::Program,
512 nd3::kit::FORMAT,
513 Evidence::Specimens,
514 ),
515];
516
517const CARRIES: &[(&str, Family)] = &[
524 (ne3::program::FORMAT, Family::Electro3),
525 (ne3::organ_preset::FORMAT, Family::Electro3),
526 (ne4::program::FORMAT, Family::Electro4),
527 (ne4::live::FORMAT, Family::Electro4),
528 (ne4::settings::FORMAT, Family::Electro4),
529 (ne5::program::FORMAT, Family::Electro5),
530 (ne5::live::FORMAT, Family::Electro5),
531 (ne5::song::FORMAT, Family::Electro5),
532 (ne5::settings::FORMAT, Family::Electro5),
533 (ne6::program::FORMAT, Family::Electro6),
534 (ne6::live::FORMAT, Family::Electro6),
535 (ne6::settings::FORMAT, Family::Electro6),
536 (ne7::program::FORMAT, Family::Electro7),
537 (ne7::live::FORMAT, Family::Electro7),
538 (ne7::settings::FORMAT, Family::Electro7),
539 (nsclassic::program::FORMAT, Family::StageClassic),
540 (nsclassic::synth::FORMAT, Family::StageClassic),
541 (nsclassic::piano_library::FORMAT, Family::StageClassic),
542 (ns2::program::FORMAT, Family::Stage2),
543 (ns2::live::FORMAT, Family::Stage2),
544 (ns2::synth::FORMAT, Family::Stage2),
545 (ns2::settings::FORMAT, Family::Stage2),
546 (ns3::program::FORMAT, Family::Stage3),
547 (ns3::live::FORMAT, Family::Stage3),
548 (ns3::song::FORMAT, Family::Stage3),
549 (ns3::synth::FORMAT, Family::Stage3),
550 (ns3::settings::FORMAT, Family::Stage3),
551 (ns4::program::FORMAT, Family::Stage4),
552 (ns4::live::FORMAT, Family::Stage4),
553 (ns4::synth::FORMAT, Family::Stage4),
554 (ns4::piano_preset::FORMAT, Family::Stage4),
555 (ns4::organ_preset::FORMAT, Family::Stage4),
556 (ns4::settings::FORMAT, Family::Stage4),
557 (np::program::FORMAT, Family::Piano),
558 (np::live::FORMAT, Family::Piano),
559 (np::settings::FORMAT, Family::Piano),
560 (np2::program::FORMAT, Family::Piano2),
561 (np2::live::FORMAT, Family::Piano2),
562 (np2::settings::FORMAT, Family::Piano2),
563 (np3::program::FORMAT, Family::Piano3),
564 (np3::live::FORMAT, Family::Piano3),
565 (np3::settings::FORMAT, Family::Piano3),
566 (np4::program::FORMAT, Family::Piano4),
567 (np4::live::FORMAT, Family::Piano4),
568 (np4::settings::FORMAT, Family::Piano4),
569 (np5::program::FORMAT, Family::Piano5),
570 (np5::live::FORMAT, Family::Piano5),
571 (np5::settings::FORMAT, Family::Piano5),
572 (ng2::program::FORMAT, Family::Grand),
573 (ng2::live::FORMAT, Family::Grand),
574 (ng2::settings::FORMAT, Family::Grand),
575 (nw::program::FORMAT, Family::Wave),
576 (nw::settings::FORMAT, Family::Wave),
577 (nw2::program::FORMAT, Family::Wave2),
578 (nw2::live::FORMAT, Family::Wave2),
579 (nw2::settings::FORMAT, Family::Wave2),
580 (nc2::program::FORMAT, Family::C2),
581 (nc2::settings::FORMAT, Family::C2),
582 (npip::pipe_library::FORMAT, Family::C2),
583 (nc2d::program::FORMAT, Family::C2D),
584 (nc2d::settings::FORMAT, Family::C2D),
585 (no3::program::FORMAT, Family::Organ3),
586 (no3::settings::FORMAT, Family::Organ3),
587 (nl4::program::FORMAT, Family::Lead4),
588 (nl4::performance::FORMAT, Family::Lead4),
589 (nl4::settings::FORMAT, Family::Lead4),
590 (nla1::program::FORMAT, Family::LeadA1),
591 (nla1::performance::FORMAT, Family::LeadA1),
592 (nla1::settings::FORMAT, Family::LeadA1),
593 (nd2::program::FORMAT, Family::Drum2),
594 (nd3::kit::FORMAT, Family::Drum3),
595];
596
597const _: () = {
601 let mut i = 0;
602 while i < TAKES.len() {
603 assert!(TAKES[i].2.len() == 4, "a CBIN tag is four bytes");
604 i += 1;
605 }
606 let mut i = 0;
607 while i < CARRIES.len() {
608 assert!(CARRIES[i].0.len() == 4, "a CBIN tag is four bytes");
609 i += 1;
610 }
611};
612
613impl Family {
614 pub const ALL: [Family; 24] = [
615 Family::Electro3,
616 Family::Electro4,
617 Family::Electro5,
618 Family::Electro6,
619 Family::Electro7,
620 Family::StageClassic,
621 Family::Stage2,
622 Family::Stage3,
623 Family::Stage4,
624 Family::Piano,
625 Family::Piano2,
626 Family::Piano3,
627 Family::Piano4,
628 Family::Piano5,
629 Family::Grand,
630 Family::Wave,
631 Family::Wave2,
632 Family::C2,
633 Family::C2D,
634 Family::Organ3,
635 Family::Lead4,
636 Family::LeadA1,
637 Family::Drum2,
638 Family::Drum3,
639 ];
640
641 pub fn label(self) -> &'static str {
643 match self {
644 Family::Electro3 => "Electro 3",
645 Family::Electro4 => "Electro 4",
646 Family::Electro5 => "Electro 5",
647 Family::Electro6 => "Electro 6",
648 Family::Electro7 => "Electro 7",
649 Family::StageClassic => "Stage Classic",
650 Family::Stage2 => "Stage 2",
651 Family::Stage3 => "Stage 3",
652 Family::Stage4 => "Stage 4",
653 Family::Piano => "Piano",
654 Family::Piano2 => "Piano 2",
655 Family::Piano3 => "Piano 3",
656 Family::Piano4 => "Piano 4",
657 Family::Piano5 => "Piano 5",
658 Family::Grand => "Grand",
659 Family::Wave => "Wave",
660 Family::Wave2 => "Wave 2",
661 Family::C2 => "C2",
662 Family::C2D => "C2D",
663 Family::Organ3 => "no3 organ",
664 Family::Lead4 => "Lead 4",
665 Family::LeadA1 => "Lead A1",
666 Family::Drum2 => "Drum 2",
667 Family::Drum3 => "Drum 3P",
668 }
669 }
670
671 fn product_name(self) -> Option<&'static str> {
679 match self {
680 Family::StageClassic => Some("Stage"),
681 Family::Organ3 => None,
682 named => Some(named.label()),
683 }
684 }
685
686 pub fn from_product(product: &str) -> Option<Family> {
697 Family::ALL
698 .into_iter()
699 .filter_map(|family| Some((family, family.product_name()?)))
700 .filter(|(_, name)| product.contains(name))
701 .max_by_key(|(_, name)| name.len())
702 .map(|(family, _)| family)
703 }
704
705 pub fn of_tag(tag: &str) -> Option<Family> {
708 CARRIES
709 .iter()
710 .find(|(held, _)| *held == tag)
711 .map(|(_, family)| *family)
712 }
713
714 pub fn accepts(self, slot: Slot, tag: &str) -> Acceptance {
716 if let Some((_, _, _, evidence)) = TAKES
717 .iter()
718 .find(|(family, held, format, _)| (*family, *held, *format) == (self, slot, tag))
719 {
720 return evidence.acceptance();
721 }
722 match Family::of_tag(tag) {
726 Some(_) => Acceptance::Refused,
727 None => Acceptance::Unknown,
728 }
729 }
730}
731
732#[cfg(test)]
733mod tests {
734 use super::*;
735
736 #[test]
738 fn an_electro_5_takes_its_own_program_and_refuses_a_stage_4s() {
739 let e5 = Family::Electro5;
740 assert_eq!(
741 e5.accepts(Slot::Program, ne5::program::FORMAT),
742 Acceptance::Confirmed
743 );
744 assert_eq!(
745 e5.accepts(Slot::Program, ns4::program::FORMAT),
746 Acceptance::Refused
747 );
748 assert_eq!(
749 Family::Stage4.accepts(Slot::Program, ns4::program::FORMAT),
750 Acceptance::Inferred
751 );
752 assert_eq!(e5.accepts(Slot::Program, "zzzz"), Acceptance::Unknown);
753 }
754
755 #[test]
759 fn a_familys_own_tag_is_refused_outside_its_class() {
760 assert_eq!(
761 Family::Stage4.accepts(Slot::Piano, ns4::program::FORMAT),
762 Acceptance::Refused
763 );
764 for (tag, owner) in CARRIES {
765 let open: Vec<Slot> = Slot::ALL
766 .into_iter()
767 .filter(|slot| owner.accepts(*slot, tag) != Acceptance::Refused)
768 .collect();
769 assert!(
770 open.len() <= 1,
771 "{} leaves its own {tag} unrefused in {open:?}",
772 owner.label(),
773 );
774 }
775 }
776
777 #[test]
780 fn a_shared_library_format_refuses_nobody() {
781 assert_eq!(Family::of_tag(nsmp::FORMAT), None);
782 assert_eq!(Family::of_tag(npno::FORMAT), None);
783 assert_eq!(
784 Family::Electro5.accepts(Slot::Sample, nsmp::FORMAT),
785 Acceptance::Confirmed
786 );
787 assert_eq!(
788 Family::Stage4.accepts(Slot::Sample, nsmp::FORMAT),
789 Acceptance::Unknown
790 );
791 }
792
793 #[test]
797 fn every_tag_a_family_takes_lands_in_one_class() {
798 for family in Family::ALL {
799 let mine: Vec<&str> = TAKES
800 .iter()
801 .filter(|(held, _, _, _)| *held == family)
802 .map(|(_, _, tag, _)| *tag)
803 .collect();
804 for tag in &mine {
805 assert_eq!(
806 mine.iter().filter(|held| *held == tag).count(),
807 1,
808 "{} lists {tag} in more than one class",
809 family.label()
810 );
811 assert!(
812 Family::of_tag(tag).is_none_or(|held| held == family),
813 "{} takes {tag}, which is another family's",
814 family.label()
815 );
816 }
817 }
818 }
819
820 #[test]
822 fn a_foreign_tag_is_refused_in_every_class() {
823 for family in Family::ALL {
824 for (tag, owner) in CARRIES.iter().filter(|(_, owner)| *owner != family) {
825 for slot in Slot::ALL {
826 assert_eq!(
827 family.accepts(slot, tag),
828 Acceptance::Refused,
829 "{} should refuse {}'s {tag}",
830 family.label(),
831 owner.label()
832 );
833 }
834 }
835 }
836 }
837
838 #[test]
841 fn the_product_string_names_the_model_and_then_the_keybed() {
842 assert_eq!(
843 Family::from_product("Nord Electro 5"),
844 Some(Family::Electro5)
845 );
846 assert_eq!(
847 Family::from_product("Nord Electro 5D 73"),
848 Some(Family::Electro5)
849 );
850 assert_eq!(Family::from_product("Nord Piano 88"), Some(Family::Piano));
851 assert_eq!(
852 Family::from_product("Nord Piano 5 73"),
853 Some(Family::Piano5)
854 );
855 assert_eq!(Family::from_product("Nord C2D"), Some(Family::C2D));
856 assert_eq!(Family::from_product("Nord Wave 2"), Some(Family::Wave2));
857 assert_eq!(
859 Family::from_product("Nord Stage 88"),
860 Some(Family::StageClassic)
861 );
862 assert_eq!(
863 Family::from_product("Nord Stage EX 76"),
864 Some(Family::StageClassic)
865 );
866 assert_eq!(
867 Family::from_product("Nord Stage 3 88"),
868 Some(Family::Stage3)
869 );
870 assert_eq!(Family::from_product("Nord no3 organ"), None);
872 assert_eq!(Family::from_product("Some other keyboard"), None);
873 }
874
875 #[test]
878 fn no_two_families_share_a_name() {
879 for family in Family::ALL {
880 assert_eq!(
881 Family::ALL
882 .iter()
883 .filter(|held| held.label() == family.label())
884 .count(),
885 1,
886 "{} is not a unique name",
887 family.label()
888 );
889 let Some(name) = family.product_name() else {
890 continue;
891 };
892 assert_eq!(
893 Family::ALL
894 .iter()
895 .filter(|held| held.product_name() == Some(name))
896 .count(),
897 1,
898 "{} is not a unique product name",
899 family.label()
900 );
901 }
902 }
903}