use std::sync::LazyLock;
const README: &str = include_str!("../README.md");
const PARAGRAPHS: usize = 2;
pub static INTRO: LazyLock<Vec<String>> = LazyLock::new(|| paragraphs(README, PARAGRAPHS));
fn paragraphs(markdown: &str, count: usize) -> Vec<String> {
markdown
.split("\n\n")
.map(str::trim)
.filter(|block| !block.is_empty() && !block.starts_with('#'))
.take(count)
.map(|block| inline(&block.split_whitespace().collect::<Vec<_>>().join(" ")))
.collect()
}
fn inline(text: &str) -> String {
let mut out = String::new();
let mut rest = text;
while let Some(c) = rest.chars().next() {
if let Some(after) = rest.strip_prefix("**")
&& let Some((inner, tail)) = after.split_once("**")
{
out.push_str(&format!("<strong>{}</strong>", inline(inner)));
rest = tail;
} else if let Some(after) = rest.strip_prefix('`')
&& let Some((inner, tail)) = after.split_once('`')
{
out.push_str(&format!("<code>{}</code>", escape(inner)));
rest = tail;
} else if let Some((label, url, tail)) = link(rest) {
out.push_str(&format!(
r#"<a href="{}" class="underline" rel="noreferrer">{}</a>"#,
escape(url),
inline(label)
));
rest = tail;
} else {
out.push_str(&escape(&c.to_string()));
rest = &rest[c.len_utf8()..];
}
}
out
}
fn link(text: &str) -> Option<(&str, &str, &str)> {
let (label, after) = text.strip_prefix('[')?.split_once("](")?;
let (url, tail) = after.split_once(')')?;
(url.starts_with("https://") || url.starts_with("http://")).then_some((label, url, tail))
}
fn escape(text: &str) -> String {
text.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_first_paragraphs_become_html() {
let markdown = "# defter\n\nA **self-hosted** server: see [Zotero](https://www.zotero.org/)\n\
and `commonmeta sync` <script>.\n\nSecond `one`.\n\nThird.";
assert_eq!(
paragraphs(markdown, 2),
[
"A <strong>self-hosted</strong> server: see <a href=\"https://www.zotero.org/\" \
class=\"underline\" rel=\"noreferrer\">Zotero</a> and <code>commonmeta sync</code> \
<script>.",
"Second <code>one</code>.",
]
);
assert_eq!(inline("[a](javascript:x)"), "[a](javascript:x)");
}
#[test]
fn the_readme_has_two_paragraphs_to_show() {
assert_eq!(INTRO.len(), 2);
assert!(INTRO[0].contains("Zotero"), "{}", INTRO[0]);
}
}