112 lines
3.1 KiB
Rust
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
|
|
}
|