Skip to content

Commit

Permalink
Add init command
Browse files Browse the repository at this point in the history
  • Loading branch information
rossmacarthur committed Jul 27, 2020
1 parent b94b6d8 commit 131576d
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 20 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -16,7 +16,7 @@ build = "build.rs"
[dependencies]
ansi_term = "0.11.0"
anyhow = "1.0.31"
casual = "0.1.1"
casual = "0.1.2"
fs2 = "0.4.3"
git2 = "0.13.6"
glob = "0.3.0"
Expand Down
50 changes: 49 additions & 1 deletion src/cli.rs
Expand Up @@ -10,7 +10,7 @@ use structopt::{
use url::Url;

use crate::{
config::{GistRepository, GitHubRepository, GitProtocol, GitReference, RawPlugin},
config::{GistRepository, GitHubRepository, GitProtocol, GitReference, RawPlugin, Shell},
context::Settings,
edit::Plugin,
log::{Output, Verbosity},
Expand Down Expand Up @@ -93,6 +93,14 @@ struct Add {

#[derive(Debug, PartialEq, StructOpt)]
enum RawCommand {
/// Initialize a new config file.
#[structopt(help_message = HELP_MESSAGE)]
Init {
/// The type of shell, accepted values are: bash, zsh.
#[structopt(long, value_name = "SHELL")]
shell: Option<Shell>,
},

/// Add a new plugin to the config file.
#[structopt(help_message = HELP_MESSAGE)]
Add(Box<Add>),
Expand Down Expand Up @@ -182,6 +190,8 @@ struct RawOpt {
/// The resolved command.
#[derive(Debug)]
pub enum Command {
/// Initialize a new config file.
Init { shell: Option<Shell> },
/// Add a new plugin to the config file.
Add { name: String, plugin: Box<Plugin> },
/// Open up the config file in the default editor.
Expand Down Expand Up @@ -310,6 +320,7 @@ impl Opt {
};

let command = match command {
RawCommand::Init { shell } => Command::Init { shell },
RawCommand::Add(add) => {
let (name, plugin) = Plugin::from_add(*add);
Command::Add {
Expand Down Expand Up @@ -413,6 +424,7 @@ OPTIONS:
SHELDON_DOWNLOAD_DIR=]
SUBCOMMANDS:
init Initialize a new config file
add Add a new plugin to the config file
edit Open up the config file in the default editor
remove Remove a plugin from the config file
Expand Down Expand Up @@ -507,6 +519,42 @@ For more information try --help",
assert_eq!(err.info, None);
}

#[test]
fn raw_opt_init_help() {
setup();
let err = raw_opt_err(&["init", "--help"]);
assert_eq!(
err.message,
format!(
"\
{name}-init {version}
Initialize a new config file
USAGE:
sheldon init [OPTIONS]
FLAGS:
-h, --help Show this message and exit
OPTIONS:
--shell <SHELL> The type of shell, accepted values are: bash, zsh",
name = crate_name!(),
version = crate_version!()
)
);
assert_eq!(err.kind, structopt::clap::ErrorKind::HelpDisplayed);
assert_eq!(err.info, None);
}

#[test]
fn raw_opt_init_with_invalid_shell() {
setup();
assert_eq!(
raw_opt_err(&["init", "--shell", "ksh",]).kind,
structopt::clap::ErrorKind::ValueValidation
);
}

#[test]
fn raw_opt_add_help() {
setup();
Expand Down
44 changes: 44 additions & 0 deletions src/config.rs
Expand Up @@ -28,6 +28,13 @@ const GITHUB_HOST: &str = "github.com";
// Configuration definitions
/////////////////////////////////////////////////////////////////////////

/// The type of shell that we are using.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Shell {
Bash,
Zsh,
}

/// A wrapper around a template string.
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct Template {
Expand Down Expand Up @@ -194,6 +201,16 @@ pub struct Config {
// Serialization implementations
/////////////////////////////////////////////////////////////////////////

impl fmt::Display for Shell {
/// Displays a `Shell`.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Bash => f.write_str("bash"),
Self::Zsh => f.write_str("zsh"),
}
}
}

impl fmt::Display for GitProtocol {
/// Displays a `GitProtocol`.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Expand Down Expand Up @@ -249,6 +266,33 @@ impl_serialize_as_str!(GitHubRepository);
// Deserialization implementations
/////////////////////////////////////////////////////////////////////////

impl Default for Shell {
fn default() -> Self {
Self::Zsh
}
}

#[derive(Debug)]
pub struct ParseShellError;

impl fmt::Display for ParseShellError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("expected one of `bash` or `zsh`")
}
}

impl str::FromStr for Shell {
type Err = ParseShellError;

fn from_str(s: &str) -> result::Result<Self, Self::Err> {
match &*s.to_lowercase() {
"bash" => Ok(Self::Bash),
"zsh" => Ok(Self::Zsh),
_ => Err(ParseShellError),
}
}
}

/// A visitor to deserialize a `Template` from a string or a struct.
struct TemplateVisitor;

Expand Down
30 changes: 30 additions & 0 deletions src/configs/bash.plugins.toml
@@ -0,0 +1,30 @@
# `sheldon` configuration file
# ----------------------------
#
# You can modify this file directly or you can use one of the following
# `sheldon` commands which are provided to assist in editing the config file:
#
# - `sheldon add` to add a new plugin to the config file
# - `sheldon edit` to open up the config file in the default editor
# - `sheldon remove` to remove a plugin from the config file
#
# See the documentation for more https://github.com/rossmacarthur/sheldon#readme

# This config field is overridden to use Bash relevant file matches.
match = [
"{{ name }}.plugin.bash",
"{{ name }}.plugin.sh",
"{{ name }}.bash",
"{{ name }}.sh",
"*.plugin.bash",
"*.plugin.sh",
"*.bash",
"*.sh",
]

[plugins]

# For example:
#
# [plugins.base16]
# github = "chriskempson/base16-shell"
File renamed without changes.
6 changes: 4 additions & 2 deletions src/context.rs
Expand Up @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};

use serde::{Deserialize, Serialize};

use crate::{log::Output, util::PathExt};
use crate::{config::Shell, log::Output, util::PathExt};

/// Settings to use over the entire program.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
Expand Down Expand Up @@ -34,13 +34,15 @@ pub struct Context<'a> {
pub output: &'a Output,
}

/// Contextual information to use while running edit tasks (add, remove).
/// Contextual information to use while running edit tasks (init, add, remove).
#[derive(Debug)]
pub struct EditContext {
/// Common data.
pub settings: Settings,
/// The output style.
pub output: Output,
/// The type of shell.
pub shell: Option<Shell>,
}

/// Contextual information to use while running the main tasks (lock and
Expand Down
15 changes: 8 additions & 7 deletions src/edit.rs
Expand Up @@ -4,7 +4,7 @@ use std::{fmt, fs, path::Path};

use anyhow::{bail, Context as ResultExt, Result};

use crate::config::RawPlugin;
use crate::config::{RawPlugin, Shell};

/// An editable plugin.
#[derive(Debug)]
Expand All @@ -25,19 +25,20 @@ impl From<RawPlugin> for Plugin {
}
}

impl Default for Config {
fn default() -> Self {
Self::from_str(include_str!("plugins.toml")).unwrap()
}
}

impl fmt::Display for Config {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.doc)
}
}

impl Config {
pub fn default(shell: Shell) -> Self {
match shell {
Shell::Bash => Self::from_str(include_str!("configs/bash.plugins.toml")).unwrap(),
Shell::Zsh => Self::from_str(include_str!("configs/zsh.plugins.toml")).unwrap(),
}
}

/// Read a `Config` from the given string.
pub fn from_str<S>(s: S) -> Result<Self>
where
Expand Down
55 changes: 50 additions & 5 deletions src/lib.rs
Expand Up @@ -29,7 +29,7 @@ use anyhow::{bail, Context as ResultExt, Error, Result};

use crate::{
cli::{Command, Opt},
config::Config,
config::{Config, Shell},
context::{Context, EditContext, LockContext, SettingsExt},
edit::Plugin,
lock::LockedConfig,
Expand All @@ -51,18 +51,43 @@ impl Sheldon {
)) {
bail!("aborted initialization!");
};

let shell = ctx.shell.unwrap_or_else(|| {
casual::prompt("Are you using sheldon with Bash or Zsh? [default: zsh] ")
.default(Shell::default())
.get()
});

if let Some(parent) = path.parent() {
fs::create_dir_all(parent).with_context(s!(
"failed to create directory `{}`",
&ctx.replace_home(parent).display()
))?;
}
Ok(edit::Config::default())
Ok(edit::Config::default(shell))
} else {
Err(err)
}
}

