use crate::{http, mf2};
use ::http::Request;
use microformats::types::{Class, KnownClass, PropertyValue};
fn check_if_property_values_have_url(values: &[PropertyValue], url: &url::Url) -> bool {
values
.iter()
.filter_map(|v| {
if let PropertyValue::Url(u) = v {
Some(u)
} else {
None
}
})
.any(|v| **v == *url)
}
#[tracing::instrument(skip(client))]
pub async fn for_url(
client: &impl http::Client,
url: &url::Url,
) -> Result<microformats::types::Item, crate::Error> {
let resp = client.send_request(
Request::builder()
.method("GET")
.uri(url.to_string())
.header(::http::header::ACCEPT, "text/html, text/mf2+html, application/json, application/mf2+json, application/jf2+json")
.body(crate::http::Body::Empty)
.map_err(crate::Error::Http)?,
).await?;
let doc =
mf2::http::to_mf2_document(resp.map(|body| body.as_bytes().to_vec()), url.as_str())
.map_err(crate::Error::from)?;
from_document(&doc, url).await
}
pub async fn from_document(
document: µformats::types::Document,
url: &url::Url,
) -> Result<microformats::types::Item, crate::Error> {
let cards = document
.items
.iter()
.filter(|item| item.r#type.contains(&Class::Known(KnownClass::Card)))
.cloned()
.collect::<Vec<_>>();
tracing::trace!(card_count = cards.len(), "Found cards at this URL.");
let rel_for_url = document.rels.items.get(url).cloned().unwrap_or_default();
if let Some(directly_specified_card) = cards
.iter()
.find(|card_item| {
let item_uids = card_item.get_property("uid").unwrap_or_default();
let item_urls = card_item.get_property("url").unwrap_or_default();
check_if_property_values_have_url(&item_uids, url)
&& check_if_property_values_have_url(&item_urls, url)
})
.cloned()
{
tracing::trace!("Found the representative h-card directly on the page.");
Ok(directly_specified_card.to_owned())
} else if let Some(rel_me_assoc_card) = cards
.iter()
.find(|card_item| {
rel_for_url.rels.contains(&"me".into())
&& check_if_property_values_have_url(
&card_item.get_property("url").unwrap_or_default(),
url,
)
})
.cloned()
{
tracing::trace!("Found the representative h-card via rel=me discovery.");
Ok(rel_me_assoc_card.to_owned())
} else if cards.len() == 1 {
tracing::trace!("Attempting to use the only card found on the page.");
cards
.first()
.ok_or_else(|| crate::Error::NoRepresentativeHCardFound(url.clone()))
.cloned()
} else {
tracing::trace!("Algorithm exhausted all options on discovery; found nothing.");
Err(crate::Error::NoRepresentativeHCardFound(url.clone()))
}
}
#[cfg(test)]
mod test;