diff --git a/schemas/Character.md b/schemas/Character.md index 6244ec1..a5caaf7 100644 --- a/schemas/Character.md +++ b/schemas/Character.md @@ -15,6 +15,8 @@ |`grand_company`|`GrandCompanyInfo?`|The character's Grand Company affiliation and rank. See GrandCompanyInfo section below.| |`free_company_id`|`u64?`|The ID of the character's Free Company, if any.| |`profile_text`|`String`|The profile text for this character on the Lodestone. If empty, this will be the empty string (`""`).| +|`face`|`String` (URL)|A URL pointing to an image of the character's face.| +|`portrait`|`String` (URL)|A URL pointing to a full-body image of the character.| ## Race diff --git a/src/logic/character.rs b/src/logic/character.rs index 55709dc..7ef7185 100644 --- a/src/logic/character.rs +++ b/src/logic/character.rs @@ -13,9 +13,13 @@ use ffxiv_types::{World, Race, Clan, Guardian}; use scraper::Html; +use url::Url; + use std::str::FromStr; selectors!( + PROFILE_FACE => ".frame__chara__face > img"; + PROFILE_PORTRAIT => ".character__detail__image > a > img"; PROFILE_NAME => ".frame__chara__name"; PROFILE_WORLD => ".frame__chara__world"; PROFILE_TITLE => ".frame__chara__title"; @@ -47,6 +51,9 @@ pub fn parse(id: u64, html: &str) -> Result { let profile_text = plain_parse(&html, &*PROFILE_TEXT)?.trim().to_string(); + let face = parse_face(&html)?; + let portrait = parse_portrait(&html)?; + Ok(Character { id, name, @@ -61,6 +68,8 @@ pub fn parse(id: u64, html: &str) -> Result { grand_company, free_company_id, profile_text, + face, + portrait, }) } @@ -148,3 +157,27 @@ fn parse_free_company_id(html: &Html) -> Result> { }; crate::logic::parse_id(elem.value()).map(Some) } + +fn parse_face(html: &Html) -> Result { + let elem = html + .select(&*PROFILE_FACE) + .next() + .ok_or_else(|| Error::missing_element(&*PROFILE_FACE))?; + elem + .value() + .attr("src") + .ok_or_else(|| Error::invalid_content("img src attribute", None)) + .and_then(|x| Url::parse(x).map_err(Error::InvalidUrl)) +} + +fn parse_portrait(html: &Html) -> Result { + let elem = html + .select(&*PROFILE_PORTRAIT) + .next() + .ok_or_else(|| Error::missing_element(&*PROFILE_PORTRAIT))?; + elem + .value() + .attr("src") + .ok_or_else(|| Error::invalid_content("img src attribute", None)) + .and_then(|x| Url::parse(x).map_err(Error::InvalidUrl)) +} diff --git a/src/models/character.rs b/src/models/character.rs index e7f5fb7..92c0c22 100644 --- a/src/models/character.rs +++ b/src/models/character.rs @@ -2,6 +2,8 @@ use super::GrandCompany; use ffxiv_types::{World, Race, Clan, Guardian}; +use url::Url; + #[derive(Debug, Serialize, Deserialize)] pub struct Character { pub id: u64, @@ -21,6 +23,11 @@ pub struct Character { pub free_company_id: Option, pub profile_text: String, + + #[serde(with = "url_serde")] + pub face: Url, + #[serde(with = "url_serde")] + pub portrait: Url, } #[derive(Debug, Serialize, Deserialize)]