// /// Initialize a new config file.
fn init(ctx: &EditContext) -> Result<()> {
let path = ctx.config_file();
match path
.metadata()
.with_context(s!("failed to check `{}`", path.display()))
{
Ok(_) => {
header!(ctx, "Checked", path);
}
Err(err) => {
Self::init_config(ctx, path, err)?.to_path(path)?;
header!(ctx, "Initialized", path);
}
}
Ok(())
}

/// Adds a new plugin to the config file.
fn add(ctx: &EditContext, name: String, plugin: Plugin) -> Result<()> {
let path = ctx.config_file();
Expand Down Expand Up @@ -207,16 +232,36 @@ impl Sheldon {
let mut warnings = Vec::new();

match command {
Command::Init { shell } => {
let ctx = EditContext {
settings,
output,
shell,
};
Self::init(&ctx)
}
Command::Add { name, plugin } => {
let ctx = EditContext { settings, output };
let ctx = EditContext {
settings,
output,
shell: None,
};
Self::add(&ctx, name, *plugin)
}
Command::Edit => {
let ctx = EditContext { settings, output };
let ctx = EditContext {
settings,
output,
shell: None,
};
Self::edit(&ctx)
}
Command::Remove { name } => {
let ctx = EditContext { settings, output };
let ctx = EditContext {
settings,
output,
shell: None,
};
Self::remove(&ctx, name)
}
Command::Lock { reinstall } => {
Expand Down
2 changes: 1 addition & 1 deletion src/lock.rs
Expand Up @@ -31,7 +31,7 @@ use crate::{
const MAX_THREADS: u32 = 8;

lazy_static! {
/// The default files to match on.
/// The default files to match on (for Zsh)
pub static ref DEFAULT_MATCHES: Vec<String> = vec_into![
"{{ name }}.plugin.zsh",
"{{ name }}.zsh",
Expand Down

0 comments on commit 131576d

Please sign in to comment.