1use std::io::{BufRead, BufReader};
2use std::process::exit;
3
4use anyhow::Result;
5use duct::cmd;
6use flager::{Flag, Parser, new_flag};
7
8mod model;
9pub use model::Message;
10pub use model::Mua;
11use model::Pipe;
12pub use model::Record;
13
14pub struct Midnight;
16
17impl Midnight {
18 pub fn flags() -> Result<()> {
20 let parser = Parser::new();
21
22 let help_msg = "Print this help message then quit";
23 let queue_msg = "List items in queue (alias for 'mnq')";
24 let remove_msg = "Remove item from queue via job ID (alias for 'mnrm')";
25 let send_msg = "Send message in queue via message ID (alias for 'mnsend')";
26 let version_msg = "Print the version then quit";
27
28 let help_flag: Flag<bool> = new_flag!("-h", "--help").help(help_msg);
29 let queue_flag: Flag<bool> = new_flag!("-q", "--queue").help(queue_msg);
30 let remove_flag: Flag<String> = new_flag!("-r", "--remove").help(remove_msg);
31 let send_flag: Flag<bool> = new_flag!("-s", "--send").help(send_msg);
32 let version_flag: Flag<bool> = new_flag!("-V", "--version").help(version_msg);
33
34 if parser.parse(&help_flag).unwrap() == true {
35 println!("midnight");
36 println!("\tSend mail later via batch queueing");
37 println!();
38 println!("Usage:");
39 println!("\t<message> | midnight");
40 println!("\t<message> | mn");
41 println!("\tmnq");
42 println!("\tmnrm <jobid>");
43 println!("\t<message_id> | mnsend");
44 println!();
45 println!("Options:");
46 println!("\t-h, --help\t{}", help_msg);
47 println!("\t-q, --queue\t{}", queue_msg);
48 println!("\t-r, --remove\t{}", remove_msg);
49 println!("\t-s, --send\t{}", send_msg);
50 println!("\t-V, --version\t{}", version_msg);
51 exit(0);
52 }
53
54 if parser.parse(&queue_flag).unwrap() == true {
55 Self::list()?;
56 exit(0);
57 }
58
59 if let Some(jobid) = parser.parse(&remove_flag) {
60 if jobid.is_empty() {
61 eprintln!("Usage: midnight -r <jobid>");
62 exit(1);
63 }
64 Self::rm(&jobid)?;
65 exit(0);
66 }
67
68 if parser.parse(&send_flag).unwrap() == true {
69 Self::send()?;
70 exit(0);
71 }
72
73 if parser.parse(&version_flag).unwrap() == true {
74 println!("midnight v{}", env!("CARGO_PKG_VERSION"));
75 exit(0);
76 }
77
78 Ok(())
79 }
80
81 pub fn enqueue() -> Result<()> {
83 let raw = Pipe::drain()?;
85 let mut msg = Message::new(raw)?;
86
87 msg.at = Pipe::user(Some("What time? "))?;
89
90 let (jobid, at) = msg.enqueue()?;
92 msg.at = at;
93
94 let rcd = Record::new(jobid, msg);
96 rcd.write()?;
97
98 Ok(())
99 }
100
101 pub fn send() -> Result<()> {
103 let id = Pipe::drain()?;
104 let mut rcd = Record::find(id)?;
105 rcd.msg.send()
106 }
107
108 pub fn rm(jobid: &str) -> Result<()> {
110 cmd!("atrm", jobid).run()?;
111 Self::clean()?;
112 Ok(())
113 }
114
115 pub fn list() -> Result<()> {
117 Self::clean()?;
118 let rcds = Record::read()?;
119 for (i, rcd) in rcds.iter().enumerate() {
120 println!("{}", rcd);
121 if i < rcds.len() - 1 {
122 println!();
123 }
124 }
125 Ok(())
126 }
127
128 pub fn clean() -> Result<()> {
130 let cmd = cmd!("atq");
132 let reader = cmd.stderr_to_stdout().reader()?; let mut jobs = vec![];
135 let reader = BufReader::new(reader);
136 for line in reader.lines() {
137 let line = line?;
138 let jobid = String::from(line.split_whitespace().collect::<Vec<&str>>()[0]);
139 jobs.push(jobid);
140 }
141
142 let rcds = Record::read()?;
144
145 let scheduled = rcds
147 .iter()
148 .filter(|rcd| jobs.contains(&rcd.id))
149 .collect::<Vec<&Record>>();
150
151 Record::clear()?;
153
154 for rcd in scheduled {
156 rcd.write()?;
157 }
158
159 Ok(())
160 }
161}