Skip to content

Commit

Permalink
feat: Find config in parent rather than default
Browse files Browse the repository at this point in the history
This is a stepping stone to #284.

BREAKING CHANGE: like git, cobalt will now search the parent dir for a
config file.  If non is found, the default will be used.  Previously, if
the config wasn't found in the CWD, cobalt used a default config.
  • Loading branch information
epage committed Oct 22, 2017
1 parent 1932533 commit 4e96a1f
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 21 deletions.
24 changes: 7 additions & 17 deletions src/bin/cobalt/main.rs
Expand Up @@ -71,7 +71,7 @@ mod error;
mod serve;
mod new;

use std::fs;
use std::env;

use clap::{Arg, App, SubCommand, AppSettings};
use cobalt::{Config, Dump, jekyll};
Expand Down Expand Up @@ -282,25 +282,15 @@ fn run() -> Result<()> {

let config_path = matches
.value_of("config")
.or_else(|| global_matches.value_of("config"))
.unwrap_or(".cobalt.yml")
.to_string();
.or_else(|| global_matches.value_of("config"));

// Fetch config information if available
let mut config: Config = if fs::metadata(&config_path).is_ok() {
info!("Using config file {}", &config_path);

match Config::from_file(&config_path) {
Ok(config) => config,
Err(e) => {
error!("Error reading config file:");
error!("{}", e);
std::process::exit(1);
}
}
let mut config: Config = if let Some(config_path) = config_path {
Config::from_file(config_path)
.chain_err(|| format!("Error reading config file {:?}", config_path))?
} else {
warn!("No .cobalt.yml file found in current directory, using default config.");
Default::default()
let cwd = env::current_dir().expect("How does this fail?");
Config::from_cwd(cwd)?
};

config.source = matches
Expand Down
103 changes: 99 additions & 4 deletions src/config.rs
@@ -1,8 +1,8 @@
use std::default::Default;
use std::path::Path;
use std::path;
use std::fs::File;
use std::io::Read;
use error::Result;
use error::*;
use serde_yaml;

use legacy::wildwest;
Expand Down Expand Up @@ -87,6 +87,8 @@ const DATA_DIR: &'static str = "_data";
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct Config {
#[serde(skip)]
pub root: path::PathBuf,
pub source: String,
pub dest: String,
pub layouts: String,
Expand Down Expand Up @@ -115,6 +117,7 @@ pub struct Config {
impl Default for Config {
fn default() -> Config {
Config {
root: path::PathBuf::new(),
source: "./".to_owned(),
dest: "./".to_owned(),
layouts: "_layouts".to_owned(),
Expand All @@ -140,10 +143,14 @@ impl Default for Config {
}

impl Config {
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Config> {
pub fn from_file<P: Into<path::PathBuf>>(path: P) -> Result<Config> {
Self::from_file_internal(path.into())
}

fn from_file_internal(path: path::PathBuf) -> Result<Config> {
let content = {
let mut buffer = String::new();
let mut f = File::open(path)?;
let mut f = File::open(&path)?;
f.read_to_string(&mut buffer)?;
buffer
};
Expand All @@ -155,6 +162,10 @@ impl Config {
let config: wildwest::GlobalConfig = serde_yaml::from_str(&content)?;
let mut config: Config = config.into();

let mut root = path;
root.pop();
config.root = root;

config.link = if let Some(ref link) = config.link {
let mut link = link.to_owned();
if !link.ends_with('/') {
Expand Down Expand Up @@ -183,13 +194,73 @@ impl Config {

Ok(config)
}

pub fn from_cwd<P: Into<path::PathBuf>>(cwd: P) -> Result<Config> {
Self::from_cwd_internal(cwd.into())
}

fn from_cwd_internal(cwd: path::PathBuf) -> Result<Config> {
let file_path = find_project_file(&cwd, ".cobalt.yml");
let mut config = file_path
.map(|p| {
info!("Using config file {:?}", &p);
Self::from_file(&p).chain_err(|| format!("Error reading config file {:?}", p))
})
.unwrap_or_else(|| {
warn!("No .cobalt.yml file found in current directory, using default config.");
Ok(Config::default())
})?;
config.root = cwd;
Ok(config)
}
}

fn find_project_file<P: Into<path::PathBuf>>(dir: P, name: &str) -> Option<path::PathBuf> {
find_project_file_internal(dir.into(), name)
}

fn find_project_file_internal(dir: path::PathBuf, name: &str) -> Option<path::PathBuf> {
let mut file_path = dir;
file_path.push(name);
while !file_path.exists() {
file_path.pop(); // filename
let hit_bottom = !file_path.pop();
if hit_bottom {
return None;
}
file_path.push(name);
}
Some(file_path)
}

#[test]
fn find_project_file_same_dir() {
let actual = find_project_file("tests/fixtures/config", ".cobalt.yml").unwrap();
let expected = path::Path::new("tests/fixtures/config/.cobalt.yml");
assert_eq!(actual, expected);
}

#[test]
fn find_project_file_parent_dir() {
let actual = find_project_file("tests/fixtures/config/child", ".cobalt.yml").unwrap();
let expected = path::Path::new("tests/fixtures/config/.cobalt.yml");
assert_eq!(actual, expected);
}

#[test]
fn find_project_file_doesnt_exist() {
let expected = path::Path::new("<NOT FOUND>");
let actual = find_project_file("tests/fixtures/", ".cobalt.yml")
.unwrap_or_else(|| expected.into());
assert_eq!(actual, expected);
}

#[test]
fn test_from_file_ok() {
let result = Config::from_file("tests/fixtures/config/.cobalt.yml").unwrap();
assert_eq!(result,
Config {
root: path::Path::new("tests/fixtures/config").to_path_buf(),
dest: "./dest".to_owned(),
layouts: "_my_layouts".to_owned(),
posts: "_my_posts".to_owned(),
Expand All @@ -202,6 +273,7 @@ fn test_from_file_rss() {
let result = Config::from_file("tests/fixtures/config/rss.yml").unwrap();
assert_eq!(result,
Config {
root: path::Path::new("tests/fixtures/config").to_path_buf(),
rss: Some("rss.xml".to_owned()),
name: Some("My blog!".to_owned()),
description: Some("Blog description".to_owned()),
Expand All @@ -227,3 +299,26 @@ fn test_from_file_not_found() {
let result = Config::from_file("tests/fixtures/config/config_does_not_exist.yml");
assert!(result.is_err());
}

#[test]
fn test_from_cwd_ok() {
let result = Config::from_cwd("tests/fixtures/config").unwrap();
assert_eq!(result,
Config {
root: path::Path::new("tests/fixtures/config").to_path_buf(),
dest: "./dest".to_owned(),
layouts: "_my_layouts".to_owned(),
posts: "_my_posts".to_owned(),
..Default::default()
});
}

#[test]
fn test_from_cwd_not_found() {
let result = Config::from_cwd("tests/fixtures").unwrap();
assert_eq!(result,
Config {
root: path::Path::new("tests/fixtures").to_path_buf(),
..Default::default()
});
}
Empty file.

0 comments on commit 4e96a1f

Please sign in to comment.