feat: add jobs

This commit is contained in:
Anna 2018-09-04 04:03:42 -04:00
parent 76066c56f6
commit 9a8588b283
2 changed files with 115 additions and 2 deletions

View File

@ -6,16 +6,21 @@ use crate::{
CityState,
Gender,
GrandCompanyInfo,
Job,
JobInfo,
}
};
use ffxiv_types::{World, Race, Clan, Guardian};
use scraper::Html;
use scraper::{Html, ElementRef};
use url::Url;
use std::str::FromStr;
use std::{
collections::BTreeMap,
str::FromStr,
};
selectors!(
PROFILE_FACE => ".frame__chara__face > img";
@ -30,6 +35,11 @@ selectors!(
PROFILE_GRAND_COMPANY => "div.character-block:nth-of-type(4) > .character-block__box > .character-block__name";
PROFILE_FREE_COMPANY => ".character__freecompany__name > h4 > a";
PROFILE_TEXT => ".character__selfintroduction";
PROFILE_CLASS => "ul.character__job > li";
CLASS_NAME => ".character__job__name";
CLASS_LEVEL => ".character__job__level";
CLASS_EXP => ".character__job__exp";
);
pub fn parse(id: u64, html: &str) -> Result<Character> {
@ -51,6 +61,8 @@ pub fn parse(id: u64, html: &str) -> Result<Character> {
let profile_text = plain_parse(&html, &*PROFILE_TEXT)?.trim().to_string();
let jobs = parse_jobs(&html)?;
let face = parse_face(&html)?;
let portrait = parse_portrait(&html)?;
@ -68,6 +80,7 @@ pub fn parse(id: u64, html: &str) -> Result<Character> {
grand_company,
free_company_id,
profile_text,
jobs,
face,
portrait,
})
@ -181,3 +194,50 @@ fn parse_portrait(html: &Html) -> Result<Url> {
.ok_or_else(|| Error::invalid_content("img src attribute", None))
.and_then(|x| Url::parse(x).map_err(Error::InvalidUrl))
}
fn parse_jobs(html: &Html) -> Result<BTreeMap<Job, JobInfo>> {
let mut jobs = BTreeMap::new();
for job in html.select(&*PROFILE_CLASS) {
let (job, info) = parse_job(job)?;
jobs.insert(job, info);
}
Ok(jobs)
}
fn parse_job<'a>(elem: ElementRef<'a>) -> Result<(Job, JobInfo)> {
let job = crate::logic::plain_parse_elem(elem, &*CLASS_NAME)
.and_then(|x| Job::parse(&x).ok_or_else(|| Error::invalid_content("valid job", Some(&x))))?;
let level_str = crate::logic::plain_parse_elem(elem, &*CLASS_LEVEL)?;
let level: Option<u8> = match level_str.as_str() {
"-" => None,
x => Some(x.parse().map_err(Error::InvalidNumber)?),
};
let exp_str = crate::logic::plain_parse_elem(elem, &*CLASS_EXP)?;
let mut exp_split = exp_str.split(" / ");
let first_exp = exp_split.next().unwrap(); // must have first element
let experience: Option<u64> = match first_exp {
"-" | "--" => None,
x => Some(x.parse().map_err(Error::InvalidNumber)?),
};
let second_exp = exp_split
.next()
.ok_or_else(|| Error::invalid_content("experience split by ` / `", Some(&exp_str)))?;
let next_level_experience: Option<u64> = match second_exp {
"-" | "--" => None,
x => Some(x.parse().map_err(Error::InvalidNumber)?),
};
let info = JobInfo {
level,
experience,
next_level_experience,
};
Ok((job, info))
}

View File

@ -4,6 +4,8 @@ use ffxiv_types::{World, Race, Clan, Guardian};
use url::Url;
use std::collections::BTreeMap;
#[derive(Debug, Serialize, Deserialize)]
pub struct Character {
pub id: u64,
@ -24,6 +26,8 @@ pub struct Character {
pub profile_text: String,
pub jobs: BTreeMap<Job, JobInfo>,
#[serde(with = "url_serde")]
pub face: Url,
#[serde(with = "url_serde")]
@ -36,6 +40,13 @@ pub struct GrandCompanyInfo {
pub rank: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct JobInfo {
pub level: Option<u8>,
pub experience: Option<u64>,
pub next_level_experience: Option<u64>,
}
ffxiv_enum!(Gender {
Male => "",
Female => "",
@ -46,3 +57,45 @@ ffxiv_enum!(CityState {
LimsaLominsa => "Limsa Lominsa",
UlDah => "Ul'dah",
});
ffxiv_enum!(
#[derive(PartialEq, Eq, PartialOrd, Ord)]
Job {
Gladiator => "Gladiator",
Paladin => "Paladin",
Marauder => "Marauder",
Warrior => "Warrior",
DarkKnight => "Dark Knight",
Conjurer => "Conjurer",
WhiteMage => "White Mage",
Scholar => "Scholar",
Astrologian => "Astrologian",
Pugilist => "Pugilist",
Monk => "Monk",
Lancer => "Lancer",
Dragoon => "Dragoon",
Rogue => "Rogue",
Ninja => "Ninja",
Samurai => "Samurai",
Archer => "Archer",
Bard => "Bard",
Machinist => "Machinist",
Thaumaturge => "Thaumaturge",
BlackMage => "Black Mage",
Arcanist => "Arcanist",
Summoner => "Summoner",
RedMage => "Red Mage",
Carpenter => "Carpenter",
Blacksmith => "Blacksmith",
Armorer => "Armorer",
Goldsmith => "Goldsmith",
Leatherworker => "Leatherworker",
Weaver => "Weaver",
Alchemist => "Alchemist",
Culinarian => "Culinarian",
Miner => "Miner",
Botanist => "Botanist",
Fisher => "Fisher",
}
);