Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Add --template option to specify a custom template #56

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
364 changes: 179 additions & 185 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions common/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub struct Args {
pub flag_f: bool,
pub flag_p: Option<String>,
pub flag_t: Option<String>,
pub flag_template: Option<String>,
pub flag_v: bool,
pub arg_project: String,
pub cmd_edit: bool,
Expand Down Expand Up @@ -51,6 +52,7 @@ impl Default for Args {
flag_f: false,
flag_p: None,
flag_t: None,
flag_template: None,
flag_v: false,
}
}
Expand Down
65 changes: 63 additions & 2 deletions common/src/project_paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,51 @@ use dirs::home_dir;
use std::path::PathBuf;

pub const CONFIG_EXTENSION: &str = "yml";
pub const DEFAULT_TEMPLATE_FILE: &str = ".template";
static MUXED_FOLDER: &str = ".muxed";

pub struct ProjectPaths {
pub home_directory: PathBuf,
pub project_directory: PathBuf,
pub project_file: PathBuf,
pub template_file: PathBuf,
}

impl ProjectPaths {
pub fn new(
home_directory: PathBuf,
project_directory: PathBuf,
project_file: PathBuf,
template_file: PathBuf,
) -> ProjectPaths {
ProjectPaths {
home_directory,
project_directory,
project_file,
template_file,
}
}

pub fn from_strs(
home_directory: &str,
project_directory: &str,
project_file: &str,
template_file: &str,
) -> ProjectPaths {
let home_directory = PathBuf::from(home_directory);
let project_directory = home_directory.join(project_directory);
let project_file = project_directory
.join(project_file)
.with_extension(CONFIG_EXTENSION);
let template_file = project_directory
.join(template_file)
.with_extension(CONFIG_EXTENSION);

ProjectPaths {
home_directory,
project_directory,
project_file,
template_file,
}
}
}
Expand Down Expand Up @@ -67,7 +76,8 @@ impl ProjectPaths {
/// let paths = ProjectPaths::new(
/// PathBuf::from("/tmp"),
/// PathBuf::from("/tmp/.muxed"),
/// PathBuf::from("/tmp/.muxed/projectname.yml")
/// PathBuf::from("/tmp/.muxed/projectname.yml"),
/// PathBuf::from("/tmp/.muxed/.template.yml"),
/// );
///
/// assert_eq!(project_paths.home_directory, PathBuf::from("/tmp"));
Expand All @@ -85,7 +95,19 @@ pub fn project_paths(args: &Args) -> ProjectPaths {
let project_filename = PathBuf::from(&args.arg_project).with_extension(CONFIG_EXTENSION);
let project_fullpath = project_directory.join(project_filename);

ProjectPaths::new(homedir, project_directory, project_fullpath)
let template_filename: &str = args
.flag_template
.as_deref()
.unwrap_or(DEFAULT_TEMPLATE_FILE);
let template_filename = PathBuf::from(template_filename).with_extension(CONFIG_EXTENSION);
let template_fullpath = project_directory.join(template_filename);

ProjectPaths::new(
homedir,
project_directory,
project_fullpath,
template_fullpath,
)
}

/// A Thin wrapper around the home_dir crate. This is so we can swap the default
Expand Down Expand Up @@ -124,6 +146,17 @@ mod test {
)
}

#[test]
fn expects_template_as_default_template_filename() {
let args: Args = Default::default();
let project_paths = project_paths(&args);

assert_eq!(
project_paths.template_file,
PathBuf::from("/tmp/.muxed/.template.yml")
)
}

