1use colored::*;
2use std::fs::OpenOptions;
3use std::io::prelude::Read;
4use std::io::{BufReader, BufWriter, Write};
5use std::{env, path, process};
6
7pub struct Todo {
8 pub todo: Vec<String>,
9 pub home: String,
10}
11
12impl Todo {
13 pub fn new() -> Result<Self, String> {
14 let home: String = match env::consts::OS{
17 "linux" => {
18 env::var_os("XDG_DATA_HOME").unwrap_or(env::var_os("HOME").unwrap()).into_string().unwrap()
19 },
20
21 "windows" => {
22 env::var_os("USERPROFILE").unwrap().into_string().unwrap()
23 },
24
25 _ => { panic!() },
26 };
27
28 let todo = path::Path::new("TODO");
29
30 let todofile = OpenOptions::new()
31 .write(true)
32 .read(true)
33 .create(true)
34 .open(path::Path::new(&home).join(todo))
35 .expect("Couldn't open the todofile");
36
37 let mut buf_reader = BufReader::new(&todofile);
39
40 let mut contents = String::new();
42
43 buf_reader.read_to_string(&mut contents).unwrap();
45
46 let todo = contents.to_string().lines().map(str::to_string).collect();
48
49 Ok(Self { todo, home })
51 }
52
53 pub fn list(&self) -> () {
55 for (number, task) in self.todo.iter().enumerate() {
57 if task.len() > 5 {
58 let number = (number + 1).to_string().bold();
60
61 let symbol = &task[..4];
63 let task = &task[4..];
65
66 if symbol == "[*] " {
68 println!("{} {}", number, task.strikethrough());
71 } else if symbol == "[ ] " {
72 println!("{} {}", number, task);
75 }
76 }
77 }
78 }
79
80 pub fn raw(&self, arg: &[String]) {
82 if arg.len() > 1 {
83 eprintln!("todo raw takes only 1 argument, not {}", arg.len())
84 } else if arg.len() < 1 {
85 eprintln!("todo raw takes 1 argument (done/todo)");
86 } else {
87 for task in self.todo.iter() {
89 if task.len() > 5 {
90 let symbol = &task[..4];
92 let task = &task[4..];
94
95 if symbol == "[*] " && arg[0] == "done" {
97 println!("{}", task);
100 } else if symbol == "[ ] " && arg[0] == "todo" {
101 println!("{}", task);
105 }
106 }
107 }
108 }
109 }
110 pub fn add(&self, args: &[String]) {
112 if args.len() < 1 {
113 eprintln!("todo add takes at least 1 argument");
114 process::exit(1);
115 } else {
116 let todo = path::Path::new("TODO");
117
118 let todofile = OpenOptions::new()
120 .create(true) .append(true) .open(path::Path::new(&self.home).join(todo))
123 .expect("Couldn't open the todofile");
124
125 let mut buffer = BufWriter::new(todofile);
126 for arg in args {
127 if arg.trim().len() < 1 {
128 continue;
129 }
130
131 let line = format!("[ ] {}\n", arg);
132 buffer
133 .write_all(line.as_bytes())
134 .expect("unable to write data");
135 }
136
137 }
139 }
140
141 pub fn remove(&self, args: &[String]) {
143 if args.len() < 1 {
144 eprintln!("todo rm takes at least 1 argument");
145 process::exit(1);
146 } else {
147 let todo = path::Path::new("TODO");
148
149 let todofile = OpenOptions::new()
151 .write(true) .truncate(true) .open(path::Path::new(&self.home).join(todo))
154 .expect("Couldn't open the todo file");
155
156 let mut buffer = BufWriter::new(todofile);
157
158 for (pos, line) in self.todo.iter().enumerate() {
159 if args.contains(&"done".to_string()) && &line[..4] == "[*] " {
160 continue;
161 }
162 if args.contains(&(pos + 1).to_string()) {
163 continue;
164 }
165
166 let line = format!("{}\n", line);
167
168 buffer
169 .write_all(line.as_bytes())
170 .expect("unable to write data");
171 }
172 }
173 }
174
175 pub fn sort(&self) {
177 let newtodo: String;
179
180 let mut todo = String::new();
181 let mut done = String::new();
182
183 for line in self.todo.iter() {
184 if line.len() > 5 {
185 if &line[..4] == "[ ] " {
186 let line = format!("{}\n", line);
187 todo.push_str(&line);
188 } else if &line[..4] == "[*] " {
189 let line = format!("{}\n", line);
190 done.push_str(&line);
191 }
192 }
193 }
194
195 newtodo = format!("{}{}", &todo, &done);
196 let mut todofile = OpenOptions::new()
198 .write(true) .truncate(true) .open("TODO")
201 .expect("Couldn't open the todo file");
202
203 todofile
205 .write_all(newtodo.as_bytes())
206 .expect("Error while trying to save the todofile");
207 }
208
209 pub fn done(&self, args: &[String]) {
210 if args.len() < 1 {
211 eprintln!("todo done takes at least 1 argument");
212 process::exit(1);
213 } else {
214 let todofile = OpenOptions::new()
216 .write(true)
217 .open("TODO")
218 .expect("Couldn't open the todofile");
219 let mut buffer = BufWriter::new(todofile);
220
221 for (pos, line) in self.todo.iter().enumerate() {
222 if line.len() > 5 {
223 if args.contains(&(pos + 1).to_string()) {
224 if &line[..4] == "[ ] " {
225 let line = format!("[*] {}\n", &line[4..]);
226 buffer
227 .write_all(line.as_bytes())
228 .expect("unable to write data");
229 } else if &line[..4] == "[*] " {
230 let line = format!("[ ] {}\n", &line[4..]);
231 buffer
232 .write_all(line.as_bytes())
233 .expect("unable to write data");
234 }
235 } else {
236 if &line[..4] == "[ ] " || &line[..4] == "[*] " {
237 let line = format!("{}\n", line);
238 buffer
239 .write_all(line.as_bytes())
240 .expect("unable to write data");
241 }
242 }
243 }
244 }
245 }
246 }
247}
248
249const TODO_HELP: &str = "Usage: todo [COMMAND] [ARGUMENTS]
250Todo is a super fast and simple tasks organizer written in rust
251Example: todo list
252Available commands:
253 - add [TASK/s]
254 adds new task/s
255 Example: todo add \"buy carrots\"
256 - list
257 lists all tasks
258 Example: todo list
259 - done [INDEX]
260 marks task as done
261 Example: todo done 2 3 (marks second and third tasks as completed)
262 - rm [INDEX]
263 removes a task
264 Example: todo rm 4
265 - sort
266 sorts completed and uncompleted tasks
267 Example: todo sort
268 - raw [todo/done]
269 prints nothing but done/incompleted tasks in plain text, useful for scripting
270 Example: todo raw done
271";
272
273pub fn help() {
274 println!("{}", TODO_HELP);
276}