#[derive(Debug, PartialEq, Clone)] pub struct Tone(i32); // new parses [a-g][+-]?[1-5]? to tone, sharp or flat, down 2 octave..up 1 octave pub fn new(s: S) -> Tone { Tone::new(s.to_string()) } impl Tone { fn new(s: String) -> Tone { let re = regex::Regex::new(r"^(((?^[a-g])(?[+-]?)|(?[A-G]M[0-6])|(?[A-G]m[0-6]))(?[1-5]?)|(?[0-9]+)|(?[^a-gA-G0-9]))$").unwrap(); let captures = re .captures(&s) .expect(format!("tone '{}' does not match regex", s).as_ref()); Tone(match captures.name("numeric") { Some(number) => number.as_str().parse::().unwrap(), None => match captures.name("rest") { Some(_) => 0, _ => { let mut result = match captures.name("letter") { Some(letter) => Tone::char_to_middle_i32(letter.as_str()), None => match captures.name("major") { Some(major) => Tone::char_to_middle_i32(&major.as_str()[..1]) + match &major.as_str()[2..] { "0" => 0, "1" => 2, "2" => 4, "3" => 5, "4" => 7, "5" => 9, _ => 11, }, None => { let minor = captures.name("minor").unwrap(); Tone::char_to_middle_i32(&minor.as_str()[..1]) + match &minor.as_str()[2..] { "0" => 0, "1" => 2, "2" => 3, "3" => 5, "4" => 7, "5" => 8, _ => 10, } }, }, } as i32; result += match captures.name("sharpness") { Some(sharpness) => match sharpness.as_str() { "+" => 1, "-" => -1, _ => 0, }, None => 0, } as i32; result += match captures.name("octave") { Some(octave) => match octave.as_str() { "" => 0, _ => (octave.as_str().parse::().unwrap() - 3) * 12, }, None => 0, } as i32; result } }, }) } fn char_to_middle_i32(c: &str) -> i32 { match c.to_lowercase().as_str() { "a" => 57, "b" => 59, "c" => 60, "d" => 62, "e" => 64, "f" => 65, _ => 67, } } pub fn i32(&self) -> i32 { self.0 } pub fn string(&self) -> String { let v = self.i32(); let modifier = if v > 0 && v < 57 { "-" } else if v >= 69 { "+" } else { "" }; modifier.to_string() + match v { 45|57|69 => "a", 46|58|70 => "a+", 47|59|71 => "b", 48|60|72 => "c", 49|61|73 => "c+", 50|62|74 => "d", 51|63|75 => "d+", 52|64|76 => "e", 53|65|77 => "f", 54|66|78 => "f+", 55|67|79 => "g", 56|68|80 => "g+", 0 => " ", _ => "?", } } } #[cfg(test)] mod test { #[test] fn test_tone_new() { eprintln!("numeric"); assert_eq!(super::new("60").i32(), 60); eprintln!("rests"); assert_eq!(super::new("-").i32(), 0); assert_eq!(super::new(".").i32(), 0); eprintln!("alpha"); assert_eq!(super::new("c").i32(), 60); assert_eq!(super::new("e").i32(), 64); assert_eq!(super::new("g").i32(), 67); eprintln!("alpha mod"); assert_eq!(super::new("c+").i32(), 60 + 1); assert_eq!(super::new("c-").i32(), 60 - 1); eprintln!("alpha octave"); assert_eq!(super::new("c3").i32(), 60); assert_eq!(super::new("c4").i32(), 60 + 12); eprintln!("alpha mod octave"); assert_eq!(super::new("c+4").i32(), 60 + 12 + 1); } #[test] fn test_tone_c_major() { assert_eq!(super::new("CM0"), super::new("c")); assert_eq!(super::new("CM1"), super::new("d")); assert_eq!(super::new("CM2"), super::new("e")); assert_eq!(super::new("CM3"), super::new("f")); assert_eq!(super::new("CM4"), super::new("g")); assert_eq!(super::new("CM5"), super::new("a4")); assert_eq!(super::new("CM6"), super::new("b4")); } #[test] fn test_tone_c_minor() { assert_eq!(super::new("Cm0"), super::new("c")); assert_eq!(super::new("Cm1"), super::new("d")); assert_eq!(super::new("Cm2"), super::new("e-")); assert_eq!(super::new("Cm3"), super::new("f")); assert_eq!(super::new("Cm4"), super::new("g")); assert_eq!(super::new("Cm5"), super::new("a-4")); assert_eq!(super::new("Cm6"), super::new("b-4")); } #[test] fn test_tone_a_major() { assert_eq!(super::new("AM0"), super::new("a")); assert_eq!(super::new("AM1"), super::new("b")); assert_eq!(super::new("AM2"), super::new("c+")); assert_eq!(super::new("AM3"), super::new("d")); assert_eq!(super::new("AM4"), super::new("e")); assert_eq!(super::new("AM5"), super::new("f+")); assert_eq!(super::new("AM6"), super::new("g+")); } #[test] fn test_tone_a_minor() { assert_eq!(super::new("Am0"), super::new("a")); assert_eq!(super::new("Am1"), super::new("b")); assert_eq!(super::new("Am2"), super::new("c")); assert_eq!(super::new("Am3"), super::new("d")); assert_eq!(super::new("Am4"), super::new("e")); assert_eq!(super::new("Am5"), super::new("f")); assert_eq!(super::new("Am6"), super::new("g")); } }