From f186a7345dfc99347673f46e0daff0cb63ac8492 Mon Sep 17 00:00:00 2001 From: Kaiden Fey Date: Sun, 21 Feb 2021 14:56:11 +0100 Subject: k-office: initial code dump --- apps/koffice/libko/src/cass/format/ir.rs | 99 ++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 apps/koffice/libko/src/cass/format/ir.rs (limited to 'apps/koffice/libko/src/cass/format/ir.rs') diff --git a/apps/koffice/libko/src/cass/format/ir.rs b/apps/koffice/libko/src/cass/format/ir.rs new file mode 100644 index 000000000000..d1a3a62c1508 --- /dev/null +++ b/apps/koffice/libko/src/cass/format/ir.rs @@ -0,0 +1,99 @@ +use crate::cass::{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; + +/// 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), + /// 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) -> 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.into_iter().map(|(k, v)| (k, v.replace(",", ""))).collect()), 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; +} + +pub(crate) fn clean_ir(ir: &mut IrStream) { + ir.remove(0); // FIXME: this is required to remove the leading + // comment, which will be manually re-generated at + // the moment, but which would just add more blank + // lines between the new comment, and the first line + // in this current format. This is very bad, yikes + // yikes yikes, but what can I do, I have a deadline + // (not really) lol + + // FIXME: this hack gets rid of a trailing empty line if it exists + // to make sure we don't have any gaps between work sessions. + if match ir.last() { + Some(IrItem { + tt: IrType::Ignore, .. + }) => true, + _ => false, + } { + ir.pop(); + } +} + +/// Taken an IrType and append it to an existing IR stream +pub(crate) fn append_ir(ir: &mut IrStream, tt: IrType) { + let lo = ir.last().unwrap().lo; + ir.push(IrItem { tt, lo }); +} + +/// Search for the header that contains the version string and update it +pub(crate) fn update_header(ir: &mut IrStream) { + ir.iter_mut().for_each(|item| match item.tt { + IrType::Header(ref mut map) if map.contains_key("version") => { + map.insert("version".into(), crate::cass::meta::VERSION.into()); + } + _ => {} + }); +} -- cgit v1.2.3