diff --git a/Cargo.lock b/Cargo.lock index dc25849..647c206 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,54 +34,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" -[[package]] -name = "anstream" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" - -[[package]] -name = "anstyle-parse" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - [[package]] name = "anyhow" version = "1.0.82" @@ -111,6 +63,17 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.2.0" @@ -301,30 +264,28 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ - "clap_builder", -] - -[[package]] -name = "clap_builder" -version = "4.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" -dependencies = [ - "anstream", - "anstyle", + "atty", + "bitflags 1.3.2", "clap_lex", + "indexmap 1.9.3", + "once_cell", "strsim", + "termcolor", + "textwrap", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] [[package]] name = "color_quant" @@ -332,12 +293,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - [[package]] name = "constant_time_eq" version = "0.1.5" @@ -703,7 +658,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -720,6 +675,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.14.3" @@ -732,6 +693,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hmac" version = "0.12.1" @@ -861,6 +831,16 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.2.6" @@ -868,7 +848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.3", ] [[package]] @@ -1231,6 +1211,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + [[package]] name = "parking_lot" version = "0.12.1" @@ -1841,9 +1827,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.11.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" @@ -1920,6 +1906,21 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" + [[package]] name = "thiserror" version = "1.0.58" @@ -2051,7 +2052,7 @@ version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ - "indexmap", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", @@ -2127,12 +2128,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - [[package]] name = "v_frame" version = "0.3.8" diff --git a/Cargo.toml b/Cargo.toml index a9ed60f..45e35f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -clap = { version = "4.5.4", features = ["cargo"] } +clap = { version = "3.2.22", features = ["cargo"] } crossterm = "0.27.0" ctrlc = "3.4.4" dirs = "5.0.1" diff --git a/src/fetch.rs b/src/fetch.rs index c4ae267..ae966ed 100644 --- a/src/fetch.rs +++ b/src/fetch.rs @@ -82,11 +82,10 @@ pub fn fetch(extract_destination: &std::path::Path, verbose: bool) { }; // cleanup - // TODO: uncomment - // match cleanup() { - // Ok(_) => (), - // Err(e) => eprintln!("Error cleaning up: {}", e), - // }; + match cleanup() { + Ok(_) => (), + Err(e) => eprintln!("Error cleaning up: {}", e), + }; } fn create_working_directory() -> std::io::Result<()> { @@ -159,42 +158,6 @@ fn fetch_pokemon_json() -> Result<(), Box> { } } -#[derive(serde::Serialize, serde::Deserialize, Debug)] -struct Slug { - eng: String, -} - -#[derive(serde::Serialize, serde::Deserialize, Debug)] -struct Forms { - // ignoring actual details in the forms and just capturing form names -} - -#[derive(serde::Serialize, serde::Deserialize, Debug)] -struct Generation { - forms: std::collections::HashMap, -} - -#[derive(serde::Serialize, serde::Deserialize, Debug)] -struct Pokemon { - idx: String, - slug: Slug, - #[serde(rename = "gen-8")] - gen_8: Generation, -} - -#[derive(serde::Serialize, serde::Deserialize, Debug)] -struct PokemonCollection { - #[serde(flatten)] - entries: std::collections::HashMap, -} - -#[derive(serde::Serialize, Debug)] -struct ProcessedPokemon { - pokedex: String, - name: String, - forms: Vec, -} - fn process_pokemon_json( output_directory_path: &std::path::Path, ) -> Result<(), Box> { @@ -222,7 +185,7 @@ fn process_pokemon_json( fn read_pokemon_file( file_path: &std::path::Path, -) -> Result> { +) -> Result> { // open the file in read only mode let file = std::fs::File::open(file_path)?; let reader = std::io::BufReader::new(file); @@ -234,9 +197,9 @@ fn read_pokemon_file( } fn transform_pokemon_data( - pokemon_collection: &std::collections::HashMap, -) -> Vec { - let mut processed_pokemons: Vec = pokemon_collection + pokemon_collection: &std::collections::HashMap, +) -> Vec { + let mut processed_pokemons: Vec = pokemon_collection .iter() .map(|(_key, p)| { let mut forms = p @@ -257,7 +220,7 @@ fn transform_pokemon_data( forms.insert(0, "regular".to_string()); } - ProcessedPokemon { + crate::structs::Pokemon { // remove leading zeros from the pokedex number pokedex: p.idx.trim_start_matches('0').to_string(), // use the slug as the name diff --git a/src/lib.rs b/src/lib.rs index eec94af..9f3a3d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,3 +2,5 @@ pub mod constants; pub mod fetch; pub mod list; pub mod print; +pub mod structs; +pub mod validation; diff --git a/src/list.rs b/src/list.rs index 3d35914..48aff51 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,15 +1,3 @@ -#[derive(serde::Serialize, serde::Deserialize, Debug)] -struct Pokemon { - pokedex: String, - name: String, - forms: Vec, -} - -#[derive(serde::Serialize, serde::Deserialize, Debug)] -struct PokemonList { - pokemons: Vec, -} - pub fn print_pokemon_list() -> Result<(), serde_json::Error> { // open the file in read only mode with buffer let file = std::fs::File::open(crate::constants::DATA_DIRECTORY.join("pokemon.json")) @@ -17,7 +5,7 @@ pub fn print_pokemon_list() -> Result<(), serde_json::Error> { let reader = std::io::BufReader::new(file); // parse json into pokemonlist struct - let pokemon_list: Vec = serde_json::from_reader(reader)?; + let pokemon_list: Vec = serde_json::from_reader(reader)?; // iterate through the vector and print the pokedex and name for pokemon in pokemon_list { @@ -35,7 +23,8 @@ pub fn print_pokemon_forms(pokemon_name: &str) -> std::io::Result<()> { let reader = std::io::BufReader::new(file); // parse json into pokemonlist struct - let pokemon_list: Vec = serde_json::from_reader(reader).expect("Failed to parse JSON"); + let pokemon_list: Vec = + serde_json::from_reader(reader).expect("Failed to parse JSON"); // iterate through the list to find the specified pokemon and print its forms let mut found = false; @@ -54,6 +43,8 @@ pub fn print_pokemon_forms(pokemon_name: &str) -> std::io::Result<()> { if !found { println!("No Pokemon found with the name '{}'.", pokemon_name); println!("Hint: Do `rustmon list` to see all available Pokemon.") + } else { + println!("\nHint: Pass in `--form` when using subcommand `print` to see the specific form of a Pokemon!"); } return Ok(()); diff --git a/src/main.rs b/src/main.rs index d78d978..2a79970 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ - `no-title` - Do not print Pokemon name - `pokedex` - Print Pokemon by Pokedex number - `random` - Print a random Pokemon colorscript -- `shiny` - Print the shiny version of the colorscript +- `shiny` - Rate of printing the shiny version of the colorscript */ /// Pokemon Colorscripts written in Rust @@ -34,6 +34,11 @@ fn main() { // invoke bigchungus fetch function rustmon::fetch::fetch(extract_destination, verbose) } else if let Some(list_args) = args.subcommand_matches("list") { + // list + + // validate files first + rustmon::validation::validate_files(); + let pokemon_name: &String = list_args.get_one::("forms").unwrap(); if pokemon_name.is_empty() { // list @@ -56,22 +61,19 @@ fn main() { } } else if let Some(print_args) = args.subcommand_matches("print") { // print + + // validate files first + rustmon::validation::validate_files(); + // declare and define variables from arguments - let big: bool = print_args.get_flag("big"); - let id = print_args.get_one::("id").unwrap(); - let name = print_args.get_one::("name").unwrap(); + let big = print_args.get_flag("big"); + let pokedex: u16 = *print_args.get_one::("pokedex").unwrap(); + let name: &String = print_args.get_one::("name").unwrap(); let no_title: bool = print_args.get_flag("no-title"); let random: bool = print_args.get_flag("random"); - let shiny: bool = print_args.get_flag("shiny"); + let shiny: f32 = *print_args.get_one::("shiny").unwrap(); - println!("Big: {}", big); - println!("ID: {}", id); - println!("Name: {}", name); - println!("No title: {}", no_title); - println!("Random: {}", random); - println!("Shiny: {}", shiny); - - rustmon::print::print(); + rustmon::print::print(big, pokedex, name, no_title, random, shiny); } } @@ -111,7 +113,8 @@ fn argument_parser() -> clap::ArgMatches { .help("Print a list of forms of the specified Pokemon") .short('f') .long("forms") - .default_value(""), + .default_value("") + .hide_default_value(true), ) .after_help( "Tip: Use `grep` to search for a specific Pokemon form! @@ -124,21 +127,24 @@ For more advanced usage, use `less` or `more` to scroll through the list!", clap::Command::new("print") .about("Print a Pokemon colorscript") // print/big - .arg(clap::arg!(-b --big "Print a bigger version of the colorscript")) + .arg( + clap::Arg::new("big") + .help("Print a bigger version of the colorscript") + .short('b') + .long("big") + .action(clap::ArgAction::SetTrue), + ) // print/name .arg( clap::Arg::new("name") .help("Print Pokemon by name") .short('n') .long("name") + .default_value("") + .hide_default_value(true) .conflicts_with("pokedex") .conflicts_with("random"), ) - // print/random - .arg( - clap::arg!(-r --random "Print a random Pokemon colorscript") - .conflicts_with("name"), - ) // print/no-title .arg( clap::Arg::new("no-title") @@ -146,17 +152,37 @@ For more advanced usage, use `less` or `more` to scroll through the list!", .long("no-title") .action(clap::ArgAction::SetTrue), ) + // print/random + .arg( + clap::Arg::new("random") + .help("Print a random Pokemon colorscript") + .short('r') + .long("random") + .action(clap::ArgAction::SetTrue), + ) // print/pokedex .arg( clap::Arg::new("pokedex") .help("Print Pokemon by Pokedex number") .short('p') .long("pokedex") + .value_parser(clap::value_parser!(u16).range(0..)) + .default_value("0") + .hide_default_value(true) .conflicts_with("name") .conflicts_with("random"), ) // print/shiny - .arg(clap::arg!(-s --shiny "Print the shiny version of the colorscript")), + .arg( + clap::Arg::new("shiny") + .help( + "Rate of printing the shiny version of the colorscript (e.g. 0.10 for 10% chance)", + ) + .short('s') + .long("shiny") + .value_parser(clap::value_parser!(f32)) + .default_value("0.10"), + ), ) // finalize .get_matches(); diff --git a/src/print.rs b/src/print.rs index 63706fa..3234fb6 100644 --- a/src/print.rs +++ b/src/print.rs @@ -1,3 +1,49 @@ -pub fn print() { - println!("Hello, world!"); +use std::io::Read; + +pub fn print(big: bool, pokedex: u16, name: &String, no_title: bool, random: bool, shiny: f32) { + println!("Big: {}", big); + println!("pokedex: {}", pokedex); + println!("Name: {}", name); + println!("No title: {}", no_title); + println!("Random: {}", random); + println!("Shiny: {}", shiny); + + // decide which function to call + // random, by pokedex or by name + if random { + // random + println!("Random"); + } else if pokedex > 0 { + // by pokedex + println!("By pokedex"); + } else { + // by name + println!("By name"); + } + + match find_pokemon_by_pokedex(&pokedex.to_string()) { + Ok(pokemon_name) => println!("Found Pokémon: {}", pokemon_name), + Err(e) => eprintln!("Error: {}", e), + } +} + +fn find_pokemon_by_pokedex(pokedex_number: &str) -> Result> { + // read the file + let mut file = std::fs::File::open(crate::constants::DATA_DIRECTORY.join("pokemon.json"))?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + + // deserialize into the struct + let pokemons: Vec = serde_json::from_str(&contents)?; + + // iterate through the list to find the specified pokemon + for pokemon in pokemons { + if pokemon.pokedex == pokedex_number { + // if found then return the name + return Ok(pokemon.name); + } + } + + // if not found the return an error + return Err("Pokemon not found".into()); } diff --git a/src/structs.rs b/src/structs.rs new file mode 100644 index 0000000..b2cb7c5 --- /dev/null +++ b/src/structs.rs @@ -0,0 +1,40 @@ +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct Slug { + pub eng: String, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct Forms { + // ignoring actual details in the forms and just capturing form names +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct Generation { + pub forms: std::collections::HashMap, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct PokemonRaw { + pub idx: String, + pub slug: Slug, + #[serde(rename = "gen-8")] + pub gen_8: Generation, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct PokemonRawCollection { + #[serde(flatten)] + pub entries: std::collections::HashMap, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct Pokemon { + pub pokedex: String, + pub name: String, + pub forms: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct PokemonCollection { + pub pokemons: Vec, +} diff --git a/src/validation.rs b/src/validation.rs new file mode 100644 index 0000000..92cde4d --- /dev/null +++ b/src/validation.rs @@ -0,0 +1,64 @@ +pub fn validate_files() { + match validate_pokemon_json() { + Ok(_) => {} + Err(e) => { + eprintln!("Error: {}", e); + std::process::exit(1); + } + }; + + match validate_colorscripts_directory() { + Ok(_) => {} + Err(e) => { + eprintln!("Error: {}", e); + std::process::exit(1); + } + }; +} + +fn validate_pokemon_json() -> Result<(), Box> { + let file_path: std::path::PathBuf = crate::constants::DATA_DIRECTORY.join("pokemon.json"); + + // check if pokemon.json exists + if !crate::constants::DATA_DIRECTORY + .join("pokemon.json") + .exists() + { + return Err("`pokemon.json` does not exist. Please run the `fetch` subcommand.".into()); + } + + // open pokemon.json in read only mode + let file = std::fs::File::open(file_path)?; + let reader = std::io::BufReader::new(file); + + // try to parse the json into an array of pokemons struct + let pokemon_data: Result, serde_json::Error> = + serde_json::from_reader(reader); + + match pokemon_data { + Ok(_) => return Ok(()), + Err(_) => { + return Err(format!( + "JSON structure is not correct. Please run the `fetch` subcommand." + ) + .into()) + } + } +} + +fn validate_colorscripts_directory() -> Result<(), String> { + let base_path: std::path::PathBuf = crate::constants::DATA_DIRECTORY.join("colorscripts"); + + let subdirectories = ["big/regular", "big/shiny", "small/regular", "small/shiny"]; + + for subdirectory in subdirectories.iter() { + let path = base_path.join(subdirectory); + if !path.exists() { + return Err(format!( + "Directory does not exist. Please run the `fetch` subcommand." + )); + } + } + + return Ok(()); +}