use crate::http; #[derive(Debug, Clone, Default)] pub struct OEmbed { pub oembed_type: Option, pub title: Option, pub description: Option, pub provider_name: Option, pub provider_url: Option, pub author_name: Option, pub author_url: Option, pub thumbnail_url: Option, pub url: Option, pub html: Option, pub width: Option, pub height: Option, } pub async fn fetch(endpoint: &str, max_bytes: usize) -> Option { let fetched = http::fetch(endpoint, "application/json", max_bytes) .await .ok()?; if !(200..300).contains(&fetched.status) { return None; } parse(&fetched.body) } pub fn parse(body: &str) -> Option { let value: serde_json::Value = serde_json::from_str(body).ok()?; if !value.is_object() { return None; } let oembed = OEmbed { oembed_type: string_field(&value, "type"), title: string_field(&value, "title"), description: string_field(&value, "description"), provider_name: string_field(&value, "provider_name"), provider_url: string_field(&value, "provider_url"), author_name: string_field(&value, "author_name"), author_url: string_field(&value, "author_url"), thumbnail_url: string_field(&value, "thumbnail_url"), url: string_field(&value, "url"), html: string_field(&value, "html"), width: number_field(&value, "width"), height: number_field(&value, "height"), }; (oembed.title.is_some() || oembed.description.is_some() || oembed.thumbnail_url.is_some() || oembed.html.is_some()) .then_some(oembed) } fn string_field(value: &serde_json::Value, key: &str) -> Option { let text = value.get(key)?.as_str()?.trim(); (!text.is_empty()).then(|| text.to_owned()) } fn number_field(value: &serde_json::Value, key: &str) -> Option { let node = value.get(key)?; node.as_u64() .and_then(|value| u32::try_from(value).ok()) .or_else(|| node.as_str().and_then(|value| value.trim().parse().ok())) } pub fn iframe_src(html: &str) -> Option { let lower = html.to_ascii_lowercase(); let start = lower.find("