Add a command for editing the configuration

This commit is contained in:
Roey Darwish Dror
2019-08-22 22:29:31 +03:00
parent be8b726846
commit 3c71ce019b
3 changed files with 52 additions and 7 deletions

View File

@@ -1,4 +1,5 @@
use super::error::{Error, ErrorKind}; use super::error::{Error, ErrorKind};
use super::utils::editor;
use directories::BaseDirs; use directories::BaseDirs;
use failure::ResultExt; use failure::ResultExt;
use lazy_static::lazy_static; use lazy_static::lazy_static;
@@ -8,6 +9,8 @@ use serde::Deserialize;
use shellexpand; use shellexpand;
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::fs::write; use std::fs::write;
use std::path::PathBuf;
use std::process::Command;
use std::{env, fs}; use std::{env, fs};
use structopt::StructOpt; use structopt::StructOpt;
use toml; use toml;
@@ -87,10 +90,7 @@ pub struct ConfigFile {
} }
impl ConfigFile { impl ConfigFile {
/// Read the configuration file. fn ensure(base_dirs: &BaseDirs) -> Result<PathBuf, Error> {
///
/// If the configuration file does not exist the function returns the default ConfigFile.
fn read(base_dirs: &BaseDirs) -> Result<ConfigFile, Error> {
let config_path = base_dirs.config_dir().join("topgrade.toml"); let config_path = base_dirs.config_dir().join("topgrade.toml");
if !config_path.exists() { if !config_path.exists() {
write(&config_path, include_str!("../config.example.toml")) write(&config_path, include_str!("../config.example.toml"))
@@ -100,12 +100,20 @@ impl ConfigFile {
config_path.display(), config_path.display(),
e e
); );
e
}) })
.ok(); .context(ErrorKind::Configuration)?;
debug!("No configuration exists"); debug!("No configuration exists");
return Ok(Default::default());
} }
Ok(config_path)
}
/// Read the configuration file.
///
/// If the configuration file does not exist the function returns the default ConfigFile.
fn read(base_dirs: &BaseDirs) -> Result<ConfigFile, Error> {
let config_path = Self::ensure(base_dirs)?;
let mut result: Self = toml::from_str(&fs::read_to_string(config_path).context(ErrorKind::Configuration)?) let mut result: Self = toml::from_str(&fs::read_to_string(config_path).context(ErrorKind::Configuration)?)
.context(ErrorKind::Configuration)?; .context(ErrorKind::Configuration)?;
@@ -119,12 +127,29 @@ impl ConfigFile {
Ok(result) Ok(result)
} }
fn edit(base_dirs: &BaseDirs) -> Result<(), Error> {
let config_path = Self::ensure(base_dirs)?;
let editor = editor();
debug!("Editing {} with {}", config_path.display(), editor);
Command::new(editor)
.arg(config_path)
.spawn()
.and_then(|mut p| p.wait())
.context(ErrorKind::Configuration)?;
Ok(())
}
} }
#[derive(StructOpt, Debug)] #[derive(StructOpt, Debug)]
#[structopt(name = "Topgrade")] #[structopt(name = "Topgrade", raw(setting = "structopt::clap::AppSettings::ColoredHelp"))]
/// Command line arguments /// Command line arguments
pub struct CommandLineArgs { pub struct CommandLineArgs {
/// Edit the configuration file
#[structopt(long = "edit-config")]
edit_config: bool,
/// Run inside tmux /// Run inside tmux
#[structopt(short = "t", long = "tmux")] #[structopt(short = "t", long = "tmux")]
run_in_tmux: bool, run_in_tmux: bool,
@@ -185,6 +210,11 @@ impl Config {
}) })
} }
/// Launch an editor to edit the configuration
pub fn edit(base_dirs: &BaseDirs) -> Result<(), Error> {
ConfigFile::edit(base_dirs)
}
/// The list of commands to run before performing any step. /// The list of commands to run before performing any step.
pub fn pre_commands(&self) -> &Option<Commands> { pub fn pre_commands(&self) -> &Option<Commands> {
&self.config_file.pre_commands &self.config_file.pre_commands
@@ -244,4 +274,9 @@ impl Config {
pub fn keep_at_end(&self) -> bool { pub fn keep_at_end(&self) -> bool {
self.opt.keep_at_end || env::var("TOPGRADE_KEEP_END").is_ok() self.opt.keep_at_end || env::var("TOPGRADE_KEEP_END").is_ok()
} }
/// Whether to edit the configuration file
pub fn edit_config(&self) -> bool {
self.opt.edit_config
}
} }

View File

@@ -67,6 +67,11 @@ fn run() -> Result<(), Error> {
let base_dirs = directories::BaseDirs::new().ok_or(ErrorKind::NoBaseDirectories)?; let base_dirs = directories::BaseDirs::new().ok_or(ErrorKind::NoBaseDirectories)?;
let config = Config::load(&base_dirs)?; let config = Config::load(&base_dirs)?;
if config.edit_config() {
Config::edit(&base_dirs)?;
return Ok(());
};
if config.run_in_tmux() && env::var("TOPGRADE_INSIDE_TMUX").is_err() { if config.run_in_tmux() && env::var("TOPGRADE_INSIDE_TMUX").is_err() {
#[cfg(unix)] #[cfg(unix)]
{ {

View File

@@ -1,5 +1,6 @@
use super::error::{Error, ErrorKind}; use super::error::{Error, ErrorKind};
use log::{debug, error}; use log::{debug, error};
use std::env;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fmt::{self, Debug}; use std::fmt::{self, Debug};
use std::path::{Component, Path, PathBuf}; use std::path::{Component, Path, PathBuf};
@@ -138,6 +139,10 @@ impl<'a> fmt::Display for HumanizedPath<'a> {
} }
} }
pub fn editor() -> String {
env::var("EDITOR").unwrap_or_else(|_| String::from(if cfg!(windows) { "notepad" } else { "vi" }))
}
#[cfg(test)] #[cfg(test)]
#[cfg(windows)] #[cfg(windows)]
mod tests { mod tests {