aboutsummaryrefslogtreecommitdiff
path: root/apps/cassiopeia/src/format
diff options
context:
space:
mode:
authorMx Kookie <kookie@spacekookie.de>2020-12-15 19:41:48 +0000
committerMx Kookie <kookie@spacekookie.de>2020-12-21 05:19:49 +0100
commitb9c988f42504c2e4cfa0715ac8f2d2a0db591cad (patch)
tree4850aba3bc43b7b303c4a1a3b6dd64a3f85e1df5 /apps/cassiopeia/src/format
parent236cf191b90a428325c8c179d595d4b1cd36f776 (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.rs33
-rw-r--r--apps/cassiopeia/src/format/ir.rs41
-rw-r--r--apps/cassiopeia/src/format/mod.rs30
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(())
+}