Compare commits
2
Commits
4d6ada502d
...
v0.9.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bc7ff2fde | ||
|
|
dfb2fee6ea |
Generated
+29
-4
@@ -1,5 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
@@ -83,15 +85,15 @@ name = "i3status-custom-pim"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"json",
|
||||
"serde_json",
|
||||
"structopt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "json"
|
||||
version = "0.12.4"
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
@@ -166,6 +168,29 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.185"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31"
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "i3status-custom-pim"
|
||||
description = "Generates JSON output for use with custom block of i3status-rust."
|
||||
version = "0.1.0"
|
||||
version = "0.9.1"
|
||||
authors = ["Martin Bley <dev@mb-oss.de>"]
|
||||
edition = "2018"
|
||||
|
||||
@@ -10,4 +10,4 @@ edition = "2018"
|
||||
[dependencies]
|
||||
structopt = "0.3"
|
||||
chrono = "0.4"
|
||||
json = "0.12.4"
|
||||
serde_json = "1.0"
|
||||
|
||||
+128
-128
@@ -1,6 +1,6 @@
|
||||
use chrono::{Local, NaiveDateTime, NaiveTime};
|
||||
use serde_json::json;
|
||||
use std::process;
|
||||
use std::process::Command;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
@@ -42,150 +42,150 @@ enum Status {
|
||||
Critical,
|
||||
}
|
||||
|
||||
fn get_status(events: Vec<NaiveDateTime>, w: i64, c: i64) -> Result<Status, String> {
|
||||
let now = Local::now();
|
||||
let mut event_remaining: i64 = 365 * 24 * 60;
|
||||
let mut state = Status::Idle;
|
||||
|
||||
for e in events.iter() {
|
||||
let diff = *e - now.naive_local();
|
||||
if (diff.num_minutes() < event_remaining) && (diff.num_minutes() >= 0) {
|
||||
event_remaining = diff.num_minutes()
|
||||
}
|
||||
|
||||
if event_remaining >= 0 {
|
||||
if event_remaining <= w {
|
||||
state = Status::Warning;
|
||||
}
|
||||
if event_remaining <= c {
|
||||
state = Status::Critical;
|
||||
}
|
||||
impl Status {
|
||||
fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Status::Idle => "Idle",
|
||||
Status::Warning => "Warning",
|
||||
Status::Critical => "Critical",
|
||||
}
|
||||
}
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
fn get_status(events: Vec<NaiveDateTime>, warn: i64, crit: i64) -> Status {
|
||||
let now = Local::now().naive_local();
|
||||
|
||||
let nearest = events
|
||||
.into_iter()
|
||||
.filter_map(|e| {
|
||||
let diff = e.signed_duration_since(now).num_minutes();
|
||||
if diff >= 0 {
|
||||
Some(diff)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.min()
|
||||
.unwrap_or(365 * 24 * 60);
|
||||
|
||||
if nearest <= crit {
|
||||
Status::Critical
|
||||
} else if nearest <= warn {
|
||||
Status::Warning
|
||||
} else {
|
||||
Status::Idle
|
||||
}
|
||||
}
|
||||
|
||||
fn run_command(cmd: &str, args: &[&str]) -> Result<String, String> {
|
||||
std::process::Command::new(cmd)
|
||||
.args(args)
|
||||
.output()
|
||||
.map_err(|e| format!("Error running {}: {}", cmd, e))
|
||||
.and_then(|output| {
|
||||
String::from_utf8(output.stdout)
|
||||
.map_err(|e| format!("Converting output failed: {}", e))
|
||||
})
|
||||
}
|
||||
|
||||
fn output_json(icon: &str, state: &Status, count: usize) {
|
||||
println!(
|
||||
"{}",
|
||||
json!({
|
||||
"icon": icon,
|
||||
"state": state.as_str(),
|
||||
"text": count.to_string()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Cli::from_args();
|
||||
|
||||
if (!args.khal && !args.todo) || (args.khal && args.todo) {
|
||||
eprintln!("Please provide either khal or todo flag.");
|
||||
let mode_count = (args.khal as i32) + (args.todo as i32);
|
||||
if mode_count != 1 {
|
||||
eprintln!("Please provide either --khal or --todo flag (not both, not none).");
|
||||
Cli::clap().print_help().unwrap();
|
||||
println!();
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
if args.khal {
|
||||
handle_khal(&args);
|
||||
} else {
|
||||
handle_todo(&args);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_khal(args: &Cli) {
|
||||
let now = Local::now().time();
|
||||
let from = now.format("%H:%M").to_string();
|
||||
|
||||
if args.khal {
|
||||
let cmd = Command::new("khal")
|
||||
.arg("list")
|
||||
.arg("-df")
|
||||
.arg("{name}")
|
||||
.arg("-f")
|
||||
.arg("{start-time}")
|
||||
.arg("--notstarted")
|
||||
.arg(&from)
|
||||
.arg("23:59")
|
||||
.output();
|
||||
|
||||
let stdout = match cmd {
|
||||
Ok(o) => o.stdout,
|
||||
Err(e) => {
|
||||
eprintln!("Error running khal: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let output = match String::from_utf8(stdout) {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
eprintln!("Converting output failed: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let mut out_iter = output.lines();
|
||||
|
||||
let dayline = match out_iter.nth(0) {
|
||||
Some(d) => d,
|
||||
None => { "None" }
|
||||
};
|
||||
|
||||
let today = Local::today();
|
||||
let mut events: Vec<NaiveDateTime> = Vec::new();
|
||||
if dayline.trim() == "Today" {
|
||||
for e in out_iter {
|
||||
let event_start = match NaiveTime::parse_from_str(e, "%H:%M") {
|
||||
Ok(s) => s,
|
||||
Err(_f) => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
events.push(today.and_time(event_start).unwrap().naive_local());
|
||||
}
|
||||
let output = match run_command(
|
||||
"khal",
|
||||
&["list", "-df", "{name}", "-f", "{start-time}", "--notstarted", &from, "23:59"],
|
||||
) {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
let count = events.len();
|
||||
let state = match get_status(events, args.threshold_warn, args.threshold_crit) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
println!(
|
||||
"{{ \"icon\": \"{}\", \"state\": \"{:?}\", \"text\": \"{}\" }}",
|
||||
args.icon, state, count
|
||||
);
|
||||
} else if args.todo {
|
||||
let cmd = Command::new("todo").arg("--porcelain").output();
|
||||
let mut lines = output.lines();
|
||||
let is_today = matches!(lines.next(), Some("Today"));
|
||||
|
||||
let stdout = match cmd {
|
||||
Ok(o) => o.stdout,
|
||||
Err(e) => {
|
||||
eprintln!("Error running todo: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let output = match String::from_utf8(stdout) {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
eprintln!("Converting output failed: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let parsed = match json::parse(&output) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
eprintln!("parsing JSON failed: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let count = parsed.len();
|
||||
//println!("{:#?}", count);
|
||||
|
||||
let mut tasks: Vec<NaiveDateTime> = Vec::new();
|
||||
for due in parsed.members() {
|
||||
tasks.push(NaiveDateTime::from_timestamp(
|
||||
due["due"].as_i64().unwrap(),
|
||||
0,
|
||||
));
|
||||
}
|
||||
let state = match get_status(tasks, args.threshold_warn, args.threshold_crit) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
println!(
|
||||
"{{ \"icon\": \"{}\", \"state\": \"{:?}\", \"text\": \"{}\" }}",
|
||||
args.icon, state, count
|
||||
);
|
||||
if !is_today {
|
||||
output_json(&args.icon, &Status::Idle, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
let today = Local::today();
|
||||
let events: Vec<NaiveDateTime> = lines
|
||||
.filter_map(|line| {
|
||||
NaiveTime::parse_from_str(line.trim(), "%H:%M")
|
||||
.ok()
|
||||
.and_then(|time| today.and_time(time))
|
||||
.map(|dt| dt.naive_local())
|
||||
})
|
||||
.collect();
|
||||
|
||||
let count = events.len();
|
||||
let state = get_status(events, args.threshold_warn, args.threshold_crit);
|
||||
|
||||
output_json(&args.icon, &state, count);
|
||||
}
|
||||
|
||||
fn handle_todo(args: &Cli) {
|
||||
let output = match run_command("todo", &["--porcelain"]) {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let parsed = match serde_json::from_str::<Vec<serde_json::Value>>(&output) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
eprintln!("Parsing JSON failed: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let count = parsed.len();
|
||||
|
||||
let tasks: Vec<NaiveDateTime> = parsed
|
||||
.iter()
|
||||
.filter_map(|due| {
|
||||
due.get("due")
|
||||
.and_then(|v| v.as_i64())
|
||||
.map(|ts| NaiveDateTime::from_timestamp(ts, 0))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let state = get_status(tasks, args.threshold_warn, args.threshold_crit);
|
||||
|
||||
output_json(&args.icon, &state, count);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user