1pub mod cli;
2pub mod files;
3pub mod version;
4
5use clap::{
6 error::{Error, ErrorKind},
7 Command,
8};
9use core::fmt::Display;
10
11pub trait CommandRun {
12 fn run(&self, version: &mut version::VersionFile) -> VersionResult<()>;
13}
14
15#[derive(Debug)]
16pub enum VersionError {
17 IoError(std::io::Error),
18 TomlDeError(toml::de::Error),
19 TomlSerError(toml::ser::Error),
20 RegexError(regex::Error),
21 IncompleteCommand,
22 InvalidOperation,
23 NoCommand,
24 NoValue,
25 NoNegatives,
26}
27
28pub type VersionResult<T> = Result<T, VersionError>;
29
30impl Display for VersionError {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 write!(f, "{}", self.to_string())
33 }
34}
35
36impl VersionError {
37 pub fn to_string(&self) -> String {
38 match self {
39 VersionError::IoError(e) => format!("IO Error: {}", e),
40 VersionError::TomlDeError(e) => format!("TOML Deserialize Error: {}", e),
41 VersionError::TomlSerError(e) => format!("TOML Serialize Error: {}", e),
42 VersionError::RegexError(e) => format!("Regex Error: {}", e),
43 VersionError::IncompleteCommand => "Please specify a subcommand".to_string(),
44 VersionError::InvalidOperation => "Invalid operation".to_string(),
45 VersionError::NoCommand => "Unable to parse the command".to_string(),
46 VersionError::NoValue => "No value given".to_string(),
47 VersionError::NoNegatives => "Negative values are not allowed".to_string(),
48 }
49 }
50
51 pub fn error(&self, cmd: &mut Command) -> Error {
52 cmd.error(Into::<ErrorKind>::into(self), self.to_string())
53 }
54
55 pub fn terminate(&self, cmd: &mut Command) -> ! {
56 let err = self.error(cmd);
57 err.exit()
58 }
59}
60
61impl Into<ErrorKind> for &VersionError {
62 fn into(self) -> ErrorKind {
63 match self {
64 VersionError::IoError(_) => ErrorKind::Io,
65 VersionError::TomlDeError(_) => ErrorKind::Io,
66 VersionError::TomlSerError(_) => ErrorKind::Io,
67 VersionError::RegexError(_) => ErrorKind::ValueValidation,
68 VersionError::IncompleteCommand => ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
69 VersionError::InvalidOperation => ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
70 VersionError::NoCommand => ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
71 VersionError::NoValue => ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
72 VersionError::NoNegatives => ErrorKind::InvalidValue,
73 }
74 }
75}
76
77#[cfg(test)]
78mod test {
79
80 use super::*;
81 use clap::CommandFactory;
82 #[test]
83 fn no_cmd() {
84 let error = VersionError::NoCommand;
85 let displ = error.to_string();
86 assert!(displ.len() > 0);
87 let cmd = cli::Cli::command();
88 let err = error.error(&mut cmd.clone());
89 let render = format!("{}", err.render());
90 assert!(render.len() > 0);
91 assert_eq!(
92 Into::<ErrorKind>::into(&error),
93 ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
94 );
95 }
96 #[test]
97 fn io_error() {
98 let error =
99 VersionError::IoError(std::io::Error::new(std::io::ErrorKind::NotFound, "test"));
100 let displ = error.to_string();
101 assert!(displ.len() > 0);
102 let cmd = cli::Cli::command();
103 let err = error.error(&mut cmd.clone());
104 let render = format!("{}", err.render());
105 assert!(render.len() > 0);
106 assert_eq!(Into::<ErrorKind>::into(&error), ErrorKind::Io);
107 }
108 #[test]
109 fn regex_error() {
110 let error = VersionError::RegexError(regex::Error::Syntax("test".to_string()));
111 let displ = error.to_string();
112 assert!(displ.len() > 0);
113 let cmd = cli::Cli::command();
114 let err = error.error(&mut cmd.clone());
115 let render = format!("{}", err.render());
116 assert!(render.len() > 0);
117 assert_eq!(Into::<ErrorKind>::into(&error), ErrorKind::ValueValidation);
118 }
119 #[test]
120 fn toml_de_error() {
121 use serde::de::Error;
122 let error = VersionError::TomlDeError(toml::de::Error::missing_field("test"));
123 let displ = error.to_string();
124 assert!(displ.len() > 0);
125 let cmd = cli::Cli::command();
126 let err = error.error(&mut cmd.clone());
127 let render = format!("{}", err.render());
128 assert!(render.len() > 0);
129 assert_eq!(Into::<ErrorKind>::into(&error), ErrorKind::Io);
130 }
131 #[test]
132 fn toml_ser_error() {
133 use serde::ser::Error;
134 let error = VersionError::TomlSerError(toml::ser::Error::custom("test"));
135 let displ = error.to_string();
136 assert!(displ.len() > 0);
137 let cmd = cli::Cli::command();
138 let err = error.error(&mut cmd.clone());
139 let render = format!("{}", err.render());
140 assert!(render.len() > 0);
141 assert_eq!(Into::<ErrorKind>::into(&error), ErrorKind::Io);
142 }
143 #[test]
144 fn incomplete_command_error() {
145 let error = VersionError::IncompleteCommand;
146 let displ = error.to_string();
147 assert!(displ.len() > 0);
148 let cmd = cli::Cli::command();
149 let err = error.error(&mut cmd.clone());
150 let render = format!("{}", err.render());
151 assert!(render.len() > 0);
152 assert_eq!(
153 Into::<ErrorKind>::into(&error),
154 ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
155 );
156 }
157 #[test]
158 fn invalid_operation_error() {
159 let error = VersionError::InvalidOperation;
160 let displ = error.to_string();
161 assert!(displ.len() > 0);
162 let cmd = cli::Cli::command();
163 let err = error.error(&mut cmd.clone());
164 let render = format!("{}", err.render());
165 assert!(render.len() > 0);
166 assert_eq!(
167 Into::<ErrorKind>::into(&error),
168 ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
169 );
170 }
171 #[test]
172 fn no_value_error() {
173 let error = VersionError::NoValue;
174 let displ = error.to_string();
175 assert!(displ.len() > 0);
176 let cmd = cli::Cli::command();
177 let err = error.error(&mut cmd.clone());
178 let render = format!("{}", err.render());
179 assert!(render.len() > 0);
180 assert_eq!(
181 Into::<ErrorKind>::into(&error),
182 ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
183 );
184 }
185 #[test]
186 fn no_negatives_error() {
187 let error = VersionError::NoNegatives;
188 let displ = error.to_string();
189 assert!(displ.len() > 0);
190 let cmd = cli::Cli::command();
191 let err = error.error(&mut cmd.clone());
192 let render = format!("{}", err.render());
193 assert!(render.len() > 0);
194 assert_eq!(Into::<ErrorKind>::into(&error), ErrorKind::InvalidValue);
195 }
196}