Merge pull request #426 from matthiasbeyer/libimagentrymarkdown/init
Libimagentrymarkdown/init
This commit is contained in:
commit
c986d8f217
7 changed files with 280 additions and 0 deletions
17
libimagentrymarkdown/Cargo.toml
Normal file
17
libimagentrymarkdown/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "libimagentrymarkdown"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.3"
|
||||||
|
hoedown = "5.0.0"
|
||||||
|
crossbeam = "0.2"
|
||||||
|
url = "1.1"
|
||||||
|
|
||||||
|
[dependencies.libimagstore]
|
||||||
|
path = "../libimagstore"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
||||||
|
|
9
libimagentrymarkdown/README.md
Normal file
9
libimagentrymarkdown/README.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# libimagentrymarkdown
|
||||||
|
|
||||||
|
Helper crate to add useful functionality in a wrapper around
|
||||||
|
[hoedown](https://crates.io/crates/hoedown) for imag.
|
||||||
|
|
||||||
|
Adds functionality to extract links, parse content into HTML and other things
|
||||||
|
which might be useful for markdown rendering in imag.
|
||||||
|
|
||||||
|
|
11
libimagentrymarkdown/src/error.rs
Normal file
11
libimagentrymarkdown/src/error.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
generate_error_module!(
|
||||||
|
generate_error_types!(MarkdownError, MarkdownErrorKind,
|
||||||
|
MarkdownRenderError => "Markdown render error",
|
||||||
|
LinkParsingError => "Link parsing error"
|
||||||
|
);
|
||||||
|
);
|
||||||
|
|
||||||
|
pub use self::error::MarkdownError;
|
||||||
|
pub use self::error::MarkdownErrorKind;
|
||||||
|
|
||||||
|
|
82
libimagentrymarkdown/src/html.rs
Normal file
82
libimagentrymarkdown/src/html.rs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
use hoedown::{Markdown, Html as MdHtml};
|
||||||
|
use hoedown::renderer::html::Flags as HtmlFlags;
|
||||||
|
use hoedown::renderer::Render;
|
||||||
|
|
||||||
|
use result::Result;
|
||||||
|
use error::MarkdownErrorKind;
|
||||||
|
use libimagerror::into::IntoError;
|
||||||
|
|
||||||
|
pub type HTML = String;
|
||||||
|
|
||||||
|
pub fn to_html(buffer: &str) -> Result<HTML> {
|
||||||
|
let md = Markdown::new(buffer);
|
||||||
|
let mut html = MdHtml::new(HtmlFlags::empty(), 0);
|
||||||
|
html.render(&md)
|
||||||
|
.to_str()
|
||||||
|
.map(String::from)
|
||||||
|
.map_err(Box::new)
|
||||||
|
.map_err(|e| MarkdownErrorKind::MarkdownRenderError.into_error_with_cause(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod iter {
|
||||||
|
use result::Result;
|
||||||
|
use libimagstore::store::Entry;
|
||||||
|
use super::HTML;
|
||||||
|
use super::to_html;
|
||||||
|
|
||||||
|
pub struct ToHtmlIterator<I: Iterator<Item = Entry>> {
|
||||||
|
i: I
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator<Item = Entry>> ToHtmlIterator<I> {
|
||||||
|
|
||||||
|
fn new(i: I) -> ToHtmlIterator<I> {
|
||||||
|
ToHtmlIterator { i: i }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator<Item = Entry>> Iterator for ToHtmlIterator<I> {
|
||||||
|
type Item = Result<HTML>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.i.next().map(|entry| to_html(&entry.get_content()[..]))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator<Item = Entry>> From<I> for ToHtmlIterator<I> {
|
||||||
|
|
||||||
|
fn from(obj: I) -> ToHtmlIterator<I> {
|
||||||
|
ToHtmlIterator::new(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Iterate over `(Entry, Result<HTML>)` tuples
|
||||||
|
pub struct WithHtmlIterator<I: Iterator<Item = Entry>> {
|
||||||
|
i: I
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator<Item = Entry>> WithHtmlIterator<I> {
|
||||||
|
|
||||||
|
fn new(i: I) -> WithHtmlIterator<I> {
|
||||||
|
WithHtmlIterator { i: i }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator<Item = Entry>> Iterator for WithHtmlIterator<I> {
|
||||||
|
type Item = (Entry, Result<HTML>);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.i.next().map(|entry| {
|
||||||
|
let html = to_html(&entry.get_content()[..]);
|
||||||
|
(entry, html)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
libimagentrymarkdown/src/lib.rs
Normal file
12
libimagentrymarkdown/src/lib.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
extern crate crossbeam;
|
||||||
|
extern crate hoedown;
|
||||||
|
extern crate url;
|
||||||
|
extern crate libimagstore;
|
||||||
|
#[macro_use] extern crate libimagerror;
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub mod html;
|
||||||
|
pub mod link;
|
||||||
|
pub mod result;
|
||||||
|
|
143
libimagentrymarkdown/src/link.rs
Normal file
143
libimagentrymarkdown/src/link.rs
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
use error::MarkdownErrorKind as MEK;
|
||||||
|
use result::Result;
|
||||||
|
|
||||||
|
use hoedown::renderer::Render;
|
||||||
|
use hoedown::Buffer;
|
||||||
|
use hoedown::Markdown;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use libimagerror::into::IntoError;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Link {
|
||||||
|
pub title: String,
|
||||||
|
pub link: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Link {
|
||||||
|
|
||||||
|
/// Translate a `Link` into a `UrlLink`
|
||||||
|
fn into_urllink(self) -> Result<UrlLink> {
|
||||||
|
Url::parse(&self.link[..])
|
||||||
|
.map(move |link| UrlLink { title: self.title, link: link, })
|
||||||
|
.map_err(Box::new)
|
||||||
|
.map_err(|e| MEK::LinkParsingError.into_error_with_cause(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UrlLink {
|
||||||
|
pub title: String,
|
||||||
|
pub link: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LinkExtractor {
|
||||||
|
links: Vec<Link>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LinkExtractor {
|
||||||
|
|
||||||
|
pub fn new() -> LinkExtractor {
|
||||||
|
LinkExtractor { links: vec![] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn links(self) -> Vec<Link> {
|
||||||
|
self.links
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for LinkExtractor {
|
||||||
|
|
||||||
|
fn link(&mut self,
|
||||||
|
_: &mut Buffer,
|
||||||
|
content: Option<&Buffer>,
|
||||||
|
link: Option<&Buffer>,
|
||||||
|
_: Option<&Buffer>)
|
||||||
|
-> bool
|
||||||
|
{
|
||||||
|
let link = link.and_then(|l| l.to_str().ok()).map(String::from);
|
||||||
|
let content = content.and_then(|l| l.to_str().ok()).map(String::from);
|
||||||
|
|
||||||
|
match (link, content) {
|
||||||
|
(Some(link), Some(content)) => {
|
||||||
|
self.links.push(Link { link: link, title: content });
|
||||||
|
false
|
||||||
|
},
|
||||||
|
|
||||||
|
(_, _) => {
|
||||||
|
false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract_links(buf: &str) -> Vec<Link> {
|
||||||
|
let mut le = LinkExtractor::new();
|
||||||
|
le.render(&Markdown::new(buf));
|
||||||
|
le.links()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::{Link, extract_links};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_one_link() {
|
||||||
|
let testtext = "Some [example text](http://example.com).";
|
||||||
|
|
||||||
|
let exp = Link {
|
||||||
|
title: String::from("example text"),
|
||||||
|
link: String::from("http://example.com"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut links = extract_links(testtext);
|
||||||
|
assert_eq!(1, links.len());
|
||||||
|
assert_eq!(exp, links.pop().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_two_similar_links() {
|
||||||
|
let testtext = r#"
|
||||||
|
Some [example text](http://example.com).
|
||||||
|
Some more [example text](http://example.com).
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let exp = Link {
|
||||||
|
title: String::from("example text"),
|
||||||
|
link: String::from("http://example.com"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut links = extract_links(&testtext[..]);
|
||||||
|
assert_eq!(2, links.len());
|
||||||
|
assert_eq!(exp, links.pop().unwrap());
|
||||||
|
assert_eq!(exp, links.pop().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_two_links() {
|
||||||
|
let testtext = r#"
|
||||||
|
Some [example text](http://example.com).
|
||||||
|
Some more [foo](http://example.com/foo).
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let exp1 = Link {
|
||||||
|
title: String::from("example text"),
|
||||||
|
link: String::from("http://example.com"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let exp2 = Link {
|
||||||
|
title: String::from("foo"),
|
||||||
|
link: String::from("http://example.com/foo"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut links = extract_links(&testtext[..]);
|
||||||
|
assert_eq!(2, links.len());
|
||||||
|
assert_eq!(exp2, links.pop().unwrap());
|
||||||
|
assert_eq!(exp1, links.pop().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
6
libimagentrymarkdown/src/result.rs
Normal file
6
libimagentrymarkdown/src/result.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
use std::result::Result as RResult;
|
||||||
|
|
||||||
|
use error::MarkdownError;
|
||||||
|
|
||||||
|
pub type Result<T> = RResult<T, MarkdownError>;
|
||||||
|
|
Loading…
Reference in a new issue