libzettels 0.4.1

A library intended as a backend for applications which implement Niklas Luhmann's system of a 'Zettelkasten'.
Documentation
//Copyright (c) 2020 Stefan Thesing
//
//This file is part of libzettels.
//
//libzettels is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//libzettels 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 General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with libzettels. If not, see http://www.gnu.org/licenses/.

//! Markdown links have several edge cases, especially when parentheses
//! (round brackets) and/or square brackets are present in and around
//! the hyperlink.
//! These tests try to ensure that libzettels indexing is able to handle
//! all edge cases that still are valid markdown.
// --------------------------------------------------------------------------

extern crate libzettels;
extern crate tempfile;
use self::tempfile::tempdir;

use libzettels::{Config, Error, Index, IndexingMethod};
use libzettels::examples::*;
use std::fs::File;
use std::io::Write;

const FILE_BOILERPLATE: &str = "---
title:  'Edge cases of inline markdown link syntax'
keywords: []
followups: []
...

No matter which indexing method the user chooses, the following should not break things:

";

fn boilerplate(method: IndexingMethod, file_content: &str) 
        -> Result<Index, Error>{
    let tmp_dir = tempdir().expect("Failed to setup temp dir");
    let dir = tmp_dir.path();
    generate_bare_examples(dir).expect("Failed to generate examples");
    let rootdir = dir.join("examples/Zettelkasten");
    let mut dummy_file = File::create(rootdir.join("f(o)o.md"))
                                .expect("Failed to create dummy file with parentheses in filename.");
    // write minimal stuff to the dummy file, just to be sure.
    writeln!(dummy_file, "{}", "foo").expect("Failed to write to file");
    
    let mut parentheses_file = File::create(
                                rootdir.join("test_file.md")
                                ).expect("Failed to create temporary file.");
    writeln!(parentheses_file, "{}", FILE_BOILERPLATE)
                                .expect("Failed to write to temporary file.");
    writeln!(parentheses_file, "{}", file_content)
                                .expect("Failed to write to temporary file.");
    
    let mut cfg = Config::new(rootdir.to_str().unwrap(), "foo");
    cfg.indexingmethod = method;
    Index::new(&cfg)
}

// -------------------------------------------------------------------------
// Tests
// -------------------------------------------------------------------------

// Links followed by parentheses
const PARENTHESES_FOLLOW: &str = "A [normal hyperlink](file1.md) (followed by parentheses) to a file";

#[test]
fn test_parentheses_follow_native() {
    let index = boilerplate(IndexingMethod::Native, PARENTHESES_FOLLOW);
    assert!(index.is_ok());
}

#[test]
fn test_parentheses_follow_grep() {
    let index = boilerplate(IndexingMethod::Grep, PARENTHESES_FOLLOW);
    assert!(index.is_ok());
}

#[test]
fn test_parentheses_follow_ripgrep() {
    let index = boilerplate(IndexingMethod::RipGrep, PARENTHESES_FOLLOW);
    assert!(index.is_ok());
}

// Parentheses in link text
const PARENTHESES_IN_LINK_TEXT: &str = "A [hyperlink (with parentheses) in the link text](file1.md) to a file";

#[test]
fn test_parentheses_in_link_text_native() {
    let index = boilerplate(IndexingMethod::Native, PARENTHESES_IN_LINK_TEXT);
    assert!(index.is_ok());
}

#[test]
fn test_parentheses_in_link_text_grep() {
    let index = boilerplate(IndexingMethod::Grep, PARENTHESES_IN_LINK_TEXT);
    assert!(index.is_ok());
}

#[test]
fn test_parentheses_in_link_text_ripgrep() {
    let index = boilerplate(IndexingMethod::RipGrep, PARENTHESES_IN_LINK_TEXT);
    assert!(index.is_ok());
}

// Parentheses in link text
const PARENTHESES_IN_LINK_TEXT_AND_FOLLOW: &str = "A [hyperlink (with parentheses) in the link text](file1.md) (followed by parentheses) to a file";

#[test]
fn test_parentheses_in_link_text_and_follow_native() {
    let index = boilerplate(IndexingMethod::Native, PARENTHESES_IN_LINK_TEXT_AND_FOLLOW);
    assert!(index.is_ok());
}

#[test]
fn test_parentheses_in_link_text_and_follow_grep() {
    let index = boilerplate(IndexingMethod::Grep, PARENTHESES_IN_LINK_TEXT_AND_FOLLOW);
    assert!(index.is_ok());
}

#[test]
fn test_parentheses_in_link_text_and_follow_ripgrep() {
    let index = boilerplate(IndexingMethod::RipGrep, PARENTHESES_IN_LINK_TEXT_AND_FOLLOW);
    assert!(index.is_ok());
}

// A hyperlink inside parentheses
const HYPERLINK_INSIDE_PARENTHESES: &str = "A (link [inside](file1.md) parentheses) to a file";

#[test]
fn test_hyperlink_inside_parentheses_native() {
    let index = boilerplate(IndexingMethod::Native, 
                            HYPERLINK_INSIDE_PARENTHESES);
    assert!(index.is_ok());
}

