#[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])(?[+-]?)(?[1-5]?)|(?[0-9]+))$").unwrap(); let captures = re.captures(s.as_ref()).unwrap(); Tone (match captures.name("numeric") { Some(number) => number.as_str().parse::().unwrap(), None => { let mut result = match captures.name("letter").unwrap().as_str() { "a" => 57, "b" => 59, "c" => 60, "d" => 62, "e" => 64, "f" => 65, _ => 67, } 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 }, }) } pub fn i32(&self) -> i32 { self.0 } } #[cfg(test)] mod test { #[test] fn test_tone_new() { assert_eq!(super::new("60").i32(), 60); assert_eq!(super::new("c").i32(), 60); assert_eq!(super::new("e").i32(), 64); assert_eq!(super::new("g").i32(), 67); assert_eq!(super::new("c+").i32(), 60+1); assert_eq!(super::new("c-").i32(), 60-1); assert_eq!(super::new("c3").i32(), 60); assert_eq!(super::new("c4").i32(), 60+12); assert_eq!(super::new("c+4").i32(), 60+12+1); } }