mirror of
https://github.com/dpc/tagwiki
synced 2024-09-27 14:48:15 +00:00
166 lines
3.6 KiB
Rust
166 lines
3.6 KiB
Rust
|
mod store;
|
||
|
|
||
|
use lazy_static::lazy_static;
|
||
|
pub use store::{InMemoryStore, Store, StoreMut};
|
||
|
|
||
|
use anyhow::Result;
|
||
|
use digest::Digest;
|
||
|
|
||
|
pub type Id = String;
|
||
|
pub type Tag = String;
|
||
|
|
||
|
const TAGWIKI_PAGE_ID_KEY: &str = "tagwiki-page-id";
|
||
|
|
||
|
#[derive(Debug, Default, Clone)]
|
||
|
pub struct Source(String);
|
||
|
|
||
|
#[derive(Debug, Default, Clone)]
|
||
|
pub struct Parsed {
|
||
|
pub source: Source,
|
||
|
pub html: String,
|
||
|
pub headers: Headers,
|
||
|
pub tags: Vec<Tag>,
|
||
|
pub title: String,
|
||
|
}
|
||
|
|
||
|
fn split_headers_and_body(source: &Source) -> (&str, &str) {
|
||
|
lazy_static! {
|
||
|
static ref RE: regex::Regex =
|
||
|
regex::RegexBuilder::new(r"\A[[:space:]]*<!--+(.*)--+>(.*)\z")
|
||
|
.multi_line(true)
|
||
|
.dot_matches_new_line(true)
|
||
|
.build()
|
||
|
.unwrap();
|
||
|
}
|
||
|
|
||
|
if let Some(cap) = RE.captures_iter(&source.0).next() {
|
||
|
(
|
||
|
cap.get(1).expect("be there").as_str(),
|
||
|
cap.get(2).expect("be there").as_str(),
|
||
|
)
|
||
|
} else {
|
||
|
("", &source.0)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Clone, Default)]
|
||
|
pub struct Headers {
|
||
|
pub id: String,
|
||
|
pub all: String,
|
||
|
}
|
||
|
|
||
|
impl Headers {
|
||
|
fn parse(headers_str: &str, source: &Source) -> Headers {
|
||
|
let mut id = None;
|
||
|
|
||
|
for line in headers_str.lines() {
|
||
|
match line.split(":").collect::<Vec<_>>().as_slice() {
|
||
|
[key, value] => {
|
||
|
let key = key.trim();
|
||
|
let value = value.trim();
|
||
|
match key {
|
||
|
TAGWIKI_PAGE_ID_KEY => {
|
||
|
id = Some(value.to_owned());
|
||
|
}
|
||
|
_ => {}
|
||
|
}
|
||
|
}
|
||
|
_ => {}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
match id {
|
||
|
Some(id) => Self {
|
||
|
id,
|
||
|
all: headers_str.to_owned(),
|
||
|
},
|
||
|
None => {
|
||
|
let mut hasher = blake2::Blake2b::new();
|
||
|
hasher.input(&source.0);
|
||
|
let res = hasher.result();
|
||
|
let id = hex::encode(&res.as_slice()[0..16]);
|
||
|
|
||
|
let mut all = String::new();
|
||
|
all.push_str(TAGWIKI_PAGE_ID_KEY);
|
||
|
all.push_str(": ");
|
||
|
all.push_str(&id);
|
||
|
all.push_str("\n");
|
||
|
all.push_str(headers_str);
|
||
|
|
||
|
Self { id, all }
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Parsed {
|
||
|
fn from_markdown(source: Source) -> Parsed {
|
||
|
let (headers, body) = split_headers_and_body(&source);
|
||
|
let headers = Headers::parse(headers, &source);
|
||
|
|
||
|
let parser = pulldown_cmark::Parser::new(body);
|
||
|
let mut html_output = String::new();
|
||
|
pulldown_cmark::html::push_html(&mut html_output, parser);
|
||
|
|
||
|
Parsed {
|
||
|
headers,
|
||
|
html: html_output,
|
||
|
source,
|
||
|
tags: vec!["TODO".into()],
|
||
|
title: "TODO".into(),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn split_headers_and_body_test() -> Result<()> {
|
||
|
let s = Source(
|
||
|
r#"
|
||
|
|
||
|
|
||
|
<!------- a: b
|
||
|
c: d -->banana"#
|
||
|
.into(),
|
||
|
);
|
||
|
let (headers, body) = split_headers_and_body(&s);
|
||
|
|
||
|
assert_eq!(
|
||
|
headers,
|
||
|
r#" a: b
|
||
|
c: d "#
|
||
|
);
|
||
|
assert_eq!(body, "banana");
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn parse_markdown_metadata_test() -> Result<()> {
|
||
|
let page = Parsed::from_markdown(Source(
|
||
|
r#"
|
||
|
|
||
|
<!---
|
||
|
|
||
|
a: b
|
||
|
tagwiki-page-id: xyz
|
||
|
|
||
|
foo-bar: bar
|
||
|
-->
|
||
|
bar
|
||
|
<!---
|
||
|
tagwiki-id: 123
|
||
|
-->
|
||
|
"#
|
||
|
.to_owned(),
|
||
|
));
|
||
|
|
||
|
println!("{:#?}", page);
|
||
|
assert_eq!(page.headers.id, "xyz");
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn add_to_store(_store: &impl Store, source: Source) -> Result<()> {
|
||
|
let _page = Parsed::from_markdown(source);
|
||
|
Ok(())
|
||
|
}
|