extern crate serde_yaml;
use std::fs;
use std::fs::File;
use std::io::{Write};
use std::path::{Path, PathBuf};
use backstage::indexing::IndexingMethod;
use backstage::querying::sequences::SequenceStart;
use backstage::error::Error;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Config {
pub rootdir: PathBuf,
pub indexfile: PathBuf,
#[serde(default = "default_indexingmethod")]
pub indexingmethod: IndexingMethod,
#[serde(default = "default_sequencestart")]
pub sequencestart: SequenceStart,
#[serde(default = "default_ignorefile")]
pub ignorefile: PathBuf,
}
fn default_indexingmethod() -> IndexingMethod {
IndexingMethod::Native
}
fn default_sequencestart() -> SequenceStart {
SequenceStart::Keyword
}
fn default_ignorefile() -> PathBuf {
PathBuf::from(".gitignore")
}
impl Config {
pub fn new<T: AsRef<Path>>(rootdir: T,
indexfile: T) -> Config {
let rootdir = rootdir.as_ref();
let indexfile = indexfile.as_ref();
Config {
rootdir: PathBuf::from(rootdir),
indexfile: PathBuf::from(indexfile),
indexingmethod: default_indexingmethod(),
sequencestart: default_sequencestart(),
ignorefile: default_ignorefile(),
}
}
pub fn from_file<P: AsRef<Path>>(configfile: P) -> Result<Config, Error> {
let contents = fs::read_to_string(configfile)?; let cfg: Config = serde_yaml::from_str(&contents)?; Ok(cfg)
}
pub fn to_file<P: AsRef<Path>>(&self, configfile: P) -> Result<(), Error> {
let s = serde_yaml::to_string(self)?; let mut file = File::create(configfile)?; writeln!(file, "{}", s)?; Ok(())
}
}
#[cfg(test)]
mod tests {
extern crate tempfile;
use self::tempfile::tempdir;
use super::*;
use examples::*;
#[test]
fn test_default_indexing_method() {
assert_eq!(default_indexingmethod(), IndexingMethod::Native);
}
#[test]
fn test_default_ignorefile() {
assert_eq!(default_ignorefile(), PathBuf::from(".gitignore"));
}
#[test]
fn new_config() {
let cfg = Config::new("examples/Zettelkasten",
"examples/index.yaml");
assert_eq!(&cfg.rootdir, std::path::Path::new("examples/Zettelkasten"));
assert_eq!(&cfg.indexfile, std::path::Path::new("examples/index.yaml"));
assert_eq!(&cfg.ignorefile, std::path::Path::new(".gitignore"));
}
#[test]
fn test_cfg_from_file() {
let tmp_dir = tempdir().expect("Failed to create tempdir");
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_path = config_dir.join("libzettels.cfg.yaml");
let rootdir = dir.join("examples/Zettelkasten/");
let indexfile = config_dir.join("index.yaml");
let cfg = Config::from_file(cfg_file_path).unwrap(); assert_eq!(cfg.rootdir, rootdir);
assert_eq!(cfg.indexfile, indexfile);
assert_eq!(cfg.ignorefile, std::path::Path::new(".gitignore"));
}
#[test]
fn test_cfg_from_file_minimal() {
let tmp_dir = tempdir().expect("Failed to create tempdir");
let cfg_file_path = tmp_dir.path().join("libzettels.cfg.yaml");
let mut f = std::fs::File::create(&cfg_file_path)
.expect("Failed to create file");
let yaml = "---
rootdir: examples/Zettelkasten
indexfile: examples/index.yaml";
write!(f, "{}", yaml).expect("Failed to write to file");
let cfg = Config::from_file(cfg_file_path).unwrap(); assert_eq!(cfg.rootdir, std::path::Path::new("examples/Zettelkasten"));
assert_eq!(cfg.indexfile, std::path::Path::new("examples/index.yaml"));
assert_eq!(cfg.ignorefile, std::path::Path::new(".gitignore"));
}
#[test]
fn test_cfg_to_file() {
let tmp_dir = tempdir().expect("Failed to create tempdir");
let cfg_file_path = tmp_dir.path().join("libzettels.cfg.yaml");
std::fs::File::create(&cfg_file_path)
.expect("Failed to create file");
let cfg = Config::new("examples/Zettelkasten",
"examples/index.yaml");
assert!(cfg.to_file(cfg_file_path).is_ok());
}
#[test]
fn test_cfg_from_nonexistent_file() {
let tmp_dir = tempdir().expect("Failed to create tempdir");
let cfg_file_path = tmp_dir.path().join("foo");
let cfg = Config::from_file(cfg_file_path);
assert!(cfg.is_err());
let e = cfg.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_cfg_from_random_text_file() {
let tmp_dir = tempdir().expect("Failed to create tempdir");
let dir = &tmp_dir.path();
generate_bare_examples(dir).expect("Failed to generate examples");
let cfg_file_path = dir.join("foo");
let mut test_config_file = File::create(&cfg_file_path)
.expect("Something went wrong with creating a temporary file for
the test.");
writeln!(test_config_file, "Some random stuff.")
.expect("Something went wrong with writing to the temporary file
for the test.");
let cfg = Config::from_file(cfg_file_path);
assert!(cfg.is_err());
let e = cfg.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("invalid type"));
assert!(message.contains("expected struct Config"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_cfg_from_file_missing_field() {
let tmp_dir = tempdir().expect("Failed to create tempdir");
let cfg_file_path = tmp_dir.path().join("foo");
let mut test_config_file = File::create(&cfg_file_path)
.expect("Something went wrong with creating a temporary file for
the test.");
let erroneous_config = "---
roodir: examples/Zettelkasten
indexfile: examples/index.yaml
indexingmethod: Grep
ignorefile: \".gitignore\"";
writeln!(test_config_file, "{}", erroneous_config)
.expect("Something went wrong with writing to the temporary file
for the test.");
let cfg = Config::from_file(cfg_file_path);
assert!(cfg.is_err());
let e = cfg.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("missing field `rootdir`"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_cfg_from_file_unknown_variant() {
let tmp_dir = tempdir().expect("Failed to create tempdir");
let cfg_file_path = tmp_dir.path().join("foo");
let mut test_config_file = File::create(&cfg_file_path)
.expect("Something went wrong with creating a temporary file for
the test.");
let erroneous_config = "---
rootdir: examples/Zettelkasten
indexfile: examples/index.yaml
indexingmethod: Gep
ignorefile: \".gitignore\"";
writeln!(test_config_file, "{}", erroneous_config)
.expect("Something went wrong with writing to the temporary file
for the test.");
let cfg = Config::from_file(cfg_file_path);
assert!(cfg.is_err());
let e = cfg.unwrap_err();
match e {
Error::Yaml(inner) => {
let message = inner.to_string();
assert!(message.contains("unknown variant"));
},
_ => panic!("Expected a Yaml error, got: {:#?}", e),
}
}
#[test]
fn test_cfg_from_non_text_file() {
let tmp_dir = tempdir().expect("Failed to create tempdir");
let image_file = tmp_dir.path().join("foo.png");
let cfg_file_path = tmp_dir.path().join("foo");
let width: u32 = 10;
let height: u32 = 10;
let mut non_text = image::ImageBuffer::new(width, height);
for (_, _, pixel) in non_text.enumerate_pixels_mut() {
*pixel = image::Rgb([255, 255, 255]);
}
non_text.save(&image_file).unwrap();
std::fs::rename(image_file, &cfg_file_path).expect("Failed to rename");
let cfg = Config::from_file(cfg_file_path);
assert!(cfg.is_err());
let e = cfg.unwrap_err();
match e {
Error::Io(inner) => assert_eq!(inner.kind(),
std::io::ErrorKind::InvalidData),
_ => panic!("Expected a Io error, got: {:#?}", e),
}
}
#[test]
fn test_cfg_to_file_wrong_path() {
let tmp_dir = tempdir().expect("Failed to create temp dir");
let config_dir = tmp_dir.path().join("foo"); let cfg = Config::new("examples/Zettelkasten",
"examples/index.yaml");
let configfile = config_dir.join("foo");
let result = cfg.to_file(&configfile);
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_cfg_to_file_read_only() {
use std::fs;
let tmp_dir = tempdir().expect("Failed to create temp dir");
let config_dir = tmp_dir.path();
let config_file = config_dir.join("foo");
fs::File::create(&config_file)
.expect("Failed to setup config file");
let metadata = fs::metadata(&config_file)
.expect("Failed to get metadata");
let mut perms = metadata.permissions();
perms.set_readonly(true);
fs::set_permissions(&config_file, perms)
.expect("Failed to set the config file to read_only");
let cfg = Config::new("examples/Zettelkasten",
"examples/index.yaml");
let result = cfg.to_file(&config_file);
assert!(result.is_err());
let e = result.unwrap_err();
match e {
Error::Io(inner) => {
match inner.kind() {
std::io::ErrorKind::PermissionDenied => {
assert!(inner.to_string().contains("Permission denied"));
},
_ => panic!("Expected a PermissionDenied error, got: {:#?}", inner)
}
},
_ => panic!("Expected a Io error, got: {:#?}", e),
}
}
}