diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 63 | ||||
-rw-r--r-- | src/output.rs | 9 |
2 files changed, 57 insertions, 15 deletions
diff --git a/src/main.rs b/src/main.rs index 6194ef0..d3839b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,27 +29,43 @@ use clap::{App, Arg, ArgMatches}; use api::{Api, Profession, Skill}; use bt::{BuildTemplate, ExtraData, Legend, TraitChoice, Traitline}; +/// The name of this application. +/// +/// This is used for example in the cache path. const APP_NAME: &str = "kondou"; +/// Return value indicating that a requested resource could not be found. #[derive(Debug, Clone)] -enum Error { - ProfessionNotFound(String), - SkillIdNotFound(u32), - SkillNotFound(String), - SpecializationNotFound(String), +enum NotFound { + /// Used when the requested profession can not be found. + /// + /// The argument is the requested profession. + Profession(String), + /// Used when a skill given by its ID could not be found. + /// + /// The argument is the requested skill id. + SkillId(u32), + /// Used when a skill given by its name could not be found. + /// + /// The argument is the requested skill name. + SkillName(String), + /// Used when a specialization could not be found. + /// + /// The argument is the requested specialization. + Specialization(String), } -impl fmt::Display for Error { +impl fmt::Display for NotFound { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Error::ProfessionNotFound(ref name) => { + NotFound::Profession(ref name) => { write!(f, "the profession '{}' could not be found", name) } - Error::SkillIdNotFound(id) => write!(f, "the skill with the ID '{}' was not found", id), - Error::SkillNotFound(ref 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) } - Error::SpecializationNotFound(ref name) => write!( + NotFound::Specialization(ref name) => write!( f, "the specialization with the name '{}' was not found", name @@ -58,22 +74,32 @@ impl fmt::Display for Error { } } -impl StdError for Error {} +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>>; +/// Find the profession by the given name. fn find_profession(api: &mut Api, name: &str) -> MainResult<Profession> { let profession_ids = api.get_profession_ids()?; let lower_name = name.to_lowercase(); let profession_id = profession_ids .iter() .find(|id| id.to_lowercase() == lower_name) - .ok_or_else(|| Error::ProfessionNotFound(name.to_owned()))? + .ok_or_else(|| NotFound::Profession(name.to_owned()))? .clone(); Ok(api.get_professions(&[profession_id])?.remove(0)) } +/// Resolve a skill. +/// +/// `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> { // Try it as an ID first let numeric = text.parse::<u32>(); @@ -82,7 +108,7 @@ fn resolve_skill(api: &mut Api, profession: &Profession, text: &str) -> MainResu if exists { return Ok(api.get_skills(&[num_id])?.remove(0)); } else { - return Err(Error::SkillIdNotFound(num_id).into()); + return Err(NotFound::SkillId(num_id).into()); } } @@ -93,9 +119,12 @@ fn resolve_skill(api: &mut Api, profession: &Profession, text: &str) -> MainResu all_skills .into_iter() .find(|s| s.name.to_lowercase().contains(&lower_text)) - .ok_or_else(|| Error::SkillNotFound(text.to_owned()).into()) + .ok_or_else(|| NotFound::SkillName(text.to_owned()).into()) } +/// 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> { let parts = text.split(':').collect::<Vec<_>>(); assert_eq!( @@ -110,7 +139,7 @@ fn resolve_traitline(api: &mut Api, profession: &Profession, text: &str) -> Main .get_specializations(&profession.specializations)? .into_iter() .find(|s| s.name.to_lowercase() == lower_name) - .ok_or_else(|| Error::SpecializationNotFound(name.to_owned()))?; + .ok_or_else(|| NotFound::Specialization(name.to_owned()))?; let mut choices = [TraitChoice::None; 3]; for (i, text_choice) in parts.iter().skip(1).enumerate() { @@ -122,6 +151,7 @@ fn resolve_traitline(api: &mut Api, profession: &Profession, text: &str) -> Main Ok((spec, choices)) } +/// Create the build template by manually combining the given skills/traitlines from the CLI. fn run_searching(api: &mut Api, matches: &ArgMatches) -> MainResult<BuildTemplate> { let requested_profession = matches .value_of("profession") @@ -192,11 +222,13 @@ fn run_searching(api: &mut Api, matches: &ArgMatches) -> MainResult<BuildTemplat Ok(build) } +/// Create the build template by parsing a chat link. fn run_chatlink(api: &mut Api, matches: &ArgMatches) -> MainResult<BuildTemplate> { let link = matches.value_of("chatlink").unwrap(); Ok(BuildTemplate::from_chatlink(api, link)?) } +/// Make sure a traitline is in the `"traitline:choice1:choice2:choice3"` format. fn validate_traitline_format(input: String) -> Result<(), String> { let parts = input.split(':').collect::<Vec<_>>(); if parts.len() != 4 { @@ -216,6 +248,7 @@ fn validate_traitline_format(input: String) -> Result<(), String> { Ok(()) } +/// Make sure a legend is valid. fn validate_legend(input: String) -> Result<(), String> { input .parse::<Legend>() diff --git a/src/output.rs b/src/output.rs index 2a2546f..f6e9bc4 100644 --- a/src/output.rs +++ b/src/output.rs @@ -1,3 +1,4 @@ +//! Functions for console output. use super::{ api, bt::{BuildTemplate, Traitline}, @@ -7,6 +8,9 @@ use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; const HEADER_COLOR: Color = Color::Cyan; +/// Format a skill for displaying. +/// +/// This is just the skill name (if given). fn format_skill(skill: &Option<api::Skill>) -> String { match *skill { None => "none".to_owned(), @@ -14,6 +18,7 @@ fn format_skill(skill: &Option<api::Skill>) -> String { } } +/// Format a traitline for displaying. fn format_traitline(traitline: &Option<Traitline>) -> String { match *traitline { None => "none".to_owned(), @@ -24,6 +29,7 @@ fn format_traitline(traitline: &Option<Traitline>) -> String { } } +/// Show a build template to the standard output stream. pub fn show_build_template(build: &BuildTemplate) -> io::Result<()> { let mut stdout = StandardStream::stdout(ColorChoice::Auto); let mut color_spec = ColorSpec::new(); @@ -58,6 +64,9 @@ pub fn show_build_template(build: &BuildTemplate) -> io::Result<()> { Ok(()) } +/// Show an error to the standard error stream. +/// +/// This will also show the chain of errors that lead up to this error, if available. pub fn show_error<E: Error + ?Sized>(error: &E) -> io::Result<()> { let mut error_color = ColorSpec::new(); error_color.set_fg(Some(Color::Red)); |