webmention 0.6.0

Library and a CLI tool for sending and validation webmentions
Documentation
use crate::error::WebmentionError;
use crate::http_client::get;
use crate::wm_url::absolute_url;
use crate::wm_url::Url;
use select::node::Node;
use select::predicate::{Name, Predicate};

struct RelWebmention;
impl Predicate for RelWebmention {
    fn matches(&self, node: &Node) -> bool {
        node.attr("rel").map_or(false, |rels| {
            rels.split_whitespace().any(|rel| rel == "webmention")
        })
    }
}

pub async fn find_target_endpoint(url: &Url) -> Result<Option<Url>, WebmentionError> {
    let mut endpoint_candidates: Vec<(usize, Url)> = Vec::new();

    let response = get(url)
        .await
        .map_err(|source| WebmentionError::DiscoveryRequestFailed {
            url: url.clone(),
            source: Box::new(source),
        })?;

    let url = response.url.clone();

    let endpoint_in_link_header = response
        .rels
        .get("webmention")
        .and_then(|urls| urls.first());

    if let Some(link_str) = endpoint_in_link_header {
        if let Ok(u) = absolute_url(link_str, &url) {
            endpoint_candidates.push((0, u));
        }
    }

    let doc = response.html.doc()?;

    {
        let mut link_rels = doc.find(Name("link").and(RelWebmention));

        if let Some(node) = link_rels.next() {
            if let Some(href) = node.attr("href") {
                if let Ok(url) = absolute_url(href, &url) {
                    endpoint_candidates.push((node.index(), url));
                }
            }
        }
    }

    {
        let mut a_rels = doc.find(Name("a").and(RelWebmention));

        if let Some(node) = a_rels.next() {
            if let Some(href) = node.attr("href") {
                if let Ok(url) = absolute_url(href, &url) {
                    endpoint_candidates.push((node.index(), url));
                }
            }
        }
    }
    endpoint_candidates.sort_by(|a, b| a.0.cmp(&b.0));
    Ok(endpoint_candidates.first().map(|s| s.1.clone()))
}

#[cfg(test)]
mod test;