use crate::tokens::TOKEN_HYPHEN;
use crate::utils::{collect_ascii_digits, is_valid_month};
use crate::{collect_month_and_validate, parse_format};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct YearMonth {
pub(crate) year: i32,
pub(crate) month: u32,
}
impl YearMonth {
pub(crate) const fn new(year: i32, month: u32) -> Self {
Self { year, month }
}
pub fn new_opt(year: i32, month: u32) -> Option<Self> {
if year == 0 {
return None;
}
if !is_valid_month(&month) {
return None;
}
Some(Self::new(year, month))
}
#[inline]
pub const fn year(&self) -> i32 {
self.year
}
#[inline]
pub const fn month(&self) -> u32 {
self.month
}
}
#[inline]
pub fn parse_month(s: &str) -> Option<YearMonth> {
parse_format(s, parse_month_component)
}
pub fn parse_month_component(s: &str, position: &mut usize) -> Option<YearMonth> {
let parsed_year = collect_ascii_digits(s, position);
if parsed_year.len() < 4 {
return None;
}
let year = parsed_year.parse::<i32>().ok()?;
if year == 0 {
return None;
}
if *position > s.len() || s.chars().nth(*position) != Some(TOKEN_HYPHEN) {
return None;
} else {
*position += 1;
}
let month = collect_month_and_validate(s, position)?;
Some(YearMonth::new(year, month))
}
#[cfg(test)]
mod tests {
use super::{parse_month, parse_month_component, YearMonth};
#[test]
fn test_parse_month_string() {
let parsed = parse_month("2004-12");
assert_eq!(parsed, Some(YearMonth::new(2004, 12)));
}
#[test]
fn test_parse_month_string_fails_invalid_month() {
let parsed = parse_month("2004-2a");
assert_eq!(parsed, None);
}
#[test]
fn test_parse_month_string_fails() {
let parsed = parse_month("2004-13");
assert_eq!(parsed, None);
}
#[test]
fn test_parse_month_component() {
let mut position = 0usize;
let parsed = parse_month_component("2004-12", &mut position);
assert_eq!(parsed, Some(YearMonth::new(2004, 12)));
}
#[test]
fn test_parse_month_component_fails_year_lt_4_digits() {
let mut position = 0usize;
let parsed = parse_month_component("200-12", &mut position);
assert_eq!(parsed, None);
}
#[test]
fn test_parse_month_component_fails_invalid_month_lower_bound() {
let mut position = 0usize;
let parsed = parse_month_component("2004-0", &mut position);
assert_eq!(parsed, None);
}
#[test]
fn test_parse_month_component_fails_invalid_month_upper_bound() {
let mut position = 0usize;
let parsed = parse_month_component("2004-13", &mut position);
assert_eq!(parsed, None);
}
#[test]
fn test_parse_month_component_fails_invalid_month_syntax() {
let mut position = 0usize;
let parsed = parse_month_component("2004-1a", &mut position);
assert_eq!(parsed, None);
}
#[test]
fn test_parse_month_component_fails_invalid_separator() {
let mut position = 0usize;
let parsed = parse_month_component("2004/12", &mut position);
assert_eq!(parsed, None);
}
}