use std::path::{Path, PathBuf};
use std::collections::{BTreeMap, HashSet};
use std::fs;
use std::fs::File;
use std::time::{SystemTime};
use backstage::configuration::Config;
use backstage::error::Error;
use backstage::indexing;
use backstage::querying::{self};
use backstage::querying::sequences::SequenceStart;
use backstage::zettel::Zettel;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Index {
pub files: BTreeMap<PathBuf, Zettel>,
pub timestamp: SystemTime,
}
impl Index {
pub fn new(cfg: &Config) -> Result<Index, Error> {
indexing::create_index(&cfg.indexingmethod,
&cfg.rootdir,
&cfg.ignorefile) }
pub fn load(cfg: &Config) -> Result<Index, Error> {
Index::from_file(&cfg.indexfile)
}
pub fn update(&mut self, cfg: &Config) -> Result<(), Error> {
indexing::update_index(self,
&cfg.indexingmethod,
&cfg.rootdir,
&cfg.ignorefile) }
pub fn save(&self, cfg: &Config) -> Result<(), Error> {
self.to_file(&cfg.indexfile)
}
pub fn get_zettel<P: AsRef<Path>>(&self, key: P) -> Option<&Zettel> {
self.files.get(key.as_ref())
}
pub fn sequences(&self,
scope: &HashSet<PathBuf>,
cfg_sequence_start: &SequenceStart)
-> HashSet<PathBuf> {
querying::sequences::sequences(&self, scope, cfg_sequence_start)
}
pub fn zettels_of_sequence<P: AsRef<Path>>(&self, key: P)
-> HashSet<PathBuf> {
querying::sequences::zettels_of_sequence(&self, key)
}
pub fn sequence_tree(&self, scope: &HashSet<PathBuf>) -> HashSet<PathBuf> {
querying::sequences::sequence_tree(&self, &scope)
}
pub fn sequence_tree_whole(&self, scope: &HashSet<PathBuf>)
-> HashSet<PathBuf> {
querying::sequences::sequence_tree_whole(&self, scope)
}
pub fn parents_of_zettel<P: AsRef<Path>>(&self, key: P)
-> HashSet<PathBuf> {
let scope = vec![key.as_ref().to_path_buf()];
querying::sequences::parents_of_scope(&self, scope)
}
pub fn inspect_links(&self, scope: &HashSet<PathBuf>)
-> HashSet<PathBuf> {
let mut links = HashSet::new();
for linker in scope {
let z = &self.get_zettel(linker);
if z.is_some() {
for link in &z.unwrap().links {
links.insert(link.to_path_buf());
}
}
}
links
}
pub fn inspect_incoming_links(&self, scope: &HashSet<PathBuf>, all: bool)
-> HashSet<PathBuf> {
querying::inspect_incoming(&self, &scope, all)
}
pub fn search_keywords(&self, searched_keywords: Vec<String>,
all: bool) -> HashSet<PathBuf> {
querying::search_index_for_keywords(&self, searched_keywords, all)
}
pub fn search_title<T: AsRef<str>>(&self,
search_term: T,
exact: bool)
-> HashSet<PathBuf> {
querying::search_index_for_title(&self, search_term, exact)
}
pub fn combi_search<T: AsRef<str>>(&self,
searched_keywords: Vec<String>,
all: bool,
search_term: T,
exact: bool)
-> HashSet<PathBuf> {
querying::combi_search(&self, searched_keywords, all,
search_term, exact)
}
pub fn get_keywords(&self) -> HashSet<String> {
querying::get_keywords(&self)
}
pub fn count_keywords(&self) -> BTreeMap<String, u32> {
querying::count_keywords(&self)
}
pub fn empty() -> Index {
Index {
files: BTreeMap::new(),
timestamp: SystemTime::now(),
}
}
pub fn from_yaml<T: AsRef<str>>(yaml: T) -> Result<Index, Error> {
let index: Index = serde_yaml::from_str(yaml.as_ref())?; Ok(index)
}
pub fn from_file<P: AsRef<Path>>(indexfile: P) -> Result<Index, Error> {
let indexfile = indexfile.as_ref();
debug!("Loading index from file: {:?}", indexfile);
let yaml = fs::read_to_string(indexfile)?; Index::from_yaml(yaml) }
pub fn update_timestamp(&mut self) {
self.timestamp = SystemTime::now();
}
pub fn to_file<P: AsRef<Path>>(&self, indexfile: P) -> Result<(), Error> {
use std::io::Write;
let indexfile = indexfile.as_ref();
debug!("Writing index to file {:?}", indexfile);
let s = serde_yaml::to_string(self)?;
let mut file = File::create(indexfile)?;
writeln!(file, "{}", s)?;
Ok(())
}
pub fn add_zettel<P: AsRef<Path>>(&mut self, key: P, zettel: Zettel) {
self.files.insert(key.as_ref().to_path_buf(), zettel);
}
pub fn remove_zettel<P: AsRef<Path>>(&mut self, key: P) -> Option<Zettel> {
self.files.remove(key.as_ref())
}
pub fn get_mut_zettel<P: AsRef<Path>>(&mut self, key: P) -> Option<&mut Zettel> {
self.files.get_mut(key.as_ref())
}
}
#[cfg(test)]
mod tests {
use super::*;
use IndexingMethod;
use std::io::Write;
extern crate tempfile;
use self::tempfile::tempdir;
use examples::*;
const YAML: &str = "---
files:
file1.md:
title: File 1
followups: [subdir/file2.md]
keywords: [example]
links: []
subdir/file2.md:
title: File 2
followups: []
keywords: [example, second]
links: [file1.md]
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 449322318";
#[test]
fn test_index_new() {
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_config(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let cfg_file = config_dir.join("libzettels.cfg.yaml");
let mut cfg = Config::from_file(cfg_file)
.expect("Failed to read config from file.");
let index = Index::new(&cfg);
cfg.indexingmethod = IndexingMethod::Grep;
assert!(index.is_ok());
let index_grep = index.unwrap();
cfg.indexingmethod = IndexingMethod::RipGrep;
let index = Index::new(&cfg);
assert!(index.is_ok());
let index_rg = index.unwrap();
cfg.indexingmethod = IndexingMethod::Native;
let index = Index::new(&cfg);
assert!(index.is_ok());
let index_native = index.unwrap();
assert_eq!(index_grep.files, index_rg.files);
assert_eq!(index_grep.files, index_native.files);
}
#[test]
fn test_index_load() {
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let cfg_file = config_dir.join("libzettels.cfg.yaml");
let cfg = Config::from_file(cfg_file)
.expect("Failed to read config from file.");
let index = Index::load(&cfg);
assert!(index.is_ok());
}
#[test]
fn test_index_update() {
use std::io::Write;
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let cfg_file = config_dir.join("libzettels.cfg.yaml");
let cfg = Config::from_file(cfg_file)
.expect("Failed to read config from file.");
let mut index = Index::load(&cfg)
.expect("Failed to read index from file.");
let mut file6 = std::fs::File::create(&cfg.rootdir.join("file6.md"))
.expect("Failed to create file6.md");
writeln!(file6, "{}", FILE6)
.expect("Failed to write to file");
assert!(index.update(&cfg).is_ok());
assert!(index.files.contains_key(std::path::Path::new("file6.md")));
}
const FILE6: &str ="---
title: 'File 6'
keywords: [example6]
followups: [file1.md]
lang: de
...
# Bla bla
Lorem ipsum
[//]: # (Links)
";
#[test]
fn test_index_empty() {
let i1 = Index {
files: BTreeMap::new(),
timestamp: SystemTime::now(),
};
let i2 = Index::empty();
assert!(i2.files.is_empty());
assert_eq!(i2.files, i1.files);
assert!(i1.timestamp < i2.timestamp);
}
#[test]
fn test_index_from_yaml() {
let index = Index::from_yaml(YAML);
assert!(index.is_ok());
}
#[test]
fn test_index_from_file() {
let tmp_dir = tempdir().expect("Failed setting up temp directory.");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let file_path = dir.join("examples/config/index.yaml");
let index = Index::from_file(&file_path);
assert!(index.is_ok());
}
#[test]
fn test_index_add_zettel() {
let mut index = Index {
files: BTreeMap::new(),
timestamp: SystemTime::now(),
};
let zettel = Zettel::new("Test Zettel");
let zettel_copy = zettel.clone();
index.add_zettel(&PathBuf::from("test_zettel.md"), zettel);
assert_eq!(index.files.len(), 1);
assert_eq!(index.files.get(&PathBuf::from("test_zettel.md")).unwrap(),
&zettel_copy);
}
#[test]
fn test_index_get_zettel() {
let mut index = Index {
files: BTreeMap::new(),
timestamp: SystemTime::now(),
};
let zettel = Zettel::new("Test Zettel");
let zettel_copy = zettel.clone();
index.files.insert(PathBuf::from("test_zettel.md"), zettel);
assert_eq!(index.get_zettel(PathBuf::from("test_zettel.md")).unwrap(),
&zettel_copy);
}
#[test]
fn test_index_save() {
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let cfg_file = config_dir.join("libzettels.cfg.yaml");
let cfg = Config::from_file(cfg_file)
.expect("Failed to read config from file.");
let index = Index::load(&cfg)
.expect("Failed to read index from file.");
assert!(index.save(&cfg).is_ok());
}
#[test]
fn test_index_remove_zettel() {
let mut index = Index {
files: BTreeMap::new(),
timestamp: SystemTime::now(),
};
let zettel = Zettel::new("Test Zettel");
let key = PathBuf::from("test_zettel.md");
index.files.insert(key.clone(), zettel);
assert_eq!(index.files.len(), 1);
index.remove_zettel(key);
assert!(index.files.is_empty());
}
#[test]
fn test_index_update_timestamp() {
let mut index = Index {
files: BTreeMap::new(),
timestamp: SystemTime::now(),
};
let t1 = index.timestamp;
index.update_timestamp();
assert!(t1 < index.timestamp);
}
#[test]
fn test_index_get_mut_zettel() {
let mut index = Index {
files: BTreeMap::new(),
timestamp: SystemTime::now(),
};
let zettel = Zettel::new("Test Zettel");
let key = PathBuf::from("test_zettel.md");
index.files.insert(key.clone(), zettel);
{
let z = index.get_mut_zettel(&key);
assert!(z.is_some());
let z = z.unwrap();
z.title = String::from("Changed Title 1");
}
let z = index.get_zettel(&key).unwrap();
assert_eq!(z.title, "Changed Title 1");
}
#[test]
fn test_index_to_file() {
let tmp_dir = tempdir().expect("Failed to create temp dir.");
let indexfile = tmp_dir.path().join("index.yaml");
let mut index = Index::empty();
let z1 = Zettel::new("Zettel 1");
let z2 = Zettel::new("Zettel 2");
index.add_zettel(Path::new("foo"), z1);
index.add_zettel(Path::new("bar"), z2);
assert!(index.to_file(indexfile).is_ok());
}
#[test]
fn test_index_new_no_rootdir() {
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_config(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let _rootdir = dir.join("Zettelkasten/");
let indexfile = config_dir.join("index.yaml");
let _cfg_file = config_dir.join("libzettels.cfg.yaml");
let cfg = Config::new(dir.join("foo"), indexfile);
let index = Index::new(&cfg);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Io(inner) => {
match inner.kind() {
std::io::ErrorKind::NotFound => {
assert!(inner.to_string().contains("No such file or \
directory"));
},
_ => panic!("Expected a NotFound error, got: {:#?}", inner)
}
},
_ => panic!("Expected a Io error, got: {:#?}", e),
}
}
#[test]
fn test_index_load_non_existing_file() {
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_config(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let rootdir = dir.join("Zettelkasten/");
let _cfg_file = config_dir.join("libzettels.cfg.yaml");
let indexfile = config_dir.join("foo");
let cfg = Config::new(rootdir,
indexfile);
let index = Index::load(&cfg);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Io(inner) => {
match inner.kind() {
std::io::ErrorKind::NotFound => {
assert!(inner.to_string().contains("No such file or \
directory"));
},
_ => panic!("Expected a NotFound error, got: {:#?}", inner)
}
},
_ => panic!("Expected a Io error, got: {:#?}", e),
}
}
#[test]
fn test_index_load_missing_field() {
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_config(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let rootdir = dir.join("Zettelkasten/");
let indexfile = config_dir.join("index.yaml");
let _cfg_file = config_dir.join("libzettels.cfg.yaml");
let cfg = Config::new(rootdir, indexfile.to_path_buf());
let mut f = std::fs::File::create(&indexfile)
.expect("Failed to create index file");
let erronous_index = "---
file:
file1.md:
title: File 1
followups: [file2.md]
keywords: [example, first]
links: [subdir/file3.md]
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 449322318";
writeln!(f, "{}", erronous_index).expect("Failed to write to file");
let index = Index::load(&cfg);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("missing field `files`"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_load_duplicate_field() {
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_config(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let rootdir = dir.join("Zettelkasten/");
let indexfile = config_dir.join("index.yaml");
let _cfg_file = config_dir.join("libzettels.cfg.yaml");
let cfg = Config::new(rootdir, indexfile.to_path_buf());
let mut f = std::fs::File::create(&indexfile)
.expect("Failed to create index file");
let erronous_index = "---
files:
file1.md:
title: File 1
followups: [file2.md]
keywords: [example, first]
links: [subdir/file3.md]
files: foo
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 449322318";
write!(f, "{}", erronous_index).expect("Failed to write to file");
let index = Index::load(&cfg);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("duplicate field `files`"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_load_invalid_type() {
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_config(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let rootdir = dir.join("Zettelkasten/");
let indexfile = config_dir.join("index.yaml");
let _cfg_file = config_dir.join("libzettels.cfg.yaml");
let cfg = Config::new(rootdir, indexfile.to_path_buf());
let mut f = std::fs::File::create(&indexfile)
.expect("Failed to create index file");
let erronous_index = "---
files: foo
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 449322318";
write!(f, "{}", erronous_index).expect("Failed to write to file");
let index = Index::load(&cfg);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("invalid type"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_load_unknown_field() {
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_config(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let rootdir = dir.join("Zettelkasten/");
let indexfile = config_dir.join("index.yaml");
let _cfg_file = config_dir.join("libzettels.cfg.yaml");
let cfg = Config::new(rootdir, indexfile.to_path_buf());
let mut f = std::fs::File::create(&indexfile)
.expect("Failed to create index file");
let erronous_index = "---
files:
file1.md:
title: File 1
followups: [file2.md]
keywords: [example, first]
links: [subdir/file3.md]
timestamp:
secs_since_epoc: 1543078763
nanos_since_epoch: 449322318";
write!(f, "{}", erronous_index).expect("Failed to write to file");
let index = Index::load(&cfg);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("unknown field"));
assert!(message.contains("expected `secs_since_epoch`"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_load_invalid_value() {
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_config(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let rootdir = dir.join("Zettelkasten/");
let indexfile = config_dir.join("index.yaml");
let _cfg_file = config_dir.join("libzettels.cfg.yaml");
let cfg = Config::new(rootdir, indexfile.to_path_buf());
let mut f = std::fs::File::create(&indexfile)
.expect("Failed to create index file");
let erronous_index = "---
files:
file1.md:
title: File 1
followups: [file2.md]
keywords: [example, first]
links: [subdir/file3.md]
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 44932231800000000";
write!(f, "{}", erronous_index).expect("Failed to write to file");
let index = Index::load(&cfg);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("invalid value"));
assert!(message.contains("expected u32"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_update_bad_yaml() {
use std::io::Write;
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let cfg_file = config_dir.join("libzettels.cfg.yaml");
let cfg = Config::from_file(cfg_file)
.expect("Failed to read config from file.");
let mut index = Index::load(&cfg)
.expect("Failed to read index from file.");
let mut file6 = std::fs::File::create(&cfg.rootdir.join("file6.md"))
.expect("Failed to create file6.md");
let erroneous_file = "---
title: 'File 6'
title: Foo
";
writeln!(file6, "{}", erroneous_file)
.expect("Failed to write to file");
let result = index.update(&cfg);
assert!(result.is_err());
let e = result.unwrap_err();
match e {
Error::BadHeader(_, inner) => {
let message = inner.to_string();
assert!(message.contains("duplicate field"));
},
_ => panic!("Expected a BadHeader error, got: {:#?}", e),
}
}
#[test]
fn test_index_update_one_file_only_grep() {
use std::io::Write;
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let cfg_file = config_dir.join("libzettels.cfg.yaml");
let cfg = Config::from_file(cfg_file)
.expect("Failed to read config from file.");
let mut index = Index::load(&cfg)
.expect("Failed to read index from file.");
let mut file7 = std::fs::File::create(&cfg.rootdir.join("file7.md"))
.expect("Failed to create file7.md");
writeln!(file7, "{}", FILE7)
.expect("Failed to write to file");
let r = index.update(&cfg);
assert!(r.is_ok());
assert!(index.files.contains_key(std::path::Path::new("file7.md")));
}
#[test]
fn test_index_update_one_file_only_ripgrep() {
use std::io::Write;
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let cfg_file = config_dir.join("libzettels.cfg.yaml");
let mut cfg = Config::from_file(cfg_file)
.expect("Failed to read config from file.");
cfg.indexingmethod = IndexingMethod::RipGrep;
let mut index = Index::load(&cfg)
.expect("Failed to read index from file.");
let mut file7 = std::fs::File::create(&cfg.rootdir.join("file7.md"))
.expect("Failed to create file7.md");
writeln!(file7, "{}", FILE7)
.expect("Failed to write to file");
let r = index.update(&cfg);
assert!(r.is_ok());
assert!(index.files.contains_key(std::path::Path::new("file7.md")));
}
#[test]
fn test_index_update_one_file_only_native() {
use std::io::Write;
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let cfg_file = config_dir.join("libzettels.cfg.yaml");
let mut cfg = Config::from_file(cfg_file)
.expect("Failed to read config from file.");
cfg.indexingmethod = IndexingMethod::Native;
let mut index = Index::load(&cfg)
.expect("Failed to read index from file.");
let mut file7 = std::fs::File::create(&cfg.rootdir.join("file7.md"))
.expect("Failed to create file7.md");
writeln!(file7, "{}", FILE7)
.expect("Failed to write to file");
let r = index.update(&cfg);
assert!(r.is_ok());
assert!(index.files.contains_key(std::path::Path::new("file7.md")));
}
const FILE7: &str ="---
title: 'File 7'
keywords: [example7]
followups: [file1.md]
lang: de
...
# Bla bla
Lorem [ipsum](file2.md)
[//]: # (Links)
";
#[test]
fn test_index_get_zettel_none() {
use std::collections::BTreeMap;
use std::time::SystemTime;
use std::path::PathBuf;
let index = Index {
files: BTreeMap::new(),
timestamp: SystemTime::now(),
};
let nz = index.get_zettel(PathBuf::from("non_existing_zettel.md"));
assert!(nz.is_none());
}
#[test]
fn test_index_save_non_existing_file() {
let tmp_dir = tempdir().expect("Failed to create temp directory");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config");
let cfg_file = config_dir.join("libzettels.cfg.yaml");
let mut cfg = Config::from_file(cfg_file)
.expect("Failed to read config from file.");
let index = Index::load(&cfg)
.expect("Failed to read index from file.");
cfg.indexfile = config_dir.join("nonexisting_dir/foo");
let result = index.save(&cfg);
assert!(result.is_err());
let e = result.unwrap_err();
match e {
Error::Io(inner) => {
match inner.kind() {
std::io::ErrorKind::NotFound => {
assert!(inner.to_string().contains("No such file or directory"));
},
_ => panic!("Expected a NotFound error, got: {:#?}", inner)
}
},
_ => panic!("Expected a Io error, got: {:#?}", e),
}
}
#[test]
fn test_index_from_yaml_unkown_field() {
let yaml = "---
files:
file1.md:
title: File 1
followups: [file2.md]
keywords: [example, first]
links: [subdir/file3.md]
timestamp:
secs_since_epoc: 1543078763
nanos_since_epoch: 449322318";
let index = Index::from_yaml(yaml);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("unknown field"));
assert!(message.contains("expected `secs_since_epoch`"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_from_yaml_missing_field() {
let yaml = "---
fies:
file1.md:
title: File 1
followups: [file2.md]
keywords: [example, first]
links: [subdir/file3.md]
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 449322318";
let index = Index::from_yaml(yaml);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("missing field `files`"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_from_yaml_duplicate_field() {
let yaml = "---
files:
file1.md:
title: File 1
followups: [file2.md]
keywords: [example, first]
links: [subdir/file3.md]
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 449322318
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 449322318";
let index = Index::from_yaml(yaml);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("duplicate field `timestamp`"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_from_yaml_invalid_type() {
let yaml = "---
files: foo
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 449322318";
let index = Index::from_yaml(yaml);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("invalid type"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_from_file_non_existing_file() {
let tmp_dir = tempdir().expect("Failed setting up temp directory.");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config/");
let indexfile = config_dir.join("foo");
let index = Index::from_file(indexfile);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Io(inner) => {
match inner.kind() {
std::io::ErrorKind::NotFound => {
assert!(inner.to_string().contains("No such file or \
directory"));
},
_ => panic!("Expected a NotFound error, got: {:#?}", inner)
}
},
_ => panic!("Expected a Io error, got: {:#?}", e),
}
}
#[test]
fn test_index_from_file_missing_field() {
let tmp_dir = tempdir().expect("Failed setting up temp directory.");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config/");
let indexfile = config_dir.join("index.yaml");
let mut f = std::fs::File::create(&indexfile)
.expect("Failed to create index file");
let erronous_index = "---
file:
file1.md:
title: File 1
followups: [file2.md]
keywords: [example, first]
links: [subdir/file3.md]
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 449322318";
writeln!(f, "{}", erronous_index).expect("Failed to write to file");
let index = Index::from_file(indexfile);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("missing field `files`"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_from_file_duplicate_field() {
let tmp_dir = tempdir().expect("Failed setting up temp directory.");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config/");
let indexfile = config_dir.join("index.yaml");
let mut f = std::fs::File::create(&indexfile)
.expect("Failed to create index file");
let erronous_index = "---
files:
file1.md:
title: File 1
followups: [file2.md]
keywords: [example, first]
links: [subdir/file3.md]
files: foo
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 449322318";
write!(f, "{}", erronous_index).expect("Failed to write to file");
let index = Index::from_file(indexfile);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("duplicate field `files`"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_from_file_invalid_type() {
let tmp_dir = tempdir().expect("Failed setting up temp directory.");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config/");
let indexfile = config_dir.join("index.yaml");
let mut f = std::fs::File::create(&indexfile)
.expect("Failed to create index file");
let erronous_index = "---
files: foo
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 449322318";
write!(f, "{}", erronous_index).expect("Failed to write to file");
let index = Index::from_file(indexfile);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("invalid type"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_from_file_unknown_field() {
let tmp_dir = tempdir().expect("Failed setting up temp directory.");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config/");
let indexfile = config_dir.join("index.yaml");
let mut f = std::fs::File::create(&indexfile)
.expect("Failed to create index file");
let erronous_index = "---
files:
file1.md:
title: File 1
followups: [file2.md]
keywords: [example, first]
links: [subdir/file3.md]
timestamp:
secs_since_epoc: 1543078763
nanos_since_epoch: 449322318";
write!(f, "{}", erronous_index).expect("Failed to write to file");
let index = Index::from_file(indexfile);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("unknown field"));
assert!(message.contains("expected `secs_since_epoch`"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_from_file_invalid_value() {
let tmp_dir = tempdir().expect("Failed setting up temp directory.");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config/");
let indexfile = config_dir.join("index.yaml");
let mut f = std::fs::File::create(&indexfile)
.expect("Failed to create index file");
let erronous_index = "---
files:
file1.md:
title: File 1
followups: [file2.md]
keywords: [example, first]
links: [subdir/file3.md]
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 44932231800000000";
write!(f, "{}", erronous_index).expect("Failed to write to file");
let index = Index::from_file(indexfile);
assert!(index.is_err());
let e = index.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("invalid value"));
assert!(message.contains("expected u32"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_index_to_file_non_existing_path() {
let tmp_dir = tempdir().expect("Failed setting up temp directory.");
let dir = tmp_dir.path();
generate_examples_with_index(dir).expect("Failed to generate examples");
let config_dir = dir.join("examples/config/");
let indexfile = config_dir.join("nonexisting_dir/foo"); let index = Index::empty();
let result = index.to_file(indexfile);
assert!(result.is_err());
let e = result.unwrap_err();
match e {
Error::Io(inner) => {
match inner.kind() {
std::io::ErrorKind::NotFound => {
assert!(inner.to_string().contains("No such file or directory"));
},
_ => panic!("Expected a NotFound error, got: {:#?}", inner)
}
},
_ => panic!("Expected a Io error, got: {:#?}", e),
}
}
#[test]
fn test_index_remove_zettel_not_contained() {
let mut index = Index {
files: std::collections::BTreeMap::new(),
timestamp: std::time::SystemTime::now(),
};
let key = std::path::PathBuf::from("non_existing_zettel.md");
let rz = index.remove_zettel(key);
assert!(rz.is_none());
}
#[test]
fn test_index_get_mut_zettel_none() {
use std::collections::BTreeMap;
use std::time::SystemTime;
use std::path::PathBuf;
let mut index = Index {
files: BTreeMap::new(),
timestamp: SystemTime::now(),
};
let nz = index.get_mut_zettel(PathBuf::from("non_existing_zettel.md"));
assert!(nz.is_none());
}
fn sequence_test_index() -> Index {
let mut index = Index::empty();
let mut f1 = Zettel::new("File 1");
let mut f2 = Zettel::new("File 2");
let mut f3 = Zettel::new("File 3");
let mut f4 = Zettel::new("File 4");
let f5 = Zettel::new("File 5");
let mut f6 = Zettel::new("File 6");
let mut f7 = Zettel::new("File 7");
f1.add_keyword("example");
f3.add_keyword("foo");
f4.add_keyword("bar");
f7.add_keyword("baz");
f1.add_followup("file2.md");
f2.add_followup("file3.md");
f2.add_followup("file4.md");
f3.add_followup("file5.md");
f4.add_followup("file5.md"); f4.add_followup("file6.md");
f6.add_followup("file7.md");
index.add_zettel("file1.md", f1);
index.add_zettel("file2.md", f2);
index.add_zettel("file3.md", f3);
index.add_zettel("file4.md", f4);
index.add_zettel("file5.md", f5);
index.add_zettel("file6.md", f6);
index.add_zettel("file7.md", f7);
index
}
#[test]
fn test_sequence_tree() {
let index = sequence_test_index();
let mut scope = HashSet::new();
scope.insert(PathBuf::from("file4.md"));
scope.insert(PathBuf::from("file6.md"));
let stn = querying::sequences::sequence_tree(&index, &scope);
assert_eq!(stn.len(), 6);
assert!(stn.contains(&PathBuf::from("file1.md")));
assert!(stn.contains(&PathBuf::from("file2.md")));
assert!(stn.contains(&PathBuf::from("file4.md")));
assert!(stn.contains(&PathBuf::from("file5.md")));
assert!(stn.contains(&PathBuf::from("file6.md")));
assert!(stn.contains(&PathBuf::from("file7.md")));
}
#[test]
fn test_inspect_links() {
let tmp_dir = tempdir().expect("Failed setting up temp directory.");
let dir = tmp_dir.path();
generate_examples_with_index(dir)
.expect("Failed to generate examples");
let file_path = dir.join("examples/config/index.yaml");
let index = Index::from_file(&file_path)
.expect("Failed to load index.");
let mut linkers = HashSet::new();
linkers.insert(PathBuf::from("file1.md"));
let links = index.inspect_links(&linkers);
assert_eq!(links.len(), 2);
assert!(links.contains(&PathBuf::from("file2.md")));
assert!(links.contains(&PathBuf::from("file3.md")));
}
#[test]
fn test_sequence_tree_whole() {
let tmp_dir = tempdir().expect("Failed setting up temp directory.");
let dir = tmp_dir.path();
generate_examples_with_index(dir)
.expect("Failed to generate examples");
let file_path = dir.join("examples/config/index.yaml");
let index = Index::from_file(&file_path)
.expect("Failed to load index.");
let mut scope = HashSet::new();
scope.insert(PathBuf::from("file1.md"));
let sequence_tree = index.sequence_tree_whole(&scope);
assert_eq!(sequence_tree.len(), 5);
assert!(sequence_tree.contains(&PathBuf::from("file1.md")));
assert!(sequence_tree.contains(&PathBuf::from("file2.md")));
assert!(sequence_tree.contains(&PathBuf::from("file3.md")));
assert!(sequence_tree.contains(&PathBuf::from("subdir/file4.md")));
assert!(sequence_tree.contains(&PathBuf::from("subdir/file5.md")));
}
}