feat: add GCs and reputation
This commit is contained in:
parent
311d283e22
commit
e25a855e1f
@ -6,11 +6,13 @@
|
|||||||
|`world`|`String`|The world the Free Company is on.|
|
|`world`|`String`|The world the Free Company is on.|
|
||||||
|`slogan`|`String`|The Free Company's slogan.|
|
|`slogan`|`String`|The Free Company's slogan.|
|
||||||
|`crest`|`Array` of `String`|The image URLs that are layered to created the Free Company crest.|
|
|`crest`|`Array` of `String`|The image URLs that are layered to created the Free Company crest.|
|
||||||
|
|`grand_company`|`String`|The Grand Company that the Free Company is affiliated with. Will be one of `"Flames"`, `"TwinAdders"`, or `"Maelstrom"`.|
|
||||||
|`active_members`|`u16`|The amount of active members.
|
|`active_members`|`u16`|The amount of active members.
|
||||||
|`rank`|`u8`|The Free Company's rank ([1,8]).|
|
|`rank`|`u8`|The Free Company's rank ([1,8]).|
|
||||||
|`pvp_rankings`|`PvpRankings`|The Free Company's PvP rankings.|
|
|`pvp_rankings`|`PvpRankings`|The Free Company's PvP rankings.|
|
||||||
|`formed`|`DateTime` (UTC, RFC3339 formatted)|The date and time at which the Free Company was created.|
|
|`formed`|`DateTime` (UTC, RFC3339 formatted)|The date and time at which the Free Company was created.|
|
||||||
|`estate`|`Estate?`|The Free Company's estate.|
|
|`estate`|`Estate?`|The Free Company's estate.|
|
||||||
|
|`reputation`|`Map` of `String` to `u8`|The reputation the Free Company has with each Grand Company. The keys are all possible values of the `grand_company` field.|
|
||||||
|
|
||||||
## PvpRankings
|
## PvpRankings
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
error::*,
|
error::*,
|
||||||
models::free_company::{FreeCompany, PvpRankings, Estate},
|
models::free_company::{FreeCompany, PvpRankings, Estate, GrandCompany},
|
||||||
};
|
};
|
||||||
|
|
||||||
use chrono::{DateTime, Local, TimeZone, Utc};
|
use chrono::{DateTime, Local, TimeZone, Utc};
|
||||||
@ -11,9 +11,13 @@ use scraper::Html;
|
|||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
selectors!(
|
selectors!(
|
||||||
|
FC_GRAND_COMPANY => "p.entry__freecompany__gc:nth-of-type(1)";
|
||||||
FC_NAME => ".entry__freecompany__name";
|
FC_NAME => ".entry__freecompany__name";
|
||||||
FC_WORLD => "p.entry__freecompany__gc:nth-of-type(3)";
|
FC_WORLD => "p.entry__freecompany__gc:nth-of-type(3)";
|
||||||
FC_SLOGAN => ".freecompany__text__message.freecompany__text";
|
FC_SLOGAN => ".freecompany__text__message.freecompany__text";
|
||||||
@ -22,6 +26,11 @@ selectors!(
|
|||||||
FC_FORMED => "p.freecompany__text:nth-of-type(5) > script";
|
FC_FORMED => "p.freecompany__text:nth-of-type(5) > script";
|
||||||
FC_ACTIVE_MEMBERS => "p.freecompany__text:nth-of-type(6)";
|
FC_ACTIVE_MEMBERS => "p.freecompany__text:nth-of-type(6)";
|
||||||
FC_RANK => "p.freecompany__text:nth-of-type(7)";
|
FC_RANK => "p.freecompany__text:nth-of-type(7)";
|
||||||
|
|
||||||
|
FC_REPUTATION => ".freecompany__reputation__data";
|
||||||
|
FC_REPUTATION_NAME => ".freecompany__reputation__gcname";
|
||||||
|
FC_REPUTATION_RANK => ".freecompany__reputation__rank";
|
||||||
|
|
||||||
FC_WEEKLY_RANKING => ".character__ranking__data tr:nth-of-type(1) > th";
|
FC_WEEKLY_RANKING => ".character__ranking__data tr:nth-of-type(1) > th";
|
||||||
FC_MONTHLY_RANKING => ".character__ranking__data tr:nth-of-type(2) > th";
|
FC_MONTHLY_RANKING => ".character__ranking__data tr:nth-of-type(2) > th";
|
||||||
|
|
||||||
@ -34,6 +43,7 @@ selectors!(
|
|||||||
pub fn parse(id: u64, html: &str) -> Result<FreeCompany> {
|
pub fn parse(id: u64, html: &str) -> Result<FreeCompany> {
|
||||||
let html = Html::parse_document(html);
|
let html = Html::parse_document(html);
|
||||||
|
|
||||||
|
let grand_company = parse_grand_company(&html)?;
|
||||||
let name = plain_parse(&html, &*FC_NAME)?;
|
let name = plain_parse(&html, &*FC_NAME)?;
|
||||||
let world = parse_world(&html)?;
|
let world = parse_world(&html)?;
|
||||||
let slogan = plain_parse(&html, &*FC_SLOGAN)?;
|
let slogan = plain_parse(&html, &*FC_SLOGAN)?;
|
||||||
@ -46,17 +56,20 @@ pub fn parse(id: u64, html: &str) -> Result<FreeCompany> {
|
|||||||
};
|
};
|
||||||
let formed = parse_formed(&html)?;
|
let formed = parse_formed(&html)?;
|
||||||
let estate = parse_estate(&html)?;
|
let estate = parse_estate(&html)?;
|
||||||
|
let reputation = parse_reputation(&html)?;
|
||||||
|
|
||||||
Ok(FreeCompany {
|
Ok(FreeCompany {
|
||||||
name,
|
name,
|
||||||
world,
|
world,
|
||||||
slogan,
|
slogan,
|
||||||
crest,
|
crest,
|
||||||
|
grand_company,
|
||||||
active_members,
|
active_members,
|
||||||
rank,
|
rank,
|
||||||
pvp_rankings,
|
pvp_rankings,
|
||||||
formed,
|
formed,
|
||||||
estate,
|
estate,
|
||||||
|
reputation,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,3 +166,49 @@ fn parse_crest(html: &Html) -> Result<Vec<Url>> {
|
|||||||
.map(|x| Url::parse(x).map_err(Error::InvalidUrl))
|
.map(|x| Url::parse(x).map_err(Error::InvalidUrl))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_grand_company(html: &Html) -> Result<GrandCompany> {
|
||||||
|
let text = plain_parse(html, &*FC_GRAND_COMPANY)?;
|
||||||
|
let name = text
|
||||||
|
.split(" <")
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| Error::invalid_content("grand company and reputation", Some(&text)))?;
|
||||||
|
GrandCompany::parse(&name)
|
||||||
|
.ok_or_else(|| Error::invalid_content("valid grand company", Some(&name)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_reputation(html: &Html) -> Result<BTreeMap<GrandCompany, u8>> {
|
||||||
|
let mut reps = BTreeMap::new();
|
||||||
|
|
||||||
|
for elem in html.select(&*FC_REPUTATION).into_iter() {
|
||||||
|
let name: String = elem
|
||||||
|
.select(&*FC_REPUTATION_NAME)
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| Error::missing_element(&*FC_REPUTATION_NAME))?
|
||||||
|
.text()
|
||||||
|
.collect();
|
||||||
|
let gc = GrandCompany::parse(&name)
|
||||||
|
.ok_or_else(|| Error::invalid_content("valid grand company", Some(&name)))?;
|
||||||
|
let rank_elem = elem
|
||||||
|
.select(&*FC_REPUTATION_RANK)
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| Error::missing_element(&*FC_REPUTATION_RANK))?;
|
||||||
|
let color_class = rank_elem
|
||||||
|
.value()
|
||||||
|
.classes()
|
||||||
|
.find(|x| x.starts_with("color_"))
|
||||||
|
.ok_or_else(|| Error::invalid_content("color_## class", None))?;
|
||||||
|
let rank: u8 = color_class
|
||||||
|
.split("color_")
|
||||||
|
.nth(1)
|
||||||
|
.ok_or_else(|| Error::invalid_content("color_## class", Some(&color_class)))
|
||||||
|
.and_then(|x| x.parse().map_err(Error::InvalidNumber))?;
|
||||||
|
reps.insert(gc, rank);
|
||||||
|
}
|
||||||
|
|
||||||
|
if reps.len() != 3 {
|
||||||
|
return Err(Error::invalid_content("three grand companies with reputation", None));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(reps)
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
macro_rules! ffxiv_enum {
|
macro_rules! ffxiv_enum {
|
||||||
($name:ident { $($variant:ident => $str_repr:expr),+$(,)? }) => {
|
($(#[$meta:meta])* $name:ident { $($variant:ident => $str_repr:expr),+$(,)? }) => {
|
||||||
|
$(#[$meta])*
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub enum $name {
|
pub enum $name {
|
||||||
$($variant,)+
|
$($variant,)+
|
||||||
|
@ -4,6 +4,8 @@ use ffxiv_types::World;
|
|||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct FreeCompany {
|
pub struct FreeCompany {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -11,13 +13,24 @@ pub struct FreeCompany {
|
|||||||
pub slogan: String,
|
pub slogan: String,
|
||||||
#[serde(serialize_with = "multi_url")]
|
#[serde(serialize_with = "multi_url")]
|
||||||
pub crest: Vec<Url>,
|
pub crest: Vec<Url>,
|
||||||
|
pub grand_company: GrandCompany,
|
||||||
pub active_members: u16,
|
pub active_members: u16,
|
||||||
pub rank: u8,
|
pub rank: u8,
|
||||||
pub pvp_rankings: PvpRankings,
|
pub pvp_rankings: PvpRankings,
|
||||||
pub formed: DateTime<Utc>,
|
pub formed: DateTime<Utc>,
|
||||||
pub estate: Option<Estate>,
|
pub estate: Option<Estate>,
|
||||||
|
pub reputation: BTreeMap<GrandCompany, u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ffxiv_enum!(
|
||||||
|
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
GrandCompany {
|
||||||
|
Flames => "Immortal Flames",
|
||||||
|
Maelstrom => "Maelstrom",
|
||||||
|
TwinAdders => "Order of the Twin Adder",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct PvpRankings {
|
pub struct PvpRankings {
|
||||||
pub weekly: Option<u64>,
|
pub weekly: Option<u64>,
|
||||||
|
Loading…
Reference in New Issue
Block a user