#[test]
fn expects_spacey_as_project_dir() {
let args = Args {
Expand All @@ -148,4 +181,32 @@ mod test {
PathBuf::from("/tmp/.muxed/projectname.yml")
)
}

#[test]
fn expects_template_as_yml_file() {
let args = Args {
flag_template: Some("custom_template".to_string()),
..Default::default()
};
let project_paths = project_paths(&args);

assert_eq!(
project_paths.template_file,
PathBuf::from("/tmp/.muxed/custom_template.yml")
)
}

#[test]
fn expects_template_with_extension_as_yml_file() {
let args = Args {
flag_template: Some("custom_template.json".to_string()),
..Default::default()
};
let project_paths = project_paths(&args);

assert_eq!(
project_paths.template_file,
PathBuf::from("/tmp/.muxed/custom_template.yml")
)
}
}
9 changes: 9 additions & 0 deletions common/src/rand_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ pub fn project_path_name() -> String {
format!("/tmp/.muxed-test-{}/", random::<u16>())
}

pub fn template_path_name() -> String {
format!("muxed-test-template-{}", random::<u16>())
}

pub fn project_path() -> PathBuf {
PathBuf::from(project_path_name())
}
Expand All @@ -24,6 +28,11 @@ pub fn project_file_path_with_name(name: &str) -> PathBuf {
project_path().join(project_file)
}

pub fn project_template_file_path() -> PathBuf {
let project_file = PathBuf::from(template_path_name()).with_extension(CONFIG_EXTENSION);
project_path().join(project_file)
}

