2016-10-01 15:35:06 +00:00
|
|
|
//
|
|
|
|
// imag - the personal information management suite for the commandline
|
2018-02-07 01:48:53 +00:00
|
|
|
// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> and contributors
|
2016-10-01 15:35:06 +00:00
|
|
|
//
|
|
|
|
// This library is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
|
|
// License as published by the Free Software Foundation; version
|
|
|
|
// 2.1 of the License.
|
|
|
|
//
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
|
|
// License along with this library; if not, write to the Free Software
|
|
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
//
|
|
|
|
|
2016-05-29 09:48:37 +00:00
|
|
|
use error::MarkdownErrorKind as MEK;
|
2017-09-03 19:28:36 +00:00
|
|
|
use error::ResultExt;
|
2017-09-03 19:39:32 +00:00
|
|
|
use error::Result;
|
2016-05-23 12:11:32 +00:00
|
|
|
|
2016-05-24 14:30:06 +00:00
|
|
|
use hoedown::renderer::Render;
|
|
|
|
use hoedown::Buffer;
|
|
|
|
use hoedown::Markdown;
|
2016-05-29 09:48:37 +00:00
|
|
|
use url::Url;
|
|
|
|
|
2016-05-25 11:37:14 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
2016-05-24 14:30:06 +00:00
|
|
|
pub struct Link {
|
|
|
|
pub title: String,
|
|
|
|
pub link: String,
|
|
|
|
}
|
|
|
|
|
2016-05-29 09:48:37 +00:00
|
|
|
impl Link {
|
|
|
|
|
|
|
|
/// Translate a `Link` into a `UrlLink`
|
2017-02-05 14:03:05 +00:00
|
|
|
pub fn into_urllink(self) -> Result<UrlLink> {
|
2016-05-29 09:48:37 +00:00
|
|
|
Url::parse(&self.link[..])
|
|
|
|
.map(move |link| UrlLink { title: self.title, link: link, })
|
2017-09-03 19:28:36 +00:00
|
|
|
.chain_err(|| MEK::LinkParsingError)
|
2016-05-29 09:48:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct UrlLink {
|
|
|
|
pub title: String,
|
|
|
|
pub link: Url,
|
|
|
|
}
|
|
|
|
|
2017-09-05 16:21:44 +00:00
|
|
|
#[derive(Debug)]
|
2016-05-24 14:30:06 +00:00
|
|
|
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);
|
|
|
|
|
2017-09-05 16:21:44 +00:00
|
|
|
trace!("Processing...");
|
|
|
|
trace!("link = {:?}", link);
|
|
|
|
trace!("content = {:?}", content);
|
|
|
|
|
2016-05-24 14:30:06 +00:00
|
|
|
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));
|
2017-09-05 16:21:44 +00:00
|
|
|
trace!("Extracted: {:?}", le);
|
2016-05-24 14:30:06 +00:00
|
|
|
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())
|
|
|
|
}
|
2016-05-23 12:11:32 +00:00
|
|
|
|
2016-05-29 09:16:55 +00:00
|
|
|
#[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());
|
|
|
|
}
|
|
|
|
|
2016-05-29 09:18:29 +00:00
|
|
|
#[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());
|
|
|
|
}
|
|
|
|
|
2016-05-23 12:11:32 +00:00
|
|
|
}
|
|
|
|
|