diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 63 |
1 files changed, 48 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>() |