The personal anime and manga catalogue written in rust.
at master 17 kB view raw
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}