diff options
Diffstat (limited to 'apps/cassiopeia/src/format')
-rw-r--r-- | apps/cassiopeia/src/format/ir.rs | 63 | ||||
-rw-r--r-- | apps/cassiopeia/src/format/mod.rs | 32 | ||||
-rw-r--r-- | apps/cassiopeia/src/format/parser.rs | 3 |
3 files changed, 91 insertions, 7 deletions
diff --git a/apps/cassiopeia/src/format/ir.rs b/apps/cassiopeia/src/format/ir.rs new file mode 100644 index 000000000000..32922ec079e7 --- /dev/null +++ b/apps/cassiopeia/src/format/ir.rs @@ -0,0 +1,63 @@ +use crate::{format::LineCfg, Date, Time, TimeFile}; +use std::collections::BTreeMap; + +/// A set of IR parsed items that makes up a whole cass file +pub(crate) type IrStream = Vec<IrItem>; + +/// Intermediate representation for parsing and generating files +/// +/// The CASS IR is largely based on the output of the parser's +/// [`LineCfg`](crate::format::LineCfg), but with concrete types used +/// in the data layer (namely [`Date`][date] and [`Time`][time]), +/// while also keeping track of the line numbers to allow idempotent +/// file changes. +/// +/// Something not yet implemented is comment pass-through (this needs +/// to happen in the parser first), but will likely be implemented in +/// a future version. +/// +/// [date]: crate::Date +/// [time]: crate::Time +#[derive(Debug, Clone)] +pub(crate) struct IrItem { + pub(crate) tt: IrType, + pub(crate) lo: usize, +} + +/// Disambiguate between different IR line types with their payload +#[derive(Debug, Clone)] +pub(crate) enum IrType { + /// A line with parsed header information + Header(BTreeMap<String, String>), + /// Start a session at a given timestapm + Start(Time), + /// Stop a session at a given timestamp + Stop(Time), + /// Invoice a block of previous work + Invoice(Date), + /// An item that gets ignored + Ignore, +} + +/// Generate a stream of IR items from the raw parser output +pub(crate) fn generate_ir(buf: impl Iterator<Item = LineCfg>) -> IrStream { + buf.enumerate().fold(vec![], |mut buf, (lo, item)| { + #[cfg_attr(rustfmt, rustfmt_skip)] + buf.push(match item { + LineCfg::Header(map) => IrItem { tt: IrType::Header(map), lo }, + LineCfg::Start(Some(time)) => IrItem { tt: IrType::Start(time.into()), lo }, + LineCfg::Stop(Some(time)) => IrItem { tt: IrType::Stop(time.into()), lo }, + LineCfg::Invoice(Some(date)) => IrItem { tt: IrType::Invoice(date.into()), lo }, + LineCfg::Ignore => IrItem { tt: IrType::Ignore, lo }, + _ => IrItem { tt: IrType::Ignore, lo }, + }); + + buf + }) +} + + +pub(crate) trait MakeIr { + /// Make a new IR line from an object + fn make_ir(&self) -> IrType; +} diff --git a/apps/cassiopeia/src/format/mod.rs b/apps/cassiopeia/src/format/mod.rs index e55658d515ff..814c08656dbe 100644 --- a/apps/cassiopeia/src/format/mod.rs +++ b/apps/cassiopeia/src/format/mod.rs @@ -1,17 +1,33 @@ //! cassiopeia file format +mod ir; mod lexer; mod parser; +pub(crate) use ir::{IrItem, IrStream, IrType, MakeIr}; pub(crate) use lexer::{LineLexer, LineToken, Token}; pub(crate) use parser::LineCfg; use crate::TimeFile; use std::{fs::File, io::Read}; +#[derive(Default)] +pub struct ParseOutput { + pub(crate) ir: IrStream, + pub(crate) tf: TimeFile, +} + +impl ParseOutput { + fn append(mut self, ir: IrItem) -> Self { + self.tf.append(ir.clone()); + self.ir.push(ir); + self + } +} + /// Load a file from disk and parse it into a /// [`TimeFile`](crate::TimeFile) -pub fn load_file(path: &str) -> Option<TimeFile> { +pub fn load_file(path: &str) -> Option<ParseOutput> { let mut f = File::open(path).ok()?; let mut content = String::new(); f.read_to_string(&mut content).ok()?; @@ -19,11 +35,13 @@ pub fn load_file(path: &str) -> Option<TimeFile> { let mut lines: Vec<String> = content.split("\n").map(|l| l.to_owned()).collect(); Some( - lines - .iter_mut() - .map(|line| lexer::lex(line)) - .map(|lex| parser::parse(lex)) - .filter(|line| line.valid()) - .fold(TimeFile::default(), |file, line| file.append(line)), + ir::generate_ir( + lines + .iter_mut() + .map(|line| lexer::lex(line)) + .map(|lex| parser::parse(lex)), + ) + .into_iter() + .fold(ParseOutput::default(), |output, ir| output.append(ir)), ) } diff --git a/apps/cassiopeia/src/format/parser.rs b/apps/cassiopeia/src/format/parser.rs index 430fee6332a7..d1f0bcdebc68 100644 --- a/apps/cassiopeia/src/format/parser.rs +++ b/apps/cassiopeia/src/format/parser.rs @@ -53,6 +53,9 @@ pub(crate) fn parse<'l>(lex: LineLexer<'l>) -> LineCfg { (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, }) |