use super::{CalverDate, CalverError};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum Token {
FullYear,
ShortYear,
ZeroPaddedMonth,
Month,
IsoWeek,
Day,
Patch,
Separator(String),
}
pub(super) fn parse_format(format: &str) -> Result<Vec<Token>, CalverError> {
if format.is_empty() {
return Err(CalverError::EmptyFormat);
}
let mut tokens = Vec::new();
let mut remaining = format;
while !remaining.is_empty() {
if let Some(rest) = remaining.strip_prefix("YYYY") {
tokens.push(Token::FullYear);
remaining = rest;
} else if let Some(rest) = remaining.strip_prefix("YY") {
tokens.push(Token::ShortYear);
remaining = rest;
} else if let Some(rest) = remaining.strip_prefix("0M") {
tokens.push(Token::ZeroPaddedMonth);
remaining = rest;
} else if let Some(rest) = remaining.strip_prefix("MM") {
tokens.push(Token::Month);
remaining = rest;
} else if let Some(rest) = remaining.strip_prefix("WW") {
tokens.push(Token::IsoWeek);
remaining = rest;
} else if let Some(rest) = remaining.strip_prefix("DD") {
tokens.push(Token::Day);
remaining = rest;
} else if let Some(rest) = remaining.strip_prefix("PATCH") {
tokens.push(Token::Patch);
remaining = rest;
} else {
let ch = remaining.chars().next().unwrap();
if ch == '.' || ch == '-' || ch == '_' {
if let Some(Token::Separator(s)) = tokens.last_mut() {
s.push(ch);
} else {
tokens.push(Token::Separator(ch.to_string()));
}
remaining = &remaining[ch.len_utf8()..];
} else {
return Err(CalverError::UnknownToken(remaining.to_string()));
}
}
}
if !tokens.iter().any(|t| matches!(t, Token::Patch)) {
return Err(CalverError::NoPatchToken);
}
Ok(tokens)
}
pub(super) fn build_date_prefix(tokens: &[Token], date: CalverDate) -> String {
let mut prefix = String::new();
for token in tokens {
match token {
Token::Patch => break,
Token::FullYear => prefix.push_str(&date.year.to_string()),
Token::ShortYear => prefix.push_str(&format!("{}", date.year % 100)),
Token::ZeroPaddedMonth => prefix.push_str(&format!("{:02}", date.month)),
Token::Month => prefix.push_str(&date.month.to_string()),
Token::IsoWeek => prefix.push_str(&date.iso_week.to_string()),
Token::Day => prefix.push_str(&date.day.to_string()),
Token::Separator(s) => prefix.push_str(s),
}
}
prefix
}
pub(super) fn build_date_suffix(tokens: &[Token], date: CalverDate) -> String {
let mut suffix = String::new();
let mut past_patch = false;
for token in tokens {
if matches!(token, Token::Patch) {
past_patch = true;
continue;
}
if !past_patch {
continue;
}
match token {
Token::FullYear => suffix.push_str(&date.year.to_string()),
Token::ShortYear => suffix.push_str(&format!("{}", date.year % 100)),
Token::ZeroPaddedMonth => suffix.push_str(&format!("{:02}", date.month)),
Token::Month => suffix.push_str(&date.month.to_string()),
Token::IsoWeek => suffix.push_str(&date.iso_week.to_string()),
Token::Day => suffix.push_str(&date.day.to_string()),
Token::Separator(s) => suffix.push_str(s),
Token::Patch => {} }
}
suffix
}
pub(super) fn find_separator(tokens: &[Token]) -> String {
for token in tokens {
if let Token::Separator(s) = token {
return s.chars().next().unwrap().to_string();
}
}
".".to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_default_format() {
let tokens = parse_format("YYYY.MM.PATCH").unwrap();
assert_eq!(tokens.len(), 5); }
#[test]
fn parse_zero_padded_month() {
let tokens = parse_format("YYYY.0M.PATCH").unwrap();
assert_eq!(tokens.len(), 5);
assert_eq!(tokens[2], Token::ZeroPaddedMonth);
}
#[test]
fn parse_daily_format() {
let tokens = parse_format("YYYY.MM.DD.PATCH").unwrap();
assert_eq!(tokens.len(), 7); }
#[test]
fn parse_weekly_format() {
let tokens = parse_format("YY.WW.PATCH").unwrap();
assert_eq!(tokens.len(), 5);
assert_eq!(tokens[0], Token::ShortYear);
assert_eq!(tokens[2], Token::IsoWeek);
}
#[test]
fn error_no_patch_token() {
let err = parse_format("YYYY.MM").unwrap_err();
assert_eq!(err, CalverError::NoPatchToken);
}
#[test]
fn error_empty_format() {
let err = parse_format("").unwrap_err();
assert_eq!(err, CalverError::EmptyFormat);
}
#[test]
fn error_unknown_token() {
let err = parse_format("YYYY.MM.PATCH.UNKNOWN").unwrap_err();
assert!(matches!(err, CalverError::UnknownToken(_)));
}
}