pub fn project_file_with_dir(dir: &str) -> PathBuf {
let project_file = PathBuf::from(project_file_name()).with_extension(CONFIG_EXTENSION);
PathBuf::from(dir).join(project_file)
Expand Down
1 change: 1 addition & 0 deletions list/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub fn exec(args: Args) -> Result<(), String> {
.read_dir()
.map_err(|_| "Could not read the dir")?
.filter_map(|path| path.ok())
.filter(|path| path.path() != project_paths.template_file)
.map(|path| PathBuf::from(path.file_name()))
.filter_map(|buf| match buf.extension().and_then(|x| x.to_str()) {
Some(CONFIG_EXTENSION) => buf
Expand Down
2 changes: 1 addition & 1 deletion load/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ publish = false
common = { path = "../common" }
dirs = "2.0.2"
libc = "0.2.66"
yaml-rust = { version = "0.4.3", default-features = false }
yaml-rust = { version = "0.4.5", default-features = false }

[dev-dependencies]
rand = "0.7.2"
Expand Down
6 changes: 3 additions & 3 deletions load/src/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,15 @@ mod test {

#[test]
fn missing_file_returns_err() {
let project_paths = ProjectPaths::from_strs("/tmp", ".muxed", "");
let project_paths = ProjectPaths::from_strs("/tmp", ".muxed", "", "");
let result = read(&String::from("not_a_file"), &project_paths);
assert!(result.is_err())
}

#[test]
fn poorly_formatted_file_returns_err() {
let name = rand_names::project_file_name();
let project_paths = ProjectPaths::from_strs("/tmp", ".muxed", &name);
let project_paths = ProjectPaths::from_strs("/tmp", ".muxed", &name, "");

let _ = fs::create_dir(&project_paths.project_directory);
let mut buffer = File::create(&project_paths.project_file).unwrap();
Expand All @@ -130,7 +130,7 @@ mod test {
#[test]
fn good_file_returns_ok() {
let name = rand_names::project_file_name();
let project_paths = ProjectPaths::from_strs("/tmp", ".muxed", &name);
let project_paths = ProjectPaths::from_strs("/tmp", ".muxed", &name, "");

let _ = fs::create_dir(&project_paths.project_directory);
let mut buffer = File::create(&project_paths.project_file).unwrap();
Expand Down
73 changes: 54 additions & 19 deletions new/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::fs::OpenOptions;
use std::io::Write;
use std::path::PathBuf;

static TEMPLATE: &str = include_str!("template.yml");
static DEFAULT_TEMPLATE: &str = include_str!("template.yml");

/// The main execution method.
/// Accept the name of a project to create a configuration file in the
Expand All @@ -32,8 +32,25 @@ pub fn exec(args: Args) -> Result<(), String> {

check_first_run(&project_paths.project_directory)?;

let template = modified_template(TEMPLATE, &project_paths.project_file);
write_template(&template, &project_paths.project_file, args.flag_f)?;
let template = if project_paths.template_file.exists() {
std::fs::read_to_string(project_paths.template_file).map_err(|e| e.to_string())?
} else {
DEFAULT_TEMPLATE.to_string()
};

let replacements = [
(
"{file}",
project_paths
.project_file
.to_str()
.expect("Couldn't convert the {:?} path into a String to write into the new file"),
),
("{project}", &args.arg_project),
];

let new_project = modified_template(&template, &replacements);
write_template(&new_project, &project_paths.project_file, args.flag_f)?;

println!(
"\u{270C} The template file {} has been written to {}\nHappy tmuxing!",
Expand All @@ -43,8 +60,16 @@ pub fn exec(args: Args) -> Result<(), String> {
Ok(())
}

fn modified_template(template: &str, file: &PathBuf) -> String {
template.replace("{file}", file.to_str().expect("Couldn't convert the {:?} path into a String to write into the new file"))
type Replacement<'a, 'b> = (&'a str, &'b str);

fn modified_template(template: &str, replacements: &[Replacement]) -> String {
let mut template = template.to_string();

for (placeholder, value) in replacements {
template = template.replace(placeholder, value);
}

template
}

pub fn write_template<S>(template: S, path: &PathBuf, force: bool) -> Result<(), String>
Expand Down Expand Up @@ -79,44 +104,54 @@ mod test {
use common::rand_names;
use std::fs;
use std::fs::File;
use std::path::Path;

static DEFAULT_TEMPLATE: &str = "file: {file}\nproject: {project}";

fn file_replacement(path: &Path) -> Replacement {
("{file}", path.to_str().unwrap())
}

#[test]
fn expect_muxed_project_text() {
fn expect_muxed_file_text() {
let file = PathBuf::from("~/.muxed").join("superProject");
let value = modified_template(TEMPLATE, &file);
let result = value.contains("superProject");
assert!(result);
let value = modified_template(DEFAULT_TEMPLATE, &[file_replacement(&file)]);

assert!(value.contains("file: ~/.muxed/superProject"));
assert!(value.contains("project: {project}"));
}

#[test]
fn expect_muxed_dir_text() {
let file = PathBuf::from("~/.muxed").join("superProject");
let value = modified_template(TEMPLATE, &file);
let result = value.contains("~/.muxed/");
assert!(result);
fn expect_muxed_project_text() {
let value = modified_template(DEFAULT_TEMPLATE, &[("{project}", "superProject")]);

assert!(value.contains("project: superProject"));
}

#[test]
fn expect_no_file_name_placeholder() {
let file = PathBuf::from("~/.my_dir").join("superProject");
let value = modified_template(TEMPLATE, &file);
let value = modified_template(DEFAULT_TEMPLATE, &[file_replacement(&file)]);

let result = !value.contains("{file}");
assert!(result);
}

#[test]
fn expect_project_name_with_dir() {
let file = PathBuf::from("~/.my_dir").join("superProject.yml");
let value = modified_template(TEMPLATE, &file);
let result = value.contains("# ~/.my_dir/superProject.yml");
let value = modified_template(DEFAULT_TEMPLATE, &[file_replacement(&file)]);

let result = value.contains("file: ~/.my_dir/superProject.yml");
assert!(result);
}

#[test]
fn expect_project_name_with_dir_and_trailing_slash() {
let file = PathBuf::from("~/.my_dir/").join("superProject.yml");
let value = modified_template(TEMPLATE, &file);
let result = value.contains("# ~/.my_dir/superProject.yml");
let value = modified_template(DEFAULT_TEMPLATE, &[file_replacement(&file)]);

let result = value.contains("file: ~/.my_dir/superProject.yml");
assert!(result);
}

Expand Down
Loading