use std::io::Write;
use zs_utilinv_core::countermeter::{
validate_logdata, FixpNum, Handle as CmHandle, LogCommand, LogEntry, NaiveDate,
};
struct ViewIo {
iatty: bool,
stdin: std::io::Stdin,
stdout: std::io::Stdout,
buf: String,
}
impl ViewIo {
fn prompt(&mut self, q: &str) -> std::io::Result<&str> {
if self.iatty {
write!(&mut self.stdout, "{} $ ", q)?;
self.stdout.flush()?;
}
self.buf.clear();
self.stdin.read_line(&mut self.buf)?;
if !self.iatty {
writeln!(&mut self.stdout)?;
}
Ok(self.buf.trim())
}
fn prompt_yn(&mut self, q: &str) -> std::io::Result<bool> {
let q2 = format!("{} ? [yn]", q);
Ok(loop {
break match self.prompt(&q2)?.to_lowercase().as_str() {
"y" | "yes" | "j" | "ja" => true,
"n" | "no" | "nein" => false,
_ => continue,
};
})
}
}
fn main() -> anyhow::Result<()> {
let mut vio = ViewIo {
iatty: atty::is(atty::Stream::Stdin) && atty::is(atty::Stream::Stdout),
stdin: std::io::stdin(),
stdout: std::io::stdout(),
buf: String::new(),
};
std::fs::create_dir_all("cometers")?;
let date: NaiveDate = loop {
let tmp = vio.prompt("enter the date to edit")?;
if tmp.is_empty() {
return Ok(());
}
if let Ok(x) = NaiveDate::parse_from_str(tmp, "%Y-%m-%d") {
break x;
}
};
loop {
let meter = vio.prompt("meter")?;
if meter.is_empty() {
break;
}
let meter = CmHandle::new(std::path::PathBuf::from("cometers"), meter.to_string());
let mut meterlog = if !meter.log_path().exists() {
eprintln!("ERROR: countermeter «{}» doesn't exist.", meter.name());
if !vio.prompt_yn("would you like to init this cometer log")? {
continue;
}
Default::default()
} else {
meter.read_logdata()?
};
for i in validate_logdata(&meterlog, Some(&date)) {
println!("{}", i);
}
let use_res = on_meter(&mut vio, &date, meter.name(), &mut meterlog)?;
if !use_res {
} else if meterlog.is_empty() {
if vio.prompt_yn("WARN: would you like to clear+remove this cometer log")? {
std::fs::remove_file(meter.log_path())?;
}
} else {
for i in validate_logdata(&meterlog, Some(&date)) {
println!("{}", i);
}
meter.write_logdata(&meterlog)?;
}
}
Ok(())
}
#[derive(Clone)]
enum MeterAct {
Value(Option<FixpNum>),
Command(LogCommand),
Comment(String),
}
fn on_meter(
vio: &mut ViewIo,
date: &NaiveDate,
meter: &str,
meterlog: &mut std::collections::BTreeSet<LogEntry>,
) -> std::io::Result<bool> {
let prpt = format!("cm:{}", meter);
for i in meterlog.iter().filter(|i| &i.date == date) {
println!("{:?}", i);
}
Ok(loop {
let tmp = match vio.prompt(&prpt)? {
"" | "exit" | "quit" => break true,
"qwos" => break false,
"brk" => MeterAct::Command(LogCommand::Broken),
"rst" => MeterAct::Command(LogCommand::Reset),
"dec" => MeterAct::Command(LogCommand::Decrease),
"inc" => MeterAct::Command(LogCommand::Increase),
"-" => MeterAct::Value(None),
"dlt" => {
meterlog.retain(|i| &i.date != date);
continue;
}
"p" => {
for i in meterlog.iter().filter(|i| &i.date == date) {
println!("{:?}", i);
}
continue;
}
"?" | "help" => {
println!(
r#"available log line types:
brk .... broken
rst .... reset
dec .... decrease
inc .... increase
- .... remove the registered value
dlt .... delete the log line
? .... print this help text
exit ... exit this sub-repl, save updated entries
qwos ... exit this sub-repl, don't save updates entries
p .... print the current entry
other allowed inputs:
- a float number (must start with a digit)
- "cmt " and then a comment
"#
);
continue;
}
tmp => {
if tmp
.chars()
.next()
.map(|i| i.is_ascii_digit())
.unwrap_or(false)
{
match tmp.parse::<FixpNum>() {
Ok(x) => MeterAct::Value(Some(x)),
Err(e) => {
eprintln!("ERROR: unable to parse float number: {}", e);
continue;
}
}
} else if let Some(x) = tmp.strip_prefix("cmt ") {
MeterAct::Comment(x.to_string())
} else {
eprintln!("ERROR: unknown meter action: {}", tmp);
continue;
}
}
};
let mut cnt = 0;
*meterlog = std::mem::take(meterlog)
.into_iter()
.map(|mut i| {
if &i.date == date {
match tmp.clone() {
MeterAct::Command(x) => i.cmd = x,
MeterAct::Value(x) => i.value = x,
MeterAct::Comment(x) => i.comment = x,
}
cnt += 1;
}
i
})
.collect();
if cnt == 0 {
let mut i = LogEntry {
date: date.clone(),
cmd: LogCommand::Broken,
comment: String::new(),
value: None,
};
match tmp {
MeterAct::Command(x) => i.cmd = x,
MeterAct::Value(x) => i.value = x,
MeterAct::Comment(x) => i.comment = x,
}
meterlog.insert(i);
}
for i in validate_logdata(&meterlog, Some(&date)) {
println!("{}", i);
}
})
}