use std::{collections::HashMap, fmt::Write};
use anyhow::Result;
use crate::data::{self, PreviewEvent, UrlPreviewInfo};
use super::CodeBlock;
pub(crate) struct UrlPreviewBlock<'a> {
url: &'a str,
info: UrlPreviewInfo,
show_image: bool,
}
impl<'a> UrlPreviewBlock<'a> {
pub(crate) fn new(
options: HashMap<String, &'a str>,
url: &'a str,
info: UrlPreviewInfo,
) -> Self {
UrlPreviewBlock {
url,
info,
show_image: options
.get("image")
.and_then(|v| str::parse::<bool>(v).ok())
.unwrap_or(true),
}
}
}
impl<'a> CodeBlock for UrlPreviewBlock<'a> {
fn render(&self) -> Result<String> {
let mut html = String::new();
writeln!(&mut html, r#"<div class="url-preview">"#)?;
writeln!(&mut html, r#" <div>{}</div>"#, self.info.title)?;
writeln!(&mut html, r#" <div>{}</div>"#, self.info.description)?;
writeln!(&mut html, r#" <a href="{url}">{url}</a>"#, url = self.url)?;
if self.show_image {
if let Some(image) = self.info.image.as_ref().filter(|i| !i.is_empty()) {
writeln!(&mut html, r#" <img src="{}" />"#, image)?;
}
}
writeln!(&mut html, r#"</div>"#)?;
Ok(html)
}
}
pub(crate) struct UrlPreviewError<'a>(pub &'a str, pub &'a str);
impl<'a> CodeBlock for UrlPreviewError<'a> {
fn render(&self) -> Result<String> {
let mut html = String::new();
writeln!(&mut html, r#"<div class="url-preview">"#)?;
writeln!(&mut html, r#" <div></div>"#)?;
writeln!(&mut html, r#" <div>Url preview error: {}</div>"#, self.1)?;
writeln!(&mut html, r#" <a href="{url}">{url}</a>"#, url = self.0)?;
writeln!(&mut html, r#"</div>"#)?;
Ok(html)
}
}
#[tokio::main(flavor = "current_thread")]
pub(crate) async fn render(url: &str, options: HashMap<String, &str>) -> Option<String> {
let (first_preview, mut rx) = {
let data = data::read();
if let Some(info) = data.get_preview(url) {
let html = UrlPreviewBlock::new(options, url, info).render().unwrap();
return Some(html);
}
data.preview_url(url)
};
rx.changed()
.await
.expect("URL preview watch channel receive failed.");
let event = rx.borrow();
match event.to_owned().expect("Url preview didn't initialized.") {
PreviewEvent::Finished(info) => {
let html = UrlPreviewBlock::new(options, url, info).render().unwrap();
if first_preview {
println!("URL previewed: {url}");
}
Some(html)
}
PreviewEvent::Failed(err) => {
Some(UrlPreviewError(url, &err).render().unwrap())
}
}
}