From 7c3eec6f3ae981bb64ea866bf7ebda2d29e7c956 Mon Sep 17 00:00:00 2001 From: Oscar Stevens <82244060+ostev@users.noreply.github.com> Date: Tue, 13 Jul 2021 07:55:07 +1000 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8=20added=20support=20for=20def?= =?UTF-8?q?ault=20template=20(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(init): :sparkles: Add default templates This is used for `bonnie --init`. It fetches the file at `$BONNIE_TEMPLATE_PATH`, falling back to `~/.bonnie/template.toml` if necessary. The `home` crate was introduced to handle the finding of the user's home directory on Windows, which is surprisingly complicated and **can fail**. * feat(init): :sparkles: Add edit template file flag Using the `-e` or `--edit-template` flag, the user can now open the template file using the editor in the `EDITOR` environment variable or, on Windows, `start`. * docs(help): :memo: Update help text to add `-e` * fix(init): :bug: Change template environment variable It is now `BONNIE_TEMPLATE` to match `BONNIE_CONFIG`. This breaks with the previous implementation. * docs(help): :memo: Update help with new env var * docs(wiki): :memo: Update docs with new env var Changed `BONNIE_TEMPLATE_PATH` to `BONNIE_TEMPLATE`. * Update src/help.rs Co-authored-by: arctic_hen7 * Update src/help.rs Co-authored-by: arctic_hen7 * Update src/init.rs Co-authored-by: arctic_hen7 * Update src/init.rs Co-authored-by: arctic_hen7 * Update src/bin/main.rs Co-authored-by: arctic_hen7 * Update src/bin/main.rs Co-authored-by: arctic_hen7 * refactor(init): :recycle: use `String`s as errors * refactor: :recycle: Extract `-e` into function * refactor(init): :recycle: Remove "Initialising..." text Copying a file doesn't take that long. * refactor(init): :recycle: Remove needles format\ It added quotes around a string. * refactor(init): :recycle: Remove unclear names * fix: :bug: Add error message if template not found * docs(wiki): :memo: Add info about `-e` * fix: :bug: Add more descriptive error * fix(init): :bug: Add actual path to error message * Update src/template.rs Co-authored-by: arctic_hen7 * Update src/template.rs Co-authored-by: arctic_hen7 * style: :art: Run `cargo fmt` Co-authored-by: arctic_hen7 --- .vscode/settings.json | 4 +- Cargo.lock | 32 ++++++++++++++++ Cargo.toml | 3 +- src/bin/main.rs | 9 ++++- src/help.rs | 4 +- src/init.rs | 30 +++++++++------ src/lib.rs | 1 + src/template.rs | 88 +++++++++++++++++++++++++++++++++++++++++++ wiki | 2 +- 9 files changed, 157 insertions(+), 16 deletions(-) create mode 100644 src/template.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 6c95bdd..68b8fe8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,8 @@ { "conventionalCommits.scopes": [ "install_scripts", - "wiki" + "wiki", + "init", + "help" ] } diff --git a/Cargo.lock b/Cargo.lock index 11b3b62..80e9d94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,6 +16,7 @@ name = "bonnie" version = "0.3.1" dependencies = [ "dotenv", + "home", "regex", "serde", "serde_json", @@ -28,6 +29,15 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "home" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" +dependencies = [ + "winapi", +] + [[package]] name = "itoa" version = "0.4.7" @@ -137,3 +147,25 @@ name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 85fc483..bbace48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,10 +8,11 @@ edition = "2018" [dependencies] toml = "0.5.8" -serde = { version = "1.0.125", features = ["derive"] } +serde = { version="1.0.125", features=["derive"] } serde_json = "1.0.64" dotenv = "0.15.0" regex = "1.5.4" +home = "0.5.3" [lib] name = "lib" diff --git a/src/bin/main.rs b/src/bin/main.rs index 539a7b2..6ab6894 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,4 +1,6 @@ -use lib::{cache, cache_exists, get_cfg, help, init, load_from_cache, Config, BONNIE_VERSION}; +use lib::{ + cache, cache_exists, get_cfg, help, init, load_from_cache, template, Config, BONNIE_VERSION, +}; use std::env; use std::io::Write; @@ -50,12 +52,17 @@ fn core() -> Result { _ => None, }, )?; + + println!("A new Bonnie configuration file has been initialized at ./bonnie.toml!"); + return Ok(0); } else if prog_args[0] == "-h" || prog_args[0] == "--help" { help(stdout); return Ok(0); } else if prog_args[0] == "-c" || prog_args[0] == "--cache" { should_cache = true; + } else if prog_args[0] == "-e" || prog_args[0] == "--edit-template" { + return template::edit().map(|_| 0); } } // Check if there's a cache we should read from diff --git a/src/help.rs b/src/help.rs index 9dbfbbc..d494007 100644 --- a/src/help.rs +++ b/src/help.rs @@ -11,11 +11,13 @@ This just summarizes the functionality of this command, not the syntax of Bonnie -h, --help prints this help page -v, --version prints the current version of Bonnie --i, --init [-t, --template ] creates a new `bonnie.toml` configuration, potentially taking a template file to use +-i, --init [-t, --template ] creates a new `bonnie.toml` configuration, using the specified template file if provided. +-e, --edit-template opens the default template in your default cli editor -c, --cache caches the Bonnie configuration file to `.bonnie.cache.json` for performance (this cache must be MANUALLY updated by re-running this command!) The expected location of a Bonnie configuration file can be changed from the default `./bonnie.toml` by setting the `BONNIE_CONF` environment variable. The expected location of a Bonnie cache file can be changed from the default `./.bonnie.cache.json` by setting the `BONNIE_CACHE` environment variable. +The expected location of your default template can be changed from the default `~/.bonnie/template.toml` by setting the `BONNIE_TEMPLATE` environment variable. Further information can be found at https://github.com/arctic-hen7/bonnie/wiki. ", diff --git a/src/init.rs b/src/init.rs index 36d45c8..d427aad 100644 --- a/src/init.rs +++ b/src/init.rs @@ -1,10 +1,12 @@ +use crate::template; use crate::version::BONNIE_VERSION; + use std::fs; // Creates a new Bonnie configuration file using a template, or from the default pub fn init(template: Option) -> Result<(), String> { // Check if there's already a config file in this directory - if fs::metadata("./bonnie.toml").is_ok() { + if fs::metadata("bonnie.toml").is_ok() { Err(String::from("A Bonnie configuration file already exists in this directory. If you want to create a new one, please delete the old one first.")) } else { // Check if a template has been given @@ -22,19 +24,25 @@ pub fn init(template: Option) -> Result<(), String> { // We have a template file that doesn't exist return Err(format!("The given template file at '{}' does not exist or can't be read. Please make sure the file exists and you have the permissions necessary to read from it.", template.as_ref().unwrap())); } else { - // Create a new `bonnie.toml` file using the default - // TODO read the default from `~/.bonnie/template.toml` if it exists - output = fs::write( - "./bonnie.toml", - format!( - "version=\"{version}\" + // Try to get the default template file from `~/.bonnie/template.toml` + // If it's not available, we'll use a pre-programmed default + let template = match template::get_default() { + Ok(template) => Ok(template), + // Not ideal, but... + Err(err) if err == "The system cannot find the file specified. (os error 2)" => { + Ok(format!( + "version=\"{version}\" [scripts] start = \"echo \\\"No start script yet!\\\"\" - ", - version = BONNIE_VERSION - ), - ); +", + version = BONNIE_VERSION + )) + } + Err(err) => Err(err), + }?; + + output = fs::write("bonnie.toml", template) } match output { diff --git a/src/lib.rs b/src/lib.rs index 4cf0798..82d5c30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ mod help; mod init; mod raw_schema; mod schema; +pub mod template; mod version; pub use crate::cache::{cache, cache_exists, load_from_cache}; diff --git a/src/template.rs b/src/template.rs new file mode 100644 index 0000000..4a12b18 --- /dev/null +++ b/src/template.rs @@ -0,0 +1,88 @@ +use home::home_dir; + +use std::env; +use std::fs; +use std::path::PathBuf; +use std::process::Command as OsCommand; + +pub fn get_template_path() -> Result { + let default_template_path = home_dir() + .map(|path| path.join(".bonnie").join("template.toml")) + .ok_or(String::from( + "Your home directory couldn't be found. Please check your system configuration.", + ))?; + + Ok(env::var("BONNIE_TEMPLATE") + .map(|value| PathBuf::from(value)) + .unwrap_or(default_template_path)) +} + +pub fn get_default() -> Result { + let path = get_template_path()?; + + let template = fs::read_to_string(path); + + template.map_err(|err| err.to_string()) +} + +pub fn edit() -> Result<(), String> { + // This can take a little while with with `start` on Windows + println!("Opening template file..."); + + let template_path: String = match get_template_path() { + Ok(path) => path + .to_str() + .map(String::from) + .ok_or(String::from("The path provided is not valid Unicode.")), + Err(err) => Err(format!( + "Failed to get template path with the following error: {}", + err + )), + }?; + + let template_exists = fs::metadata(&template_path).is_ok(); + + if !template_exists { + return Err(format!( + "I could not find a template file to edit at {}.", + template_path + )); + } + + let child; + + let command; + + if cfg!(target_os = "windows") { + // We need to spawn a `powershell` process to make `start` available. + child = OsCommand::new("powershell") + .arg(format!("start '{}'", template_path)) + .spawn() + .map(|mut x| x.wait()); + + command = format!("powershell -command 'start {}'", template_path); + } else { + let editor = PathBuf::from(env::var("EDITOR").unwrap_or("nano".to_string())); + + let safe_editor = editor.to_str().ok_or( + "The value given in the 'EDITOR' environment variable couldn't be parsed as a valid path.", + )?; + + child = OsCommand::new(safe_editor) + .arg(&template_path) + .spawn() + .map(|mut x| x.wait()); + + command = format!("{} {}", safe_editor, template_path); + } + + let result = match child { + Ok(_) => Ok(()), + Err(err) => Err(format!( + "The specified editor failed to start with the following error: '{}' when the command '{}' was run.", + err, command + )), + }; + + return result; +} diff --git a/wiki b/wiki index b772fd6..5cdd38c 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit b772fd67adae35b1a785005a28887aef86b7d066 +Subproject commit 5cdd38c8c397de2b5d23cf226c8d0dbbac4ac2ce