From 5502c6d320f05f43239fc8e2b2839eb3fd5d742a Mon Sep 17 00:00:00 2001 From: Mx Kookie Date: Fri, 11 Dec 2020 13:38:58 +0000 Subject: cassiopeia: implementing basic file parser --- apps/cassiopeia/src/format/parser.rs | 78 ++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 apps/cassiopeia/src/format/parser.rs (limited to 'apps/cassiopeia/src/format/parser.rs') diff --git a/apps/cassiopeia/src/format/parser.rs b/apps/cassiopeia/src/format/parser.rs new file mode 100644 index 000000000000..cc4b1b7c77df --- /dev/null +++ b/apps/cassiopeia/src/format/parser.rs @@ -0,0 +1,78 @@ +//! cassiopeia parser +//! +//! Takes a lexer's token stream as an input, and outputs a fully +//! parsed time file. + +use crate::format::{LineLexer, LineToken, Token}; +use chrono::{DateTime, FixedOffset as Offset}; +use logos::Lexer; +use std::collections::BTreeMap; +use std::iter::Iterator; + +/// A type-parsed line in a time file +#[derive(Debug)] +pub enum LineCfg { + /// A header line with a set of keys and values + Header(BTreeMap), + /// A session start line with a date and time + Start(Option>), + /// A session stop line with a date and time + Stop(Option>), + /// An invoice line with a date + Invoice(Option>), + /// An empty line + Empty, + /// A temporary value that is invalid + #[doc(hidden)] + Ignore, +} + +impl LineCfg { + pub(crate) fn valid(&self) -> bool { + match self { + LineCfg::Ignore => false, + _ => true, + } + } +} + +pub(crate) fn parse<'l>(lex: LineLexer<'l>) -> LineCfg { + use LineCfg::*; + use Token as T; + + #[cfg_attr(rustfmt, rustfmt_skip)] + lex.get_all().into_iter().fold(Ignore, |cfg, tok| match (cfg, tok) { + // If the first token is a comment, we ignore it + (Ignore, LineToken { tt: T::Comment, .. }, ) => Ignore, + // If the first token is a keyword, we wait for more data + (Ignore, LineToken { tt: T::Header, .. }) => Header(Default::default()), + (Ignore, LineToken { tt: T::Start, .. }) => Start(None), + (Ignore, LineToken { tt: T::Stop, .. }) => Stop(None), + (Ignore, LineToken { tt: T::Invoice, .. }) => Invoice(None), + + // If the first token _was_ a keyword, fill in the data + (Header(map), LineToken { tt: T::HeaderData, slice }) => Header(append_data(map, slice)), + (Start(_), LineToken { tt: T::Date, slice }) => Start(parse_date(slice)), + (Stop(_), LineToken { tt: T::Date, slice }) => Stop(parse_date(slice)), + (Invoice(_), LineToken { tt: T::Date, slice }) => Invoice(parse_date(slice)), + + // Pass empty lines through, + (Empty, _) => Empty, + + // Ignore everything else (which will be filtered) + _ => Ignore, + }) +} + +fn append_data(mut map: BTreeMap, slice: &str) -> BTreeMap { + let split = slice.split("=").collect::>(); + map.insert(split[0].into(), split[1].into()); + map +} + +fn parse_date(slice: &str) -> Option> { + Some( + DateTime::parse_from_str(slice, "%Y-%m-%d %H:%M:%S%:z") + .expect("Failed to parse date; invalid format!"), + ) +} -- cgit v1.2.3