1use std::env;
2use std::fs::File;
3use std::path::Path;
4use std::io::prelude::*;
5use json::JsonValue;
6use json::object;
7use edit;
8use std::string::ToString;
9use strum_macros::Display;
10use dirs;
11
12use std::{
13 io::Read,
14};
15
16#[derive(Display, Debug)]
17enum SERIESSTATUS {
18 PLANNED,
19 WATCHING,
20 COMPLETED,
21 HOLD,
22 DROPPED
23}
24
25impl SERIESSTATUS {
26 fn from_i8(value: i8) -> SERIESSTATUS {
27 match value {
28 0 => SERIESSTATUS::PLANNED,
29 1 => SERIESSTATUS::WATCHING,
30 2 => SERIESSTATUS::COMPLETED,
31 3 => SERIESSTATUS::HOLD,
32 4 => SERIESSTATUS::DROPPED,
33 _ => panic!("Unknown value: {}", value),
34 }
35 }
36}
37
38#[derive(Display, Debug)]
39enum MOVIESTATUS {
40 PLANNED,
41 COMPLETED
42}
43
44impl MOVIESTATUS {
45 fn from_i8(value: i8) -> MOVIESTATUS {
46 match value {
47 0 => MOVIESTATUS::PLANNED,
48 1 => MOVIESTATUS::COMPLETED,
49 _ => panic!("Unknown value: {}", value),
50 }
51 }
52}
53
54#[derive(Display, Debug)]
55enum MANGASTATUS {
56 PLANNED,
57 READING,
58 COMPLETED,
59 HOLD,
60 DROPPED
61}
62
63impl MANGASTATUS {
64 fn from_i8(value: i8) -> MANGASTATUS {
65 match value {
66 0 => MANGASTATUS::PLANNED,
67 1 => MANGASTATUS::READING,
68 2 => MANGASTATUS::COMPLETED,
69 3 => MANGASTATUS::HOLD,
70 4 => MANGASTATUS::DROPPED,
71 _ => panic!("Unknown value: {}", value),
72 }
73 }
74}
75
76fn print_help() {
77 println!("SERIES STATUS:");
78 println!(" --planned, -p Planning to watch");
79 println!(" --watching, -w Currently watching");
80 println!(" --completed, -c Completed");
81 println!(" --hold, -h On Hold");
82 println!(" --dropped, -d Dropped");
83 println!("");
84 println!("MOVIE STATUS:");
85 println!(" --planned, -p Planning to watch");
86 println!(" --completed, -c Completed");
87 println!("");
88 println!("MANGA STATUS:");
89 println!(" --planned, -p Planning to watch");
90 println!(" --reading, -r Reading");
91 println!(" --completed, -c Completed");
92 println!(" --hold, -h On Hold");
93 println!(" --dropped, -d Dropped");
94 println!("");
95 println!("CATEGORIES:");
96 println!(" --series, -s Series");
97 println!(" --movies, --movie, -mo Movies");
98 println!(" --manga, -ma Manga");
99 println!("");
100 println!("SORT:");
101 println!(" --alphabetical, -a Sort by alphabetical order.");
102 println!(" --rating, -r Sort by rating.");
103 println!(" --ratingdesc, -rd Sort by rating descending.");
104 println!("");
105 println!("STATISTIC:");
106 println!(" --completed, -c Number of completed items.");
107 println!(" --total, -t Total items in a category.");
108 println!("");
109 println!("OPTIONS:");
110 println!(" --get <CATEGORY> <NAME> Get the information for a specific anime or manga in the catalogue.");
111 println!(" --getstat <CATEGORY/--all> <STATISTIC> Get a statistic under `STATISTIC` for a specific category or everything.");
112 println!("x--getsort <CATEGORY/--all> <SORT> <?LENGTH/--all> Get a specific category or everything sorted by any of the methods under `SORT`.");
113 println!(" --add <CATEGORY> <NAME> <?RATING> <?STATUS> Add anime or manga to database with optional rating and status.");
114 println!(" --edit <CATEGORY> <NAME> Edit the notes for a specific anime or manga.");
115 println!(" --editjson <CATEGORY> <NAME> Edit the json for a specific anime or manga.");
116 println!();
117}
118
119fn main() -> std::io::Result<()> {
120 let args: Vec<String> = env::args().collect();
121 //println!("{}", args.len());
122
123 let mut data: JsonValue;
124
125 let mut path: String;
126
127 if cfg!(windows) {
128 path = dirs::home_dir().unwrap().into_os_string().into_string().unwrap();
129 path.push_str("\\anilog.json");
130 } else if cfg!(unix) {
131 path = dirs::home_dir().unwrap().into_os_string().into_string().unwrap();
132 path.push_str("/anilog.json");
133 } else {
134 path = String::from("anilog.json");
135 }
136
137 if Path::new(&path).exists() {
138 let mut file = File::open(&path)?;
139 let mut contents = String::new();
140 file.read_to_string(&mut contents)?;
141
142 data = json::parse(&contents).unwrap();
143 } else {
144 data = object! {
145 series: {},
146 movies: {},
147 manga: {},
148 };
149 }
150
151 //println!("{:?}", args);
152
153 if args.len() < 2 {
154 print_help();
155 return Ok(());
156 }
157
158 if &args[1] == "--help" || &args[1] == "-h" {
159 print_help();
160 return Ok(());
161 }
162
163 if args[1] == "--add" {
164
165 let category: String;
166
167 if args[2] == "-s" || args[2] == "--series" {
168 category = String::from("series");
169 } else if args[2] == "-mo" || args[2] == "--movie" || args[2] == "--movies" {
170 category = String::from("movies");
171 } else if args[2] == "-ma" || args[2] == "--manga" {
172 category = String::from("manga");
173 } else {
174 category = String::from("series");
175 }
176
177 if args.len() == 4 {
178 data[category][&args[3]] = object!{
179 name: String::from(&args[3]),
180 rating: 0,
181 status: 0,
182 notes: ""
183 };
184 } else if args.len() == 5 {
185
186 if String::from(&args[4]).parse::<i8>().unwrap() > 101 || String::from(&args[4]).parse::<i8>().unwrap() < 0 {
187 println!("Ratings must be between 0 and 100!");
188 return Ok(());
189 }
190
191 data[category][&args[3]] = object!{
192 name: String::from(&args[3]),
193 rating: String::from(&args[4]).parse::<i8>().unwrap(),
194 status: 0,
195 notes: ""
196 };
197 } else if args.len() == 6 {
198
199 let mut status: i8 = 0;
200 let status_arg = &args[5].to_lowercase();
201
202 if category == "series" {
203 if status_arg == "planned" || status_arg == "p" {
204 status = SERIESSTATUS::PLANNED as i8;
205 } else if status_arg == "watching" || status_arg == "w" {
206 status = SERIESSTATUS::WATCHING as i8;
207 } else if status_arg == "completed" || status_arg == "c" {
208 status = SERIESSTATUS::COMPLETED as i8;
209 } else if status_arg == "hold" || status_arg == "h" {
210 status = SERIESSTATUS::HOLD as i8;
211 } else if status_arg == "dropped" || status_arg == "d" {
212 status = SERIESSTATUS::DROPPED as i8;
213 }
214 } else if category == "movies" {
215 if status_arg == "planned" || status_arg == "p" {
216 status = MOVIESTATUS::PLANNED as i8;
217 } else if status_arg == "completed" || status_arg == "c" {
218 status = MOVIESTATUS::COMPLETED as i8;
219 }
220 } else if category == "manga" {
221 if status_arg == "planned" || status_arg == "p" {
222 status = MANGASTATUS::PLANNED as i8;
223 } else if status_arg == "reading" || status_arg == "r" {
224 status = MANGASTATUS::READING as i8;
225 } else if status_arg == "completed" || status_arg == "c" {
226 status = MANGASTATUS::COMPLETED as i8;
227 } else if status_arg == "hold" || status_arg == "h" {
228 status = MANGASTATUS::HOLD as i8;
229 } else if status_arg == "dropped" || status_arg == "d" {
230 status = MANGASTATUS::DROPPED as i8;
231 }
232 }
233
234 if String::from(&args[4]).parse::<i8>().unwrap() > 101 || String::from(&args[4]).parse::<i8>().unwrap() < 0 {
235 println!("Ratings must be between 0 and 100!");
236 return Ok(());
237 }
238
239 data[category][&args[3]] = object!{
240 name: String::from(&args[3]),
241 rating: String::from(&args[4]).parse::<i8>().unwrap(),
242 status: status,
243 notes: ""
244 };
245 } else {
246 print_help();
247 return Ok(());
248 }
249 } else if args[1] == "--get" {
250 if args.len() != 4 {
251 print_help();
252 println!("Invalid number of arguments!");
253 return Ok(());
254 }
255
256 if args[2] == "--series" || args[2] == "-s" {
257 println!("Name:");
258 println!(" {}", data["series"][&args[3]]["name"]);
259 println!("Rating:");
260 println!(" {}", data["series"][&args[3]]["rating"]);
261 println!("Status:");
262 let status_int = String::from(&json::stringify(data["series"][&args[3]]["status"].clone())).parse::<i8>().unwrap();
263 println!(" {}", SERIESSTATUS::from_i8(status_int).to_string());
264 println!("Notes:");
265 println!(" {}", data["series"][&args[3]]["notes"]);
266 } else if args[2] == "-mo" || args[2] == "--movie" || args[2] == "--movies" {
267 println!("Name:");
268 println!(" {}", data["movies"][&args[3]]["name"]);
269 println!("Rating:");
270 println!(" {}", data["movies"][&args[3]]["rating"]);
271 println!("Status:");
272 let status_int = String::from(&json::stringify(data["movies"][&args[3]]["status"].clone())).parse::<i8>().unwrap();
273 println!(" {}", MOVIESTATUS::from_i8(status_int).to_string());
274 println!("Notes:");
275 println!(" {}", data["movies"][&args[3]]["notes"]);
276 } else if args[2] == "-ma" || args[2] == "--manga" {
277 println!("Name:");
278 println!(" {}", data["manga"][&args[3]]["name"]);
279 println!("Rating:");
280 println!(" {}", data["manga"][&args[3]]["rating"]);
281 println!("Status:");
282 let status_int = String::from(&json::stringify(data["manga"][&args[3]]["status"].clone())).parse::<i8>().unwrap();
283 println!(" {}", MANGASTATUS::from_i8(status_int).to_string());
284 println!("Notes:");
285 println!(" {}", data["manga"][&args[3]]["notes"]);
286 }
287 } else if args[1] == "--getstat" {
288 if args.len() != 4 {
289 print_help();
290 println!("Invalid number of arguments!");
291 return Ok(());
292 }
293
294 if args[2] == "--series" || args[2] == "-s" {
295 if args[3] == "--completed" || args[3] == "-c" {
296 println!("Indexing Series...");
297 let mut completed: i16 = 0;
298 for i in data["series"].entries() {
299 if i.1["status"] == SERIESSTATUS::COMPLETED as i8 {
300 completed += 1;
301 }
302 }
303 println!("You have completed {} series.", completed);
304 } else if args[3] == "--total" || args[3] == "-t" {
305 println!("You have {} series in the catalogue.", data["series"].entries().len());
306 }
307 } else if args[2] == "-mo" || args[2] == "--movie" || args[2] == "--movies" {
308 if args[3] == "--completed" || args[3] == "-c" {
309 println!("Indexing Movies...");
310 let mut completed: i16 = 0;
311 for i in data["movies"].entries() {
312 if i.1["status"] == MOVIESTATUS::COMPLETED as i8 {
313 completed += 1;
314 }
315 }
316 println!("You have completed {} movies.", completed);
317 } else if args[3] == "--total" || args[3] == "-t" {
318 println!("You have {} movies in the catalogue.", data["movies"].entries().len());
319 }
320 } else if args[2] == "-ma" || args[2] == "--manga" {
321 if args[3] == "--completed" || args[3] == "-c" {
322 println!("Indexing Mangas...");
323 let mut completed: i16 = 0;
324 for i in data["manga"].entries() {
325 if i.1["status"] == MANGASTATUS::COMPLETED as i8 {
326 completed += 1;
327 }
328 }
329 println!("You have completed {} mangas.", completed);
330 } else if args[3] == "--total" || args[3] == "-t" {
331 println!("You have {} mangas in the catalogue.", data["manga"].entries().len());
332 }
333 } else if args[2] == "--all" {
334 if args[3] == "--completed" || args[3] == "-c" {
335 println!("Indexing Series...");
336 let mut completed: i16 = 0;
337 for i in data["series"].entries() {
338 if i.1["status"] == SERIESSTATUS::COMPLETED as i8 {
339 completed += 1;
340 }
341 }
342 println!("Indexing Movies...");
343 for i in data["movies"].entries() {
344 if i.1["status"] == MOVIESTATUS::COMPLETED as i8 {
345 completed += 1;
346 }
347 }
348 println!("Indexing Mangas...");
349 for i in data["manga"].entries() {
350 if i.1["status"] == MANGASTATUS::COMPLETED as i8 {
351 completed += 1;
352 }
353 }
354 println!("You have completed {} animes and mangas.", completed);
355 } else if args[3] == "--total" || args[3] == "-t" {
356 println!("You have {} animes and mangas in the catalogue.", data["series"].entries().len() + data["movies"].entries().len() + data["manga"].entries().len());
357 }
358 }
359 } else if args[1] == "--getsort" {
360 println!("This is not implemented yet.");
361 } else if args[1] == "--edit" {
362 if args.len() == 4 {
363 let category: String;
364
365 if args[2] == "-s" || args[2] == "--series" {
366 category = String::from("series");
367 } else if args[2] == "-mo" || args[2] == "--movie" || args[2] == "--movies" {
368 category = String::from("movies");
369 } else if args[2] == "-ma" || args[2] == "--manga" {
370 category = String::from("manga");
371 } else {
372 category = String::from("series");
373 }
374
375 if data[&category].contains(args[3].clone()) {
376 println!("The specified item is not present!");
377 return Ok(());
378 }
379
380 let template = json::stringify(data[&category][&args[3]]["notes"].clone());
381 let edited = edit::edit(template)?;
382
383 data[&category][&args[3]]["notes"] = json::parse(&edited).unwrap();
384 } else {
385 print_help();
386 println!("Invalid number of arguments!");
387 return Ok(());
388 }
389 } else if args[1] == "--editjson" {
390 if args.len() == 4 {
391 let category: String;
392
393 if args[2] == "-s" || args[2] == "--series" {
394 category = String::from("series");
395 } else if args[2] == "-mo" || args[2] == "--movie" || args[2] == "--movies" {
396 category = String::from("movies");
397 } else if args[2] == "-ma" || args[2] == "--manga" {
398 category = String::from("manga");
399 } else {
400 category = String::from("series");
401 }
402
403 if data[&category].contains(args[3].clone()) {
404 println!("The specified item is not present!");
405 return Ok(());
406 }
407
408 let template = json::stringify(data[&category][&args[3]].clone());
409 let edited = edit::edit(template)?;
410
411 data[&category][&args[3]] = json::parse(&edited).unwrap();
412 } else {
413 print_help();
414 return Ok(());
415 }
416 }
417
418 // println!("{:#}", data);
419
420 let mut path: String;
421
422 if cfg!(windows) {
423 path = dirs::home_dir().unwrap().into_os_string().into_string().unwrap();
424 path.push_str("\\anilog.json");
425 } else if cfg!(unix) {
426 path = dirs::home_dir().unwrap().into_os_string().into_string().unwrap();
427 path.push_str("/anilog.json");
428 } else {
429 path = String::from("anilog.json");
430 }
431
432 let mut file = File::create(&path)?;
433 file.write_all(json::stringify(data).as_bytes())?;
434 Ok(())
435}