algorithm-problem-client 0.4.0

HTTP Client for algorithm problems
Documentation
use crate::{Error, Result};

use super::AtCoderProblem;

use scraper::{Html, Selector};

pub(super) fn scrape(html: &str, contest_id: &str) -> Result<Vec<AtCoderProblem>> {
    Html::parse_document(html)
        .select(&Selector::parse("tbody").unwrap())
        .next()
        .ok_or_else(|| Error::HtmlParseError)?
        .select(&Selector::parse("tr").unwrap())
        .map(|tr| {
            let selector = Selector::parse("td").unwrap();
            let mut tds = tr.select(&selector);
            let position = tds
                .next()
                .ok_or_else(|| Error::HtmlParseError)?
                .text()
                .next()
                .ok_or_else(|| Error::HtmlParseError)?
                .to_owned();
            let problem = tds.next().ok_or_else(|| Error::HtmlParseError)?;
            let id = problem
                .select(&Selector::parse("a").unwrap())
                .next()
                .ok_or_else(|| Error::HtmlParseError)?
                .value()
                .attr("href")
                .ok_or_else(|| Error::HtmlParseError)?
                .rsplit('/')
                .next()
                .ok_or_else(|| Error::HtmlParseError)?
                .to_owned();
            let title = problem
                .text()
                .next()
                .ok_or_else(|| Error::HtmlParseError)?
                .to_owned();
            Ok(AtCoderProblem {
                id,
                contest_id: contest_id.to_owned(),
                title,
                position,
            })
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs::File;
    use std::io::prelude::*;

    #[test]
    fn test_scrape() {
        let mut file = File::open("test_resources/abc107_tasks").unwrap();
        let mut contents = String::new();
        file.read_to_string(&mut contents).unwrap();
        let problems = scrape(&contents, "abc107").unwrap();
        assert_eq!(
            problems,
            vec![
                AtCoderProblem {
                    id: "abc107_a".to_owned(),
                    contest_id: "abc107".to_owned(),
                    title: "Train".to_owned(),
                    position: "A".to_owned()
                },
                AtCoderProblem {
                    id: "abc107_b".to_owned(),
                    contest_id: "abc107".to_owned(),
                    title: "Grid Compression".to_owned(),
                    position: "B".to_owned()
                },
                AtCoderProblem {
                    id: "arc101_a".to_owned(),
                    contest_id: "abc107".to_owned(),
                    title: "Candles".to_owned(),
                    position: "C".to_owned()
                },
                AtCoderProblem {
                    id: "arc101_b".to_owned(),
                    contest_id: "abc107".to_owned(),
                    title: "Median of Medians".to_owned(),
                    position: "D".to_owned()
                }
            ]
        );
    }
}