diff --git a/src/lib.rs b/src/lib.rs index e85f222..f24109a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,9 @@ #[macro_use] extern crate quick_error; use chrono::{ Local,prelude::*, }; +use std::str::Chars; +use std::iter::Peekable; + quick_error! { #[derive(Debug)] pub enum ParseError { @@ -10,42 +13,108 @@ quick_error! { } } +#[derive(Debug)] enum SimpleDateFormatPart { - Year, - Month, + YearLower(usize), + YearUpper(usize), + MonthLower(usize), + MonthUpper(usize), Day, Hour, Minute, Second, + LiteralChar(char), Literal(String), } +#[derive(Debug)] pub struct SimpleDateFormat { parts: Vec, } impl SimpleDateFormat { - fn format_local(&self, date_time: &DateTime) -> String { + pub fn format_local(&self, date_time: &DateTime) -> String { let mut ret = String::with_capacity(512); - ret.push_str(&format!("{}", date_time.year())); - ret.push('-'); - ret.push_str(&format!("{}", date_time.month())); - ret.push('-'); - ret.push_str(&format!("{}", date_time.day())); + + for part in &self.parts { + match part { + SimpleDateFormatPart::LiteralChar(c) => ret.push(*c), + SimpleDateFormatPart::Literal(s) => ret.push_str(s), + SimpleDateFormatPart::YearLower(cnt) => ret.push_str(&format_str(date_time.year(), *cnt)), + SimpleDateFormatPart::MonthLower(cnt) => ret.push_str(&format_str(date_time.month() as i32, *cnt)), + _ => (), + } + } ret } } pub fn fmt(f: &str) -> Result { - Ok(SimpleDateFormat{ parts: vec![] }) + let mut parts = vec![]; + + let mut is_in_quotation_mark = false; + let mut literal = String::new(); + let mut chars = f.chars().peekable(); + while let Some(c) = chars.next() { + if is_in_quotation_mark && c != '\'' { + literal.push(c); + continue; + } + match c { + '\'' => if is_in_quotation_mark { + if let Some('\'') = chars.peek() { + literal.push(c); + chars.next(); // eat '\'' + } else { + is_in_quotation_mark = false; + parts.push(SimpleDateFormatPart::Literal(literal)); + literal = String::new(); + } + } else { + is_in_quotation_mark = true; + }, + ',' | '.' | ':' | '-' | ' ' => parts.push(SimpleDateFormatPart::LiteralChar(c)), + 'y' => parts.push(SimpleDateFormatPart::YearLower(get_all_chars(c, &mut chars))), + 'Y' => parts.push(SimpleDateFormatPart::YearUpper(get_all_chars(c, &mut chars))), + 'm' => parts.push(SimpleDateFormatPart::MonthLower(get_all_chars(c, &mut chars))), + 'M' => parts.push(SimpleDateFormatPart::MonthUpper(get_all_chars(c, &mut chars))), + _ => (), + } + } + + Ok(SimpleDateFormat{ parts }) // Err(ParseError::Format(f.into())) } +fn format_str(n: i32, cnt: usize) -> String { + let ret = format!("{}", n); + if cnt > ret.len() { + "0".repeat(cnt - ret.len()) + &ret + } else { + ret + } +} + +fn get_all_chars(c: char, chars: &mut Peekable) -> usize { + let mut cnt = 1_usize; + while let Some(next_char) = chars.peek() { + if *next_char == c { + cnt += 1; + chars.next(); + } else { + break; + } + } + cnt +} #[test] fn it_works() { - println!("test output: {}", fmt("").unwrap().format_local(&Local::now())); + // println!("test output: {}", fmt("").unwrap().format_local(&Local::now())); + + println!("{:?}", fmt("y yy-mm 'mm '''")); + println!("{:?}", fmt("y yy-mm'(-'m')' '[mm]'").unwrap().format_local(&Local::now())); assert_eq!(2 + 2, 4); }