use cursive::theme;
use html2text::render::text_renderer;
use crate::{Element, RenderedDocument};
pub type RichRenderer = Renderer<text_renderer::RichDecorator, RichConverter>;
pub struct Renderer<D: text_renderer::TextDecorator + Clone, C: Converter<D::Annotation>> {
render_tree: html2text::RenderTree,
decorator: D,
converter: C,
}
pub trait Converter<A> {
fn get_style(&self, annotation: &A) -> Option<theme::Style>;
fn get_link<'a>(&self, annotation: &'a A) -> Option<&'a str>;
}
pub struct RichConverter;
impl Renderer<text_renderer::RichDecorator, RichConverter> {
pub fn new(html: &str) -> Renderer<text_renderer::RichDecorator, RichConverter> {
Renderer::custom(html, text_renderer::RichDecorator::new(), RichConverter)
}
}
impl<D: text_renderer::TextDecorator + Clone, C: Converter<D::Annotation>> Renderer<D, C> {
pub fn custom(html: &str, decorator: D, converter: C) -> Renderer<D, C> {
Renderer {
render_tree: html2text::parse(html.as_bytes()),
decorator,
converter,
}
}
}
impl<D: text_renderer::TextDecorator + Clone, C: Converter<D::Annotation>> super::Renderer
for Renderer<D, C>
{
fn render(&self, constraint: cursive::XY<usize>) -> RenderedDocument {
let mut doc = RenderedDocument::new(constraint);
let lines = self
.render_tree
.clone()
.render(std::cmp::max(5, constraint.x), self.decorator.clone())
.into_lines();
for line in lines {
let mut elements = Vec::new();
for element in line.iter() {
if let text_renderer::TaggedLineElement::Str(ts) = element {
let styles: Vec<_> = ts
.tag
.iter()
.filter_map(|a| self.converter.get_style(a))
.collect();
let link_target = ts
.tag
.iter()
.find_map(|a| self.converter.get_link(a))
.map(ToOwned::to_owned);
elements.push(Element::new(
ts.s.clone(),
theme::Style::merge(&styles),
link_target,
));
}
}
doc.push_line(elements);
}
doc
}
}
impl Converter<text_renderer::RichAnnotation> for RichConverter {
fn get_style(&self, annotation: &text_renderer::RichAnnotation) -> Option<theme::Style> {
use text_renderer::RichAnnotation;
match annotation {
RichAnnotation::Default => None,
RichAnnotation::Link(_) => Some(theme::Effect::Underline.into()),
RichAnnotation::Image => None,
RichAnnotation::Emphasis => Some(theme::Effect::Italic.into()),
RichAnnotation::Strong => Some(theme::Effect::Bold.into()),
RichAnnotation::Strikeout => Some(theme::Effect::Strikethrough.into()),
RichAnnotation::Code => Some(theme::PaletteColor::Secondary.into()),
RichAnnotation::Preformat(_) => None,
}
}
fn get_link<'a>(&self, annotation: &'a text_renderer::RichAnnotation) -> Option<&'a str> {
if let text_renderer::RichAnnotation::Link(target) = annotation {
Some(target)
} else {
None
}
}
}