1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use std::sync::Arc;

use chrono::naive::NaiveDate;
use failure::Error;
use reqwest::Client;
use select::document::Document;
use select::predicate::{Attr, Class, Name};
use xvii::Roman;

pub const BASE_URL: &str = "https://www.swordscomic.com";

#[derive(Debug)]
pub struct Comic {
    id: u32,
    title: String,
    date: NaiveDate,
    image: String,
    characters: Vec<String>,
    locations: Vec<String>,
    creatures: Vec<String>,
    swords: Vec<String>
}
//struct Character {}
//struct Creature {}
//struct Location {}
//struct Sword {}

pub struct SwordsComic {
    client: Arc<Client>,
}

impl Default for SwordsComic {
    fn default() -> Self {
        Self {
            client: Arc::new(Client::new()),
        }
    }
}

impl SwordsComic {
    pub fn new() -> Self {
        Default::default()
    }

    pub fn from_client(client: Client) -> Self {
        Self {
            client: Arc::new(client),
        }
    }


    pub fn get_comic(&self, id: u32) -> Result<Comic, Error> {
        let roman_no = match Roman::from(id as i32) {
            Some(roman) => roman.to_uppercase(),
            None => String::from("cover"),
        };

        let mut resp = self.client.get(format!("{}/swords/{}", BASE_URL, roman_no).as_str()).send()?.error_for_status()?;
        let dom = Document::from(resp.text()?.as_str());

        let title = dom.find(Attr("id", "comic-title"))
            .next()
            .unwrap()
            .text();

        let date = dom.find(Attr("id", "comic-post-date"))
            .next()
            .unwrap()
            .text();
        let date = NaiveDate::parse_from_str(date.as_str(), "%B %e, %Y")?;

        let image = dom.find(Attr("id", "comic-image"))
            .next()
            .unwrap()
            .attr("src")
            .unwrap()
            .to_string();

        macro_rules! create_vec {
            ($vec_name:ident) => (
                let mut $vec_name: Vec<String> = Vec::new();
            )
        }

        create_vec!(characters);
        create_vec!(creatures);
        create_vec!(swords);
        create_vec!(locations);


        for tag_group in dom.find(Class("tag-group")) {
            macro_rules! iter_on_tag_group_and_append {
                ($var_name:ident) => (
                    for tag in tag_group.find(Class("tag")) {
                        $var_name.push(String::from(tag.text().as_str().trim()));
                    }
                )
            }

            let category = tag_group.find(Name("p")).next().unwrap().text();
            if category == "Characters:" {
                iter_on_tag_group_and_append!(characters);
            } else if category == "Locations:" {
                iter_on_tag_group_and_append!(locations);
            } else if category == "Swords:" {
                iter_on_tag_group_and_append!(swords);
            } else if category == "Creatures:" {
                iter_on_tag_group_and_append!(creatures);
            }
        }

        let comic = Comic {
            id,
            title,
            date,
            image,
            characters,
            creatures,
            swords,
            locations,
        };
        Ok(comic)
    }
}