aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs68
1 files changed, 15 insertions, 53 deletions
diff --git a/src/main.rs b/src/main.rs
index 98dbc1e..5b11a40 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,18 +1,3 @@
-use std::error::Error as StdError;
-use std::fmt;
-
-macro_rules! error_froms {
- ($tname:ty, $($ename:ident : $fty:ty => $res:expr,)*) => {
- $(
- impl From<$fty> for $tname {
- fn from($ename: $fty) -> Self {
- $res
- }
- }
- )*
- }
-}
-
mod api;
mod bt;
mod cache;
@@ -20,6 +5,8 @@ mod output;
mod render;
mod useropts;
+use anyhow::{Context, Result};
+use thiserror::Error;
use clap::{App, Arg, ArgMatches};
use api::{Api, Profession, Skill};
@@ -32,54 +19,30 @@ use render::RenderError;
const APP_NAME: &str = "kondou";
/// Return value indicating that a requested resource could not be found.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Error)]
enum NotFound {
/// Used when the requested profession can not be found.
///
/// The argument is the requested profession.
+ #[error("The profession '{0}' could not be found")]
Profession(String),
/// Used when a skill given by its ID could not be found.
///
/// The argument is the requested skill id.
+ #[error("The skill with ID {0} could not be found")]
SkillId(u32),
/// Used when a skill given by its name could not be found.
///
/// The argument is the requested skill name.
+ #[error("The skill named '{0}' could not be found")]
SkillName(String),
/// Used when a specialization could not be found.
///
/// The argument is the requested specialization.
+ #[error("The specialization named '{0}' could not be found")]
Specialization(String),
}
-impl fmt::Display for NotFound {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- NotFound::Profession(ref name) => {
- write!(f, "the profession '{}' could not be found", name)
- }
- NotFound::SkillId(id) => write!(f, "the skill with the ID '{}' was not found", id),
- NotFound::SkillName(ref name) => {
- write!(f, "the skill with the name '{}' was not found", name)
- }
- NotFound::Specialization(ref name) => write!(
- f,
- "the specialization with the name '{}' was not found",
- name
- ),
- }
- }
-}
-
-impl StdError for NotFound {}
-
-/// A top-level result.
-///
-/// We use a dynamic error dispatch here for two reasons:
-/// 1. The only thing that we really do here is displaying the error to the user.
-/// 2. We don't want yet another error kind with a lot of kinds.
-type MainResult<T> = Result<T, Box<dyn StdError>>;
-
/// A trait for containers that only contain a single item.
trait SingleContainer<T> {
/// Extract the single element by consuming the container.
@@ -94,7 +57,7 @@ impl<T> SingleContainer<T> for Vec<T> {
}
/// Find the profession by the given name.
-fn find_profession(api: &mut Api, name: &str) -> MainResult<Profession> {
+fn find_profession(api: &mut Api, name: &str) -> Result<Profession> {
let profession_ids = api.get_profession_ids()?;
let lower_name = name.to_lowercase();
let profession_id = profession_ids
@@ -110,7 +73,7 @@ fn find_profession(api: &mut Api, name: &str) -> MainResult<Profession> {
///
/// `text` can either be a skill name, in which case all skills of the profession will be searched.
/// Alternatively, it can also be a numeric ID, in which case it will be requested directly.
-fn resolve_skill(api: &mut Api, profession: &Profession, text: &str) -> MainResult<Skill> {
+fn resolve_skill(api: &mut Api, profession: &Profession, text: &str) -> Result<Skill> {
// Try it as an ID first
let numeric = text.parse::<u32>();
if let Ok(num_id) = numeric {
@@ -135,7 +98,7 @@ fn resolve_skill(api: &mut Api, profession: &Profession, text: &str) -> MainResu
/// Resolve a traitline.
///
/// `text` must be in the `"name:choice1:choice2:choice3"` format.
-fn resolve_traitline(api: &mut Api, profession: &Profession, text: &str) -> MainResult<Traitline> {
+fn resolve_traitline(api: &mut Api, profession: &Profession, text: &str) -> Result<Traitline> {
let parts = text.split(':').collect::<Vec<_>>();
assert_eq!(
parts.len(),
@@ -162,7 +125,7 @@ fn resolve_traitline(api: &mut Api, profession: &Profession, text: &str) -> Main
}
/// Create the build template by manually combining the given skills/traitlines from the CLI.
-fn run_searching(api: &mut Api, matches: &ArgMatches) -> MainResult<BuildTemplate> {
+fn run_searching(api: &mut Api, matches: &ArgMatches) -> Result<BuildTemplate> {
let requested_profession = matches
.value_of("profession")
.expect("clap handles missing argument");
@@ -229,7 +192,7 @@ fn run_searching(api: &mut Api, matches: &ArgMatches) -> MainResult<BuildTemplat
}
/// Create the build template by parsing a chat link.
-fn run_chatlink(api: &mut Api, matches: &ArgMatches) -> MainResult<BuildTemplate> {
+fn run_chatlink(api: &mut Api, matches: &ArgMatches) -> Result<BuildTemplate> {
let link = matches.value_of("chatlink").unwrap();
Ok(BuildTemplate::from_chatlink(api, link)?)
}
@@ -262,7 +225,7 @@ fn validate_legend(input: String) -> Result<(), String> {
.map_err(|_| "invalid legend name".to_owned())
}
-fn run() -> MainResult<()> {
+fn run() -> Result<()> {
let matches = App::new(APP_NAME)
.version("0.1")
.author("Peter Parker IV")
@@ -375,8 +338,7 @@ fn run() -> MainResult<()> {
}
Err(RenderError::EmptyBuild) => (),
Err(err) => {
- eprintln!("Image could not be rendered:");
- output::show_error(&err)?;
+ return Err(err).context("Image could not be rendered");
}
}
@@ -386,7 +348,7 @@ fn run() -> MainResult<()> {
fn main() {
let result = run();
if let Err(e) = result {
- output::show_error(e.as_ref()).expect("Error while displaying error");
+ output::show_error(e).expect("Error while displaying error");
std::process::exit(1);
}
}