You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
100 lines
3.4 KiB
100 lines
3.4 KiB
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.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::meta::VERSION.into());
|
|
}
|
|
_ => {}
|
|
});
|
|
}
|
|
|