#[test]
fn test_hyperlink_inside_parentheses_grep() {
    let index = boilerplate(IndexingMethod::Grep, 
                            HYPERLINK_INSIDE_PARENTHESES);
    assert!(index.is_ok());
}

#[test]
fn test_hyperlink_inside_parentheses_ripgrep() {
    let index = boilerplate(IndexingMethod::RipGrep, 
                            HYPERLINK_INSIDE_PARENTHESES);
    assert!(index.is_ok());
}

// A hyperlink directly inside parentheses
const HYPERLINK_DIRECTLY_INSIDE_PARENTHESES: &str = "A ([link directly inside parentheses](file1.md)) to a file";

#[test]
fn test_hyperlink_directly_inside_parentheses_native() {
    let index = boilerplate(IndexingMethod::Native, 
                            HYPERLINK_DIRECTLY_INSIDE_PARENTHESES);
    assert!(index.is_ok());
}

#[test]
fn test_hyperlink_directly_inside_parentheses_grep() {
    let index = boilerplate(IndexingMethod::Grep, 
                            HYPERLINK_DIRECTLY_INSIDE_PARENTHESES);
    assert!(index.is_ok());
}

#[test]
fn test_hyperlink_directly_inside_parentheses_ripgrep() {
    let index = boilerplate(IndexingMethod::RipGrep, 
                            HYPERLINK_DIRECTLY_INSIDE_PARENTHESES);
    assert!(index.is_ok());
}

// A hyperlink inside parentheses followed by parenthese
const HYPERLINK_INSIDE_PARENTHESES_FOLLOW: &str = "A (link [inside](file1.md) parentheses) (followed by parentheses) to a file";

#[test]
fn test_hyperlink_inside_parentheses_follow_native() {
    let index = boilerplate(IndexingMethod::Native, 
                            HYPERLINK_INSIDE_PARENTHESES_FOLLOW);
    assert!(index.is_ok());
}

#[test]
fn test_hyperlink_inside_parentheses_follow_grep() {
    let index = boilerplate(IndexingMethod::Grep, 
                            HYPERLINK_INSIDE_PARENTHESES_FOLLOW);
    assert!(index.is_ok());
}

#[test]
fn test_hyperlink_inside_parentheses_follow_ripgrep() {
    let index = boilerplate(IndexingMethod::RipGrep, 
                            HYPERLINK_INSIDE_PARENTHESES_FOLLOW);
    assert!(index.is_ok());
}

// A hyperlink directly inside parentheses followed by parentheses
const HYPERLINK_DIRECTLY_INSIDE_PARENTHESES_FOLLOW: &str = "A ([link directly inside parentheses](file1.md)) (followed by parentheses) to a file";

#[test]
fn test_hyperlink_directly_inside_parentheses_follow_native() {
    let index = boilerplate(IndexingMethod::Native, 
                            HYPERLINK_DIRECTLY_INSIDE_PARENTHESES_FOLLOW);
    assert!(index.is_ok());
}

#[test]
fn test_hyperlink_directly_inside_parentheses_follow_grep() {
    let index = boilerplate(IndexingMethod::Grep, 
                            HYPERLINK_DIRECTLY_INSIDE_PARENTHESES_FOLLOW);
    assert!(index.is_ok());
}

#[test]
fn test_hyperlink_directly_inside_parentheses_follow_ripgrep() {
    let index = boilerplate(IndexingMethod::RipGrep, 
                            HYPERLINK_DIRECTLY_INSIDE_PARENTHESES_FOLLOW);
    assert!(index.is_ok());
}

// Link to a file with parentheses in the filename
const TO_FILENAME_PARENTHESES: &str = " A [hyperlink](f(o)o.md) to a file with parentheses in the filename";

#[test]
fn test_to_filename_parentheses_native() {
    let index = boilerplate(IndexingMethod::Native, TO_FILENAME_PARENTHESES);
    assert!(index.is_ok());
}

#[test]
fn test_to_filename_parentheses_grep() {
    let index = boilerplate(IndexingMethod::Grep, TO_FILENAME_PARENTHESES);
    assert!(index.is_ok());
}

#[test]
fn test_to_filename_parentheses_ripgrep() {
    let index = boilerplate(IndexingMethod::RipGrep, TO_FILENAME_PARENTHESES);
    assert!(index.is_ok());
}

// Links followed by a single closing parenthesis
const PARENTHESES_FOLLOW_CLOSING: &str = "A [normal hyperlink](file1.md))  to a file followed by a single closing parenthesis";

#[test]
fn test_parentheses_follow_closing_native() {
    let index = boilerplate(IndexingMethod::Native, PARENTHESES_FOLLOW_CLOSING);
    assert!(index.is_ok());
}

#[test]
fn test_parentheses_follow_closing_grep() {
    let index = boilerplate(IndexingMethod::Grep, PARENTHESES_FOLLOW_CLOSING);
    assert!(index.is_ok());
}

#[test]
fn test_parentheses_follow_closing_ripgrep() {
    let index = boilerplate(IndexingMethod::RipGrep, PARENTHESES_FOLLOW_CLOSING);
    assert!(index.is_ok());
}