aboutsummaryrefslogtreecommitdiff
path: root/src/cli.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli.rs')
-rw-r--r--src/cli.rs128
1 files changed, 128 insertions, 0 deletions
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}