use crate::HscodeError::{HsChapterError, HsCodeLenError, InputError};
use std::fmt;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum HscodeError {
#[error("输入格式错误")]
InputError,
#[error("位数输入错误,需要8/10位,但你却输入了 {0} 位")]
HsCodeLenError(usize),
#[error("章节范围 1-97 但意外得到了 {0} 章")]
HsChapterError(u8),
}
pub struct HsCode(Vec<u8>);
impl fmt::Display for HsCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s: String = self.0.iter().map(|&d| (d + b'0') as char).collect();
write!(f, "{}", s)
}
}
impl HsCode {
pub fn new_from_str(input: &str) -> Self {
Self::try_new_from_str(input).unwrap()
}
pub fn get_chapter(&self) -> u8 {
self.0[0] * 10 + self.0[1]
}
pub fn try_new_from_str(input: &str) -> Result<Self, HscodeError> {
verify_and_trans_hs_code(input).map(HsCode)
}
}
fn verify_and_trans_hs_code(input: &str) -> Result<Vec<u8>, HscodeError> {
let bytes = input.as_bytes();
if !(bytes.len() == 8 || bytes.len() == 10) {
return Err(HsCodeLenError(bytes.len()));
}
if !bytes.iter().all(|b| b.is_ascii_digit()) {
return Err(InputError);
}
let chap: Vec<_> = bytes.iter().map(|b| b - b'0').collect();
let chapter = chap[0] * 10 + chap[1];
if chapter > 97 || chapter < 1 {
return Err(HsChapterError(chapter));
}
Ok(chap)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_8_digit_hscode() {
let code = HsCode::new_from_str("01012900");
assert_eq!(code.get_chapter(), 1);
assert_eq!(code.to_string(), "01012900");
}
#[test]
fn test_valid_10_digit_hscode() {
let code = HsCode::new_from_str("0101290010");
assert_eq!(code.get_chapter(), 1);
assert_eq!(code.to_string(), "0101290010");
}
#[test]
fn test_invalid_length() {
let result = HsCode::try_new_from_str("123");
assert!(matches!(result, Err(HsCodeLenError(3))));
}
#[test]
fn test_non_digit_input() {
let result = HsCode::try_new_from_str("ABCDEFGH");
assert!(matches!(result, Err(InputError)));
}
#[test]
fn test_invalid_chapter() {
let result = HsCode::try_new_from_str("9912345678");
assert!(matches!(result, Err(HsChapterError(99))));
}
#[test]
fn test_chapter_boundary() {
let result = HsCode::try_new_from_str("01012900");
assert!(result.is_ok());
let result = HsCode::try_new_from_str("97012900");
assert!(result.is_ok());
let result = HsCode::try_new_from_str("98012900");
assert!(matches!(result, Err(HsChapterError(98))));
}
}