diff --git a/Cargo.lock b/Cargo.lock index 31d9520..18f9e84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,55 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "autocfg" version = "1.3.0" @@ -26,6 +75,39 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "crossterm" version = "0.27.0" @@ -62,6 +144,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "libc" version = "0.2.159" @@ -93,7 +181,7 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -195,6 +283,7 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" name = "shdoku" version = "0.1.0" dependencies = [ + "clap", "crossterm", "rand", ] @@ -235,6 +324,12 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.77" @@ -252,6 +347,12 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -289,6 +390,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" diff --git a/Cargo.toml b/Cargo.toml index 5094f1c..b78d7a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ keywords = ["sudoku", "tui", "game", "crossterm"] categories = ["games", "mathematics", "science", "algorithms"] [dependencies] +clap = "4.5.20" crossterm = "0.27.0" rand = "0.8.5" diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..299fb51 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,13 @@ +use clap::{Arg, Command}; + +pub fn new() -> Command { + Command::new("shdoku") + .about("A terminal sudoku game with vim-like controls") + .arg( + Arg::new("difficulty") + .short('d') + .value_name("easy|mid|hard|expert|0..81") + .long("difficulty") + .help("Defined difficulty levels or a custom number of blank spaces"), + ) +} diff --git a/src/main.rs b/src/main.rs index 858caa4..dc1c254 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use crossterm::event::{poll, read, Event::*, KeyCode::*}; extern crate rand; +mod cli; mod state; mod sudoku; mod ui; @@ -11,8 +12,14 @@ use {state::*, sudoku::*, ui::*}; use std::{io, time::Duration}; fn main() { + let args = cli::new().get_matches(); + let difficulty = match args.get_one::("difficulty") { + None => Difficulty::Mid, + Some(d) => d.parse().unwrap(), + }; + let mut screen = Ui::init(io::stdout()); - let mut state = State::init(Difficulty::Mid); + let mut state = State::init(difficulty); screen.draw_static_elements().or_crash(); loop { diff --git a/src/sudoku/generator.rs b/src/sudoku/generator.rs index b35d797..2d100c2 100644 --- a/src/sudoku/generator.rs +++ b/src/sudoku/generator.rs @@ -32,6 +32,23 @@ impl Difficulty { } } +impl std::str::FromStr for Difficulty { + type Err = String; + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "easy" => Ok(Difficulty::Easy), + "mid" => Ok(Difficulty::Mid), + "hard" => Ok(Difficulty::Hard), + "expert" => Ok(Difficulty::Expert), + _ => Err(()), + } + .or_else(|_| match s.parse::() { + Ok(x) if x <= 81 => Ok(Difficulty::Custom(x)), + _ => Err(format!("Unable to parse difficulty: '{s}'...\nIs it a valid difficulty or number between 0 and 81?")) + }) + } +} + impl fmt::Display for Difficulty { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { match self {