aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml1
-rw-r--r--src/cli.rs128
3 files changed, 136 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index aa25765..5cd0345 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -127,6 +127,12 @@ dependencies = [
127] 127]
128 128
129[[package]] 129[[package]]
130name = "pico-args"
131version = "0.4.0"
132source = "registry+https://github.com/rust-lang/crates.io-index"
133checksum = "d70072c20945e1ab871c472a285fc772aefd4f5407723c206242f2c6f94595d6"
134
135[[package]]
130name = "radium" 136name = "radium"
131version = "0.6.2" 137version = "0.6.2"
132source = "registry+https://github.com/rust-lang/crates.io-index" 138source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -156,6 +162,7 @@ dependencies = [
156 "env_logger", 162 "env_logger",
157 "log", 163 "log",
158 "obi", 164 "obi",
165 "pico-args",
159 "sdl2", 166 "sdl2",
160] 167]
161 168
diff --git a/Cargo.toml b/Cargo.toml
index 277d6b0..ce5d614 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,3 +11,4 @@ sdl2 = {version = "0.34", features = ["ttf"]}
11obi = { git = "https://github.com/nerdypepper/obi" } 11obi = { git = "https://github.com/nerdypepper/obi" }
12env_logger = "0.8.3" 12env_logger = "0.8.3"
13log = "0.4.0" 13log = "0.4.0"
14pico-args = "0.4.0"
diff --git a/src/cli.rs b/src/cli.rs
new file mode 100644
index 0000000..1dbd098
--- /dev/null
+++ b/src/cli.rs
@@ -0,0 +1,128 @@
1use std::{
2 default::Default,
3 fmt,
4 path::{Path, PathBuf},
5};
6
7pub static HELP_TEXT: &'static str = "
8Usage
9-----
10
11slate new <FILE> -d WIDTHxHEIGHT
12
13Options
14-------
15
16 -h, --help Prints help information
17 -d, Specify dimensions for new file (default: 200x200)
18";
19
20#[derive(Debug)]
21pub enum CliError {
22 NoDimensions,
23 FileAlreadyExists,
24 FileDoesNotExist,
25 DimensionParseError,
26 NewRequiresFile,
27 InvalidSubCommand(String),
28 SubCommandParseError,
29}
30
31impl fmt::Display for CliError {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 match self {
34 Self::NoDimensions => write!(f, "no dimensions provided"),
35 Self::FileAlreadyExists => {
36 write!(f, "cannot initialize project, file already exists")
37 }
38 Self::FileDoesNotExist => {
39 write!(f, "cannot open existing project, file does not exist")
40 }
41 Self::DimensionParseError => write!(f, "invalid dimension string"),
42 Self::NewRequiresFile => write!(f, "new subcommand requires file name"),
43 Self::InvalidSubCommand(s) => write!(f, "invalid subcommand `{}`", s),
44 Self::SubCommandParseError => write!(f, "error parsing subcommand"),
45 }
46 }
47}
48
49impl std::error::Error for CliError {}
50
51#[derive(Debug)]
52pub enum Config {
53 NewProject {
54 file_name: Option<PathBuf>,
55 dimensions: (u32, u32),
56 },
57 ExistingProject {
58 file_name: PathBuf,
59 },
60 Help,
61}
62
63impl Default for Config {
64 fn default() -> Self {
65 Self::NewProject {
66 file_name: None,
67 dimensions: (200, 200),
68 }
69 }
70}
71
72pub fn parse_args() -> Result<Config, CliError> {
73 let mut args = pico_args::Arguments::from_env();
74
75 if args.contains(["-h", "--help"]) {
76 return Ok(Config::Help);
77 }
78
79 match args.subcommand() {
80 Ok(cmd) => match cmd.as_deref() {
81 Some("new") => new_project(&mut args),
82 Some(s) => {
83 if !Path::new(s).is_file() {
84 return Err(CliError::FileDoesNotExist);
85 }
86 return Ok(Config::ExistingProject {
87 file_name: PathBuf::from(s),
88 });
89 }
90 None => return Ok(Config::Help),
91 },
92 _ => Err(CliError::SubCommandParseError),
93 }
94}
95
96fn new_project(args: &mut pico_args::Arguments) -> Result<Config, CliError> {
97 let file_name = args.free_from_str();
98 if let Ok(f) = &file_name {
99 if Path::new(&f).is_file() {
100 return Err(CliError::FileAlreadyExists);
101 }
102 }
103 let dimensions = args
104 .opt_value_from_fn(["-d", "--dimensions"], parse_dimensions)
105 .map_err(|_| CliError::DimensionParseError)?
106 .unwrap_or((200, 200));
107 return Ok(Config::NewProject {
108 file_name: file_name.ok(),
109 dimensions,
110 });
111}
112
113fn parse_dimensions(input: &str) -> Result<(u32, u32), CliError> {
114 let dimensions: Vec<&str> = input.split('x').collect();
115 match &dimensions[..] {
116 [width, height] => {
117 return Ok((
118 width
119 .parse::<u32>()
120 .map_err(|_| CliError::DimensionParseError)?,
121 height
122 .parse::<u32>()
123 .map_err(|_| CliError::DimensionParseError)?,
124 ))
125 }
126 _ => return Err(CliError::DimensionParseError),
127 }
128}