// // imag - the personal information management suite for the commandline // Copyright (C) 2015-2018 Matthias Beyer and contributors // // 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 // use failure::ResultExt; use failure::Fallible as Result; use failure::Error; use failure::err_msg; use hoedown::renderer::Render; use hoedown::Buffer; use hoedown::Markdown; use url::Url; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Link { pub title: String, pub link: String, } impl Link { /// Translate a `Link` into a `UrlLink` pub fn into_urllink(self) -> Result { Url::parse(&self.link[..]) .map(move |link| UrlLink { title: self.title, link: link, }) .context(err_msg("Link parsing error")) .map_err(Error::from) } } pub struct UrlLink { pub title: String, pub link: Url, } #[derive(Debug)] struct LinkExtractor { links: Vec, } impl LinkExtractor { pub fn new() -> LinkExtractor { LinkExtractor { links: vec![] } } pub fn links(self) -> Vec { 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); trace!("Processing..."); trace!("link = {:?}", link); trace!("content = {:?}", content); match (link, content) { (Some(link), Some(content)) => { self.links.push(Link { link: link, title: content }); false }, (_, _) => { false }, } } } pub fn extract_links(buf: &str) -> Vec { let mut le = LinkExtractor::new(); le.render(&Markdown::new(buf)); trace!("Extracted: {:?}", le); 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()); } }