favicon_picker/
lib.rs

1pub mod error;
2pub(crate) mod favicon;
3mod icons_from_html;
4pub mod selectors;
5
6pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
7
8use error::Error;
9use favicon::InnerFavicon;
10pub use icons_from_html::*;
11
12pub use favicon::Favicon;
13use scraper::Html;
14use url::Url;
15
16/// This function gives you the default favicon [`Url`] from a given url 
17/// 
18/// Example:
19/// 
20/// ```
21/// 
22/// use favicon_picker::get_default_favicon_url;
23/// 
24/// use url::Url;
25/// 
26/// let base_url = Url::parse("https://crates.io").unwrap();
27/// 
28/// assert_eq!(Url::parse("https://crates.io/favicon.ico").unwrap(), get_default_favicon_url(&base_url).unwrap())
29/// 
30/// ```
31/// 
32pub fn get_default_favicon_url(base_url: &Url) -> std::result::Result<Url, url::ParseError> {
33    base_url.join("/favicon.ico")
34}
35
36/// This function gives you the default favicon [`Favicon`] from a given url 
37pub fn get_default_favicon(base_url: &Url) -> Result<Favicon> {
38    Ok(Favicon {
39        href: get_default_favicon_url(base_url)?,
40        size: Default::default(),
41        type_: Default::default()
42    })
43}
44
45/// This will get all existing `<link rel='icon'>` that are declared in the page.
46/// Please note that this not uses the default [`reqwest::Client`].
47/// If you want to use the [`reqwest::blocking::Client`], please use the [`crate::get_blocking_favicons_from_url`] instead.
48pub async fn get_favicons_from_url(
49    client: &reqwest::Client,
50    base_url: &Url,
51) -> Result<Vec<Favicon>> {
52    let res = client.get(base_url.clone()).send().await?;
53    if !res.status().is_success() {
54        return Err(Error::ReqwestSend { code: res.status() });
55    }
56    let html_raw = res.text().await?;
57    let html = Html::parse_document(&html_raw);
58    let icons = InnerFavicon::extract_favicons(&html)?
59        .into_iter()
60        .flat_map(|e| <Favicon as TryFrom<(&Url, InnerFavicon<'_>)>>::try_from((base_url, e)))
61        .collect::<Vec<Favicon>>();
62    if icons.is_empty() {
63        get_default_favicon(base_url).map(|r| vec![r])
64    } else {
65        Ok(icons)
66    }
67}
68
69/// Same as [`crate::get_favicons_from_url`] but it uses the [`reqwest::blocking::Client`]
70#[cfg(feature = "blocking")]
71#[cfg_attr(docsrs, doc(cfg("blocking")))]
72pub fn get_blocking_favicons_from_url(
73    client: &reqwest::blocking::Client,
74    base_url: &Url,
75) -> Result<Vec<Favicon>> {
76    let res = client.get(base_url.clone()).send()?;
77    if !res.status().is_success() {
78        return Err(Error::ReqwestSend { code: res.status() });
79    }
80    let html_raw = res.text()?;
81    let html = Html::parse_document(&html_raw);
82    let icons = InnerFavicon::extract_favicons(&html)?
83        .into_iter()
84        .flat_map(|e| <Favicon as TryFrom<(&Url, InnerFavicon<'_>)>>::try_from((base_url, e)))
85        .collect::<Vec<Favicon>>();
86    if icons.is_empty() {
87        get_default_favicon(base_url).map(|r| vec![r])
88    } else {
89        Ok(icons)
90    }
91}