lodestone-parser/src/logic/character.rs

107 lines
2.9 KiB
Rust

use crate::models::character::{
Character,
CityState,
Clan,
Gender,
GrandCompany,
GrandCompanyInfo,
Guardian,
Race,
};
use scraper::Html;
use std::str::FromStr;
selectors!(
PROFILE_NAME => ".frame__chara__name";
PROFILE_WORLD => ".frame__chara__world";
PROFILE_TITLE => ".frame__chara__title";
PROFILE_NAME_DAY => ".character-block__birth";
PROFILE_RACE_CLAN_GENDER => "div.character-block:nth-of-type(1) > .character-block__box > .character-block__name";
PROFILE_GUARDIAN => "div.character-block:nth-of-type(2) > .character-block__box > .character-block__name";
PROFILE_CITY_STATE => "div.character-block:nth-of-type(3) > .character-block__box > .character-block__name";
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";
);
pub fn parse(id: u64, html: &str) -> Option<Character> {
let html = Html::parse_document(html);
let name = html.select(&*PROFILE_NAME).next()?.text().collect();
let world_str: String = html.select(&*PROFILE_WORLD).next()?.text().collect();
let world = ffxiv_types::World::from_str(&world_str).ok()?;
let title: Option<String> = html
.select(&*PROFILE_TITLE)
.next()
.map(|x| x.text().collect());
let mut rcg = html.select(&*PROFILE_RACE_CLAN_GENDER).next()?.text();
let race = Race::parse(rcg.next()?)?;
let mut clan_gender_str = rcg.next()?.split(" / ");
let clan = Clan::parse(clan_gender_str.next()?)?;
let gender = Gender::parse(clan_gender_str.next()?)?;
let name_day = html.select(&*PROFILE_NAME_DAY).next()?.text().collect();
let guardian_str: String = html.select(&*PROFILE_GUARDIAN).next()?.text().collect();
let guardian = Guardian::parse(&guardian_str)?;
let city_state_str: String = html.select(&*PROFILE_CITY_STATE).next()?.text().collect();
let city_state = CityState::parse(&city_state_str)?;
let grand_company: Option<GrandCompanyInfo> = html
.select(&*PROFILE_GRAND_COMPANY)
.next()
.map(|x| x.text().collect::<String>())
.and_then(|x| {
let mut x = x.split(" / ");
let gc = GrandCompany::parse(x.next()?)?;
Some(GrandCompanyInfo {
grand_company: gc,
rank: x.next()?.to_string(),
})
});
let free_company_id: Option<u64> = html
.select(&*PROFILE_FREE_COMPANY)
.next()
.and_then(|x| x.value().attr("href"))
.and_then(|x| x
.split('/')
.filter(|x| !x.is_empty())
.last())
.and_then(|x| x.parse().ok());
let profile_text = html
.select(&*PROFILE_TEXT)
.next()?
.text()
.collect::<String>()
.trim()
.to_string();
Some(Character {
id,
name,
world,
race,
clan,
gender,
title,
name_day,
guardian,
city_state,
grand_company,
free_company_id,
profile_text,
})
}