libimagentrymarkdown 0.10.1

Library for the imag core distribution
Documentation
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2020 Matthias Beyer <mail@beyermatthias.de> 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<UrlLink> {
        Url::parse(&self.link[..])
            .map(move |link| UrlLink { title: self.title, 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<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);

        trace!("Processing...");
        trace!("link    = {:?}", link);
        trace!("content = {:?}", content);

        match (link, content) {
            (Some(link), Some(content)) => {
                self.links.push(Link { link, title: content });
                false
            },

            (_, _) => {
                false
            },
        }

    }

}

pub fn extract_links(buf: &str) -> Vec<Link> {
    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());
    }

}