aboutsummaryrefslogtreecommitdiff
path: root/apps/koffice/libko/src/cass/format/ir.rs
diff options
context:
space:
mode:
Diffstat (limited to 'apps/koffice/libko/src/cass/format/ir.rs')
-rw-r--r--apps/koffice/libko/src/cass/format/ir.rs99
1 files changed, 99 insertions, 0 deletions
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<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.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());
+ }
+ _ => {}
+ });
+}