diff --git a/src/lib.rs b/src/lib.rs index 8959da4..5aa77ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,21 +26,31 @@ pub struct Teeth { } impl Teeth { - pub fn new(number: u8, quadrant: QuadrantKind, permanent: bool) -> Teeth { - Teeth::check_teeth_number(number, permanent); + pub fn new(number: u8, quadrant: QuadrantKind, permanent: bool) -> Result { + if let Err(err) = Teeth::check_teeth_number(number, permanent) { + return Err(err); + } - Teeth { + Ok(Teeth { number, quadrant, permanent, - } + }) } - fn check_teeth_number(number: u8, permanent: bool) { + fn check_teeth_number(number: u8, permanent: bool) -> Result<(), String> { if permanent && (number < 1 || number > 8) { - panic!("Permanent teeth {} should be in range [1; 8]", number) + Err(format!( + "Permanent teeth {} should be in range [1; 8]", + number + )) } else if !permanent && (number < 1 || number > 5) { - panic!("Primary teeth {} should be in range [1; 5]", number) + Err(format!( + "Primary teeth {} should be in range [1; 5]", + number + )) + } else { + Ok(()) } } @@ -52,9 +62,9 @@ impl Teeth { } } - pub fn from_iso(value: &str) -> Self { + pub fn from_iso(value: &str) -> Result { if value.len() != 2 { - panic!("{} is not a valid ISO dental notation", value); + return Err(format!("{} is not a valid ISO dental notation", value)); } let mut char_iter = value.chars(); let (quadrant, permanent) = match char_iter.next().unwrap() { @@ -66,22 +76,24 @@ impl Teeth { '6' => (QuadrantKind::TopRight, false), '7' => (QuadrantKind::BottomRight, false), '8' => (QuadrantKind::BottomLeft, false), - x => panic!("Quadrant value {} not included in range [1; 8]", x), + x => return Err(format!("Quadrant value {} not included in range [1; 8]", x)), }; let teeth_string = char_iter.next().unwrap(); let teeth_number_option = teeth_string.to_digit(10); if teeth_number_option.is_none() { - panic!("{} is not a number", teeth_string) + return Err(format!("{} is not a number", teeth_string)); } let number = teeth_number_option.unwrap() as u8; - Teeth::check_teeth_number(number, permanent); - Teeth { + if let Err(err) = Teeth::check_teeth_number(number, permanent) { + return Err(err); + } + Ok(Teeth { quadrant, permanent, number, - } + }) } pub fn to_iso(&self) -> String { @@ -98,7 +110,7 @@ impl Teeth { quadrant_number.to_string() + &self.number.to_string() } - pub fn from_uns(value: &str) -> Self { + pub fn from_uns(value: &str) -> Result { let number_value_option = value.parse::(); let mut permanent = false; let uns = if number_value_option.is_ok() { @@ -109,12 +121,12 @@ impl Teeth { }; if permanent && (uns < 1 || uns > 32) { - panic!( + return Err(format!( "UNS permanent teeth has to be in range [1; 32] (currently {})", uns - ); + )); } else if !permanent && (uns < 1 || uns > 20) { - panic!("UNS primary teeth has to be in range [A; T]"); + return Err(format!("UNS primary teeth has to be in range [A; T]")); } let max = Teeth::quadrant_max(permanent); let quadrant = match ((uns - 1) / max) + 1 { @@ -122,19 +134,21 @@ impl Teeth { 2 => QuadrantKind::TopRight, 3 => QuadrantKind::BottomRight, 4 => QuadrantKind::BottomLeft, - _ => panic!("UNS teeth value not in range"), + _ => return Err(format!("UNS teeth value not in range")), }; let number = if quadrant == QuadrantKind::TopRight || quadrant == QuadrantKind::BottomLeft { ((uns - 1) % max) + 1 } else { max - ((uns - 1) % max) }; - Teeth::check_teeth_number(number, permanent); - Teeth { + if let Err(err) = Teeth::check_teeth_number(number, permanent) { + return Err(err); + } + Ok(Teeth { quadrant, number, permanent, - } + }) } pub fn to_uns(&self) -> String { @@ -153,19 +167,24 @@ impl Teeth { } } - pub fn from_alphanumeric(value: &str) -> Self { + pub fn from_alphanumeric(value: &str) -> Result { if value.len() != 3 { - panic!("{} is not a valid alphanumeric dental notation", value); + return Err(format!( + "{} is not a valid alphanumeric dental notation", + value + )); } let quadrant = match &value[0..2] { "UL" => QuadrantKind::TopLeft, "UR" => QuadrantKind::TopRight, "LR" => QuadrantKind::BottomRight, "LL" => QuadrantKind::BottomLeft, - x => panic!( - "Quadrant value {} not a valid value (accepted: ['UL', 'UR', 'LR', 'LL'])", - x - ), + x => { + return Err(format!( + "Quadrant value {} not a valid value (accepted: ['UL', 'UR', 'LR', 'LL'])", + x + )) + } }; let teeth_string = &value[2..3]; @@ -184,17 +203,21 @@ impl Teeth { "C" => (3, false), "D" => (4, false), "E" => (5, false), - x => panic!( - "Number value {} not a valid value (accepted: [1; 8] and ['A'; 'E'])", - x - ), + x => { + return Err(format!( + "Number value {} not a valid value (accepted: [1; 8] and ['A'; 'E'])", + x + )) + } }; - Teeth::check_teeth_number(number, permanent); - Teeth { + if let Err(err) = Teeth::check_teeth_number(number, permanent) { + return Err(err); + } + Ok(Teeth { quadrant, permanent, number, - } + }) } pub fn to_alphanumeric(&self) -> String { @@ -236,7 +259,8 @@ mod test { ($name:ident, $func:ident, $value:expr, $quadrant:expr,$number:expr, $permanent:expr) => { #[test] fn $name() { - let teeth = Teeth::$func($value); + let teeth = Teeth::$func($value).unwrap(); + assert_eq!(teeth.quadrant, $quadrant); assert_eq!(teeth.number, $number); assert_eq!(teeth.permanent, $permanent); @@ -247,9 +271,8 @@ mod test { macro_rules! from_fail { ($name:ident, $func:ident, $value:expr) => { #[test] - #[should_panic] fn $name() { - Teeth::$func($value); + assert!(Teeth::$func($value).is_err()); } }; }