diff options
author | Mx Kookie <kookie@spacekookie.de> | 2020-12-15 19:41:48 +0000 |
---|---|---|
committer | Mx Kookie <kookie@spacekookie.de> | 2020-12-21 05:19:49 +0100 |
commit | b9c988f42504c2e4cfa0715ac8f2d2a0db591cad (patch) | |
tree | 4850aba3bc43b7b303c4a1a3b6dd64a3f85e1df5 /apps/cassiopeia/src/format | |
parent | 236cf191b90a428325c8c179d595d4b1cd36f776 (diff) |
cassiopeia: finishing up version 0.3.0
This commit does kind of a lot to get cass(1) over the finish line.
For one it implements all the CLI functions (well, almost all) with
their respective parameters, and also creates a new `gen` module which
uses the IR stream to generate a new file based on the old one, while
updating header fields that need to be updated (currently only
`version`).
This version does nothing with the actual header values, and probably
has a lot of bugs. More documentation will follow in future
cassiopeia commits.
Diffstat (limited to 'apps/cassiopeia/src/format')
-rw-r--r-- | apps/cassiopeia/src/format/gen.rs | 33 | ||||
-rw-r--r-- | apps/cassiopeia/src/format/ir.rs | 41 | ||||
-rw-r--r-- | apps/cassiopeia/src/format/mod.rs | 30 |
3 files changed, 97 insertions, 7 deletions
diff --git a/apps/cassiopeia/src/format/gen.rs b/apps/cassiopeia/src/format/gen.rs new file mode 100644 index 000000000000..2bcd6cc724fd --- /dev/null +++ b/apps/cassiopeia/src/format/gen.rs @@ -0,0 +1,33 @@ +//! Cassiopeia line generator +//! +//! This module takes a set of IR lines, and generates strings from +//! them that are in accordance with the way that the parser of the +//! same version expects them. + +use crate::format::ir::{IrItem, IrType}; + +/// Take a line of IR and generate a string to print into a file +pub(crate) fn line(ir: &IrItem) -> String { + let IrItem { tt, lo } = ir; + match tt { + IrType::Ignore => "".into(), + IrType::Header(map) => format!( + "HEADER {}", + map.iter() + .map(|(k, v)| format!("{}={},", k, v)) + .collect::<Vec<_>>() + .join("") + ), + IrType::Start(time) => format!("START {}", time.to_string()), + + // FIXME: find a better way to align the lines here rather + // than having to manually having to pad the 'STOP' commands + IrType::Stop(time) => format!("STOP {}", time.to_string()), + IrType::Invoice(date) => format!("INVOICE {}", date.to_string()), + } +} + +pub(crate) fn head_comment() -> String { + ";; generated by cassiopeia, be careful about editing by hand!".into() +} + diff --git a/apps/cassiopeia/src/format/ir.rs b/apps/cassiopeia/src/format/ir.rs index 32922ec079e7..09a54e810615 100644 --- a/apps/cassiopeia/src/format/ir.rs +++ b/apps/cassiopeia/src/format/ir.rs @@ -44,7 +44,7 @@ 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::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 }, @@ -56,8 +56,45 @@ pub(crate) fn generate_ir(buf: impl Iterator<Item = LineCfg>) -> IrStream { }) } - 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()); + } + _ => {} + }); +} diff --git a/apps/cassiopeia/src/format/mod.rs b/apps/cassiopeia/src/format/mod.rs index 814c08656dbe..89f3a6ccb466 100644 --- a/apps/cassiopeia/src/format/mod.rs +++ b/apps/cassiopeia/src/format/mod.rs @@ -1,18 +1,22 @@ //! cassiopeia file format -mod ir; +mod gen; +pub(crate) 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}; +use ir::{IrItem, IrStream}; +use std::{ + fs::{File, OpenOptions}, + io::{Read, Write}, +}; #[derive(Default)] -pub struct ParseOutput { +pub(crate) struct ParseOutput { pub(crate) ir: IrStream, pub(crate) tf: TimeFile, } @@ -27,7 +31,7 @@ impl ParseOutput { /// Load a file from disk and parse it into a /// [`TimeFile`](crate::TimeFile) -pub fn load_file(path: &str) -> Option<ParseOutput> { +pub(crate) 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()?; @@ -45,3 +49,19 @@ pub fn load_file(path: &str) -> Option<ParseOutput> { .fold(ParseOutput::default(), |output, ir| output.append(ir)), ) } + +/// Write a file with the updated IR stream +pub(crate) fn write_file(path: &str, ir: &mut IrStream) -> Option<()> { + ir::update_header(ir); + let mut lines = ir.into_iter().map(|ir| gen::line(ir)).collect::<Vec<_>>(); + lines.insert(0, gen::head_comment()); + + let mut f = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(path) + .ok()?; + f.write_all(lines.join("\n").as_bytes()).ok()?; + Some(()) +} |