use std::convert::Into;
use std::default::Default;
use std::error::Error as StdErr;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use failure::Error;
use toml;
use super::super::error;
use super::{ConfigFileReader, ConfigReader};
#[derive(Debug, Deserialize)]
struct ParsedAppConfig {
package: Option<ParsedPackageConfig>,
}
#[derive(Debug, Deserialize)]
struct ParsedPackageConfig {
before_cmds: Option<Vec<String>>,
exclude: Option<Vec<String>>,
}
#[derive(Debug)]
pub struct AppConfig {
package: PackageConfig,
}
impl AppConfig {
pub fn package(&self) -> &PackageConfig {
&self.package
}
}
impl Default for AppConfig {
fn default() -> Self {
AppConfig {
package: PackageConfig::default(),
}
}
}
impl Into<AppConfig> for ParsedAppConfig {
fn into(self) -> AppConfig {
AppConfig {
package: self
.package
.map(|pc| pc.into())
.unwrap_or(PackageConfig::default()),
}
}
}
#[derive(Debug)]
pub struct PackageConfig {
before_cmds: Vec<String>,
}
impl PackageConfig {
pub fn before_cmds(&self) -> &Vec<String> {
&self.before_cmds
}
}
impl Into<PackageConfig> for ParsedPackageConfig {
fn into(self) -> PackageConfig {
PackageConfig {
before_cmds: self.before_cmds.unwrap_or(vec![]),
}
}
}
impl Default for PackageConfig {
fn default() -> Self {
PackageConfig {
before_cmds: vec![],
}
}
}
pub fn init_config(app_path: &Path) -> Result<(), Error> {
let mut path_buf = app_path.to_path_buf();
path_buf.push("krankerl.toml");
if let Ok(_) = File::open(&path_buf) {
bail!(error::KrankerlError::Other {
cause: "krankerl.toml already exists.".to_string()
});
}
let mut config_file = File::create(&path_buf)?;
config_file.write_all(
r#"[package]
exclude = [
]
before_cmds = [
]
"#
.as_bytes(),
)?;
Ok(())
}
fn load_config<R>(reader: &R) -> Result<String, Error>
where
R: ConfigReader,
{
let as_string = reader.read()?;
Ok(as_string)
}
fn parse_config(config: String) -> Result<ParsedAppConfig, Error> {
toml::from_str(&config)
.map_err(|e| format_err!("could not parse krankerl.toml: {}", e.description()))
}
pub fn get_config(path: &Path) -> Result<Option<AppConfig>, Error> {
let mut path_buf = path.to_path_buf();
path_buf.push("krankerl.toml");
let reader = ConfigFileReader::new(path_buf);
if !reader.has_config() {
Ok(None)
} else {
let config_str = load_config(&reader)?;
parse_config(config_str).map(|config| Some(config.into()))
}
}
#[cfg(test)]
mod tests {
use std::fs::File;
use std::path::PathBuf;
use fs_extra::dir::{copy, CopyOptions};
use tempdir::TempDir;
use super::*;
struct StaticReader {}
impl ConfigReader for StaticReader {
fn has_config(&self) -> bool {
true
}
fn read(&self) -> Result<String, Error> {
Ok("some config".to_owned())
}
}
#[test]
fn test_load_config() {
let reader = StaticReader {};
let conf = load_config(&reader).unwrap();
assert_eq!("some config".to_owned(), conf);
}
fn prepare_fs_test(id: &'static str) -> (PathBuf, TempDir) {
let mut src = PathBuf::from("./tests/apps");
src.push(id);
let tmp = TempDir::new("krankerl").unwrap();
let options = CopyOptions::new();
copy(&src, tmp.path(), &options).expect("copy app files");
let mut app_path = tmp.path().to_path_buf();
app_path.push(id);
(app_path, tmp)
}
#[test]
fn test_init_creates_config() {
let (app_path, tmp) = prepare_fs_test("app1");
let krankerl_path = app_path.join("krankerl.toml");
File::open(&krankerl_path).unwrap_err();
init_config(&app_path).unwrap();
File::open(&krankerl_path).unwrap();
tmp.close().unwrap();
}
#[test]
fn test_init_stops_if_config_exists() {
let (app_path, tmp) = prepare_fs_test("app2");
let krankerl_path = app_path.join("krankerl.toml");
File::open(&krankerl_path).unwrap();
init_config(&app_path).unwrap_err();
File::open(&krankerl_path).unwrap();
tmp.close().unwrap();
}
#[test]
fn test_load_real_config() {
let (app_path, tmp) = prepare_fs_test("app3");
let config_path = app_path.join("krankerl.toml");
let reader = ConfigFileReader::new(config_path);
load_config(&reader).unwrap();
tmp.close().unwrap();
}
#[test]
fn test_parse_empty_config() {
let toml = r#""#;
let config = parse_config(toml.to_owned());
assert!(config.is_ok());
}
#[test]
fn test_parse_simple_config() {
let toml = r#"
[package]
exclude = []
"#;
let config = parse_config(toml.to_owned());
assert!(config.is_ok());
}
#[test]
fn test_parse_config_without_commands() {
let toml = r#"
[package]
exclude = [
".git",
"composer.json",
"composer.lock",
]
"#;
let config = parse_config(toml.to_owned());
assert!(config.is_ok());
}
#[test]
fn test_parse_config_with_commands() {
let toml = r#"
[package]
before_cmds = [
"composer install",
"npm install",
"npm run build",
]
exclude = []"#;
let config = parse_config(toml.to_owned());
assert!(config.is_ok());
let config = config.unwrap();
assert!(config.package.is_some());
let package_config = config.package.unwrap();
assert!(package_config.before_cmds.is_some());
let cmds = package_config.before_cmds.unwrap();
assert_eq!(3, cmds.len());
}
}