aboutsummaryrefslogtreecommitdiff
path: root/apps/koffice/libko/src/cass/format/parser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'apps/koffice/libko/src/cass/format/parser.rs')
-rw-r--r--apps/koffice/libko/src/cass/format/parser.rs73
1 files changed, 73 insertions, 0 deletions
diff --git a/apps/koffice/libko/src/cass/format/parser.rs b/apps/koffice/libko/src/cass/format/parser.rs
new file mode 100644
index 000000000000..8e0602d440d2
--- /dev/null
+++ b/apps/koffice/libko/src/cass/format/parser.rs
@@ -0,0 +1,73 @@
+//! cassiopeia parser
+//!
+//! Takes a lexer's token stream as an input, and outputs a fully
+//! parsed time file.
+
+use crate::cass::format::{LineLexer, LineToken, Token};
+use chrono::{DateTime, FixedOffset as Offset, NaiveDate};
+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<String, String>),
+ /// A session start line with a date and time
+ Start(Option<DateTime<Offset>>),
+ /// A session stop line with a date and time
+ Stop(Option<DateTime<Offset>>),
+ /// An invoice line with a date
+ Invoice(Option<NaiveDate>),
+ /// A temporary value that is invalid
+ #[doc(hidden)]
+ Ignore,
+}
+
+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_datetime(slice)),
+ (Stop(_), LineToken { tt: T::Date, slice }) => Stop(parse_datetime(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<String, String>, slice: &str) -> BTreeMap<String, String> {
+ let split = slice.split("=").collect::<Vec<_>>();
+ map.insert(split[0].into(), split[1].into());
+ map
+}
+
+fn parse_datetime(slice: &str) -> Option<DateTime<Offset>> {
+ Some(
+ DateTime::parse_from_str(slice, "%Y-%m-%d %H:%M:%S%:z")
+ .expect("Failed to parse date; invalid format!"),
+ )
+}
+
+fn parse_date(slice: &str) -> Option<NaiveDate> {
+ Some(
+ NaiveDate::parse_from_str(slice, "%Y-%m-%d")
+ .expect("Failed to parse date; invalid format!"),
+ )
+}