hyaenidae/content/src/lib.rs

112 lines
3.1 KiB
Rust

use ammonia::Builder;
use once_cell::sync::Lazy;
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
fn allow_styles<'u>(allowed: &[&str], value: &'u str) -> Option<Cow<'u, str>> {
let mut altered = false;
let rules: Vec<_> = value
.split(';')
.filter_map(|rule| {
let name = rule.split(':').next()?.trim();
log::debug!("Checking '{}' against {:?}", name, allowed);
if allowed.contains(&name) {
Some(rule)
} else {
altered = true;
None
}
})
.collect();
if altered {
if rules.is_empty() {
None
} else {
Some(Cow::Owned(rules.join(";")))
}
} else {
Some(Cow::Borrowed(value))
}
}
fn attribute_filter<'u>(element: &str, attribute: &str, value: &'u str) -> Option<Cow<'u, str>> {
match (element, attribute) {
("ul", "style") | ("ol", "style") => allow_styles(&["list-style-type", "type"], value),
("span", "style") => allow_styles(&["color", "opacity"], value),
("div", "class")
| ("span", "class")
| ("figure", "class")
| ("pre", "class")
| ("pre", "data-language")
| ("span", "data-symbol")
| ("blockquote", "data-author")
| ("a", "rel")
| ("a", "title")
| ("a", "href")
| ("img", "src")
| ("img", "title")
| ("img", "alt") => Some(Cow::Borrowed(value)),
_ => None,
}
}
// Classes based on bbclash BBCode spec:
// https://github.com/EndaHallahan/BBClash/blob/master/Spec.md
static AMMONIA_CONFIG: Lazy<Builder> = Lazy::new(|| {
let mut classes = HashMap::new();
let div_hs = classes.entry("div").or_insert(HashSet::new());
div_hs.insert("center");
div_hs.insert("right");
div_hs.insert("math_container");
// div_hs.insert("embed"); for now, no embeds
div_hs.insert("indent-1");
div_hs.insert("indent-2");
div_hs.insert("indent-3");
div_hs.insert("indent-4");
let span_hs = classes.entry("span").or_insert(HashSet::new());
span_hs.insert("underline");
span_hs.insert("smallcaps");
span_hs.insert("monospace");
span_hs.insert("spoiler");
span_hs.insert("math_container");
let pre_hs = classes.entry("pre").or_insert(HashSet::new());
pre_hs.insert("codeblock");
let figure_hs = classes.entry("figure").or_insert(HashSet::new());
figure_hs.insert("figure-right");
figure_hs.insert("figure-left");
let mut schemes = HashSet::new();
schemes.insert("http");
schemes.insert("https");
schemes.insert("mailto");
let mut builder = Builder::new();
builder
.allowed_classes(classes)
.url_schemes(schemes)
.attribute_filter(attribute_filter)
.add_tag_attributes("span", &["style"])
.add_tag_attributes("div", &["style"]);
builder
});
pub fn html(source: &str) -> String {
let h = AMMONIA_CONFIG.clean(source).to_string();
log::debug!("{}", h);
h
}
pub fn bbcode(source: &str) -> String {
let bb = bbclash::bbcode_to_html(source);
log::debug!("{}", bb);
bb
}