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: Generate completions during build process #53

Closed
Closed
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
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ A dotfile manager.

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

build = "build.rs"

[dependencies]
config = "^0"
dirs = "^3"
Expand All @@ -34,5 +36,8 @@ name = "bombadil"
path = "src/bin/bombadil.rs"
required-features = ["clap"]

[build-dependencies]
clap = "^2"

[dev-dependencies]
temp_testdir = "0.2"
temp_testdir = "0.2"
20 changes: 20 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
extern crate clap;

use std::env;

use clap::Shell;

include!("src/cli.rs");

fn main() {
let outdir = match env::var_os("OUT_DIR") {
None => return,
Some(outdir) => outdir,
};
let mut app = build_cli(vec![]);
vec![Shell::Bash, Shell::Elvish, Shell::Fish, Shell::Zsh]
.iter()
.for_each(|shell| {
app.gen_completions("bombadil", *shell, outdir.clone());
});
}
9 changes: 8 additions & 1 deletion ci/action.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ echoerr() {
release() {

TAR_DIR="${BOMBADIL_HOME}/target/tar"
COMPLETIONS_DIR = "${TAR_DIR}/completions"

target="${1:-}"
if [[ $target == *"osx"* ]]; then
Expand All @@ -32,10 +33,16 @@ release() {

bin_path="${BOMBADIL_HOME}/target/${bin_folder}/bombadil"
chmod +x "$bin_path"
mkdir -p "$TAR_DIR" 2> /dev/null || true
mkdir -p "$COMPLETIONS_DIR" 2> /dev/null || true

cp "$bin_path" "$TAR_DIR"

# Copy completion files
cp "${BOMBADIL_HOME}/target/${bin_folder}/build/toml-bombadil/"-*/out/_bombadil "$COMPLETIONS_DIR"
cp "${BOMBADIL_HOME}/target/${bin_folder}/build/toml-bombadil/"-*/out/bombadil.bash "$COMPLETIONS_DIR"
cp "${BOMBADIL_HOME}/target/${bin_folder}/build/toml-bombadil/"-*/out/bombadil.elv "$COMPLETIONS_DIR"
cp "${BOMBADIL_HOME}/target/${bin_folder}/build/toml-bombadil/"-*/out/bombadil.fish "$COMPLETIONS_DIR"

cd "$TAR_DIR"
tar -czf bombadil.tar.gz *

Expand Down
141 changes: 17 additions & 124 deletions src/bin/bombadil.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
use clap::{App, AppSettings, Arg, Shell, SubCommand};
use clap::Shell;
use std::io::BufRead;
use std::path::PathBuf;
use toml_bombadil::cli;
use toml_bombadil::settings::Settings;
use toml_bombadil::{Bombadil, MetadataType, Mode};

const LINK: &str = "link";
const UNLINK: &str = "unlink";
const INSTALL: &str = "install";
const ADD_SECRET: &str = "add-secret";
const GET: &str = "get";
const GENERATE_COMPLETIONS: &str = "generate-completions";

macro_rules! fatal {
($($tt:tt)*) => {{
use std::io::Write;
Expand All @@ -19,109 +13,6 @@ macro_rules! fatal {
}}
}

fn build_cli<'a, 'b>(profile_names: Vec<&'a str>) -> App<'a, 'b>
where
'a: 'b,
{
let app_settings = &[
AppSettings::SubcommandRequiredElseHelp,
AppSettings::UnifiedHelpMessage,
AppSettings::ColoredHelp,
AppSettings::VersionlessSubcommands,
];

let subcommand_settings = &[
AppSettings::UnifiedHelpMessage,
AppSettings::ColoredHelp,
AppSettings::VersionlessSubcommands,
];

App::new("Toml Bombadil")
.settings(app_settings)
.version(env!("CARGO_PKG_VERSION"))
.author("Paul D. <[email protected]>")
.about("A dotfile template manager")
.long_about("Toml is a dotfile template manager, written in rust. \
For more info on how to configure it please go to https://github.com/oknozor/toml-bombadil")
.subcommand(SubCommand::with_name(INSTALL)
.settings(subcommand_settings)
.about("Link a given bombadil config to XDG_CONFIG_DIR/bombadil.toml")
.arg(Arg::with_name("CONFIG")
.help("path to your bombadil.toml config file inside your dotfiles directory")
.short("c")
.long("config")
.takes_value(true)
.required(true)))
.subcommand(SubCommand::with_name(LINK)
.settings(subcommand_settings)
.about("Symlink a copy of your dotfiles and inject variables according to bombadil.toml config")
.arg(Arg::with_name("profiles")
.help("A list of comma separated profiles to activate")
.short("p")
.long("profiles")
.possible_values(profile_names.as_slice())
.takes_value(true)
.multiple(true)
.required(false)))
.subcommand(SubCommand::with_name(UNLINK)
.settings(subcommand_settings)
.about("Remove all symlinks defined in your bombadil.toml"))
.subcommand(SubCommand::with_name(ADD_SECRET)
.settings(subcommand_settings)
.about("Add a secret var to bombadil environment")
.arg(Arg::with_name("key")
.help("Key of the secret variable to create")
.short("k")
.long("key")
.takes_value(true)
.required(true))
.arg(Arg::with_name("value")
.help("Value of the secret variable to create")
.short("v")
.long("value")
.takes_value(true)
.required_unless("ask"))
.arg(Arg::with_name("ask")
.help("Get the secret value from stdin")
.short("a")
.long("ask")
.takes_value(false)
.required_unless("value"))
.arg(Arg::with_name("file")
.help("Path of the var file to modify")
.short("f")
.long("file")
.takes_value(true)
.required(true)))
.subcommand(SubCommand::with_name(GET)
.settings(subcommand_settings)
.about("Get metadata about dots, hooks, path, profiles, or vars")
.arg(Arg::with_name("value")
.possible_values(&["dots", "hooks", "path", "profiles", "vars", "secrets"])
.default_value("dots")
.takes_value(true)
.help("Metadata to get"))
.arg(Arg::with_name("profiles")
.short("p")
.long("profiles")
.takes_value(true)
.possible_values(profile_names.as_slice())
.multiple(true)
.help("Get metadata for specific profiles")
)
)
.subcommand(SubCommand::with_name(GENERATE_COMPLETIONS)
.settings(subcommand_settings)
.about("Generate shell completions")
.arg(Arg::with_name("type")
.possible_values(&["bash", "elvish", "fish", "zsh"])
.required(true)
.takes_value(true)
.help("Type of completions to generate")
)
)
}

fn main() {
let profiles = Settings::get()
.map(|settings| settings.profiles)
Expand All @@ -132,21 +23,21 @@ fn main() {
.map(|profile| profile.0.as_str())
.collect::<Vec<&str>>();

let matches = build_cli(profile_names.clone()).get_matches();
let matches = cli::build_cli(profile_names.clone()).get_matches();

if let Some(subcommand) = matches.subcommand_name() {
match subcommand {
INSTALL => {
let install_commmand = matches.subcommand_matches(INSTALL).unwrap();
cli::INSTALL => {
let install_commmand = matches.subcommand_matches(cli::INSTALL).unwrap();
let config_path = install_commmand.value_of("CONFIG").map(PathBuf::from);

Bombadil::link_self_config(config_path).unwrap_or_else(|err| fatal!("{}", err));
}

LINK => {
cli::LINK => {
let mut bombadil =
Bombadil::from_settings(Mode::Gpg).unwrap_or_else(|err| fatal!("{}", err));
let link_command = matches.subcommand_matches(LINK).unwrap();
let link_command = matches.subcommand_matches(cli::LINK).unwrap();

if link_command.is_present("profiles") {
let profiles: Vec<_> = link_command.values_of("profiles").unwrap().collect();
Expand All @@ -157,13 +48,13 @@ fn main() {

bombadil.install().unwrap_or_else(|err| fatal!("{}", err));
}
UNLINK => {
cli::UNLINK => {
let bombadil =
Bombadil::from_settings(Mode::NoGpg).unwrap_or_else(|err| fatal!("{}", err));
bombadil.uninstall().unwrap_or_else(|err| fatal!("{}", err));
}
ADD_SECRET => {
let add_secret_subcommand = matches.subcommand_matches(ADD_SECRET).unwrap();
cli::ADD_SECRET => {
let add_secret_subcommand = matches.subcommand_matches(cli::ADD_SECRET).unwrap();
let key = add_secret_subcommand.value_of("key").unwrap();

let value = if add_secret_subcommand.is_present("ask") {
Expand All @@ -182,8 +73,8 @@ fn main() {
.add_secret(key, &value, var_file)
.unwrap_or_else(|err| fatal!("{}", err));
}
GET => {
let get_subcommand = matches.subcommand_matches(GET).unwrap();
cli::GET => {
let get_subcommand = matches.subcommand_matches(cli::GET).unwrap();
let metadata_type = match get_subcommand.value_of("value").unwrap() {
"dots" => MetadataType::Dots,
"hooks" => MetadataType::Hooks,
Expand All @@ -209,16 +100,18 @@ fn main() {

bombadil.print_metadata(metadata_type);
}
GENERATE_COMPLETIONS => {
let generate_subcommand = matches.subcommand_matches(GENERATE_COMPLETIONS).unwrap();
cli::GENERATE_COMPLETIONS => {
let generate_subcommand = matches
.subcommand_matches(cli::GENERATE_COMPLETIONS)
.unwrap();
let for_shell = match generate_subcommand.value_of("type").unwrap() {
"bash" => Shell::Bash,
"elvish" => Shell::Elvish,
"fish" => Shell::Fish,
"zsh" => Shell::Zsh,
_ => unreachable!(),
};
build_cli(profile_names).gen_completions_to(
cli::build_cli(profile_names).gen_completions_to(
"bombadil",
for_shell,
&mut std::io::stdout(),
Expand Down
111 changes: 111 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use clap::{App, AppSettings, Arg, SubCommand};

pub const LINK: &str = "link";
pub const UNLINK: &str = "unlink";
pub const INSTALL: &str = "install";
pub const ADD_SECRET: &str = "add-secret";
pub const GET: &str = "get";
pub const GENERATE_COMPLETIONS: &str = "generate-completions";

pub fn build_cli<'a, 'b>(profile_names: Vec<&'a str>) -> App<'a, 'b>
where
'a: 'b,
{
let app_settings = &[
AppSettings::SubcommandRequiredElseHelp,
AppSettings::UnifiedHelpMessage,
AppSettings::ColoredHelp,
AppSettings::VersionlessSubcommands,
];

let subcommand_settings = &[
AppSettings::UnifiedHelpMessage,
AppSettings::ColoredHelp,
AppSettings::VersionlessSubcommands,
];

App::new("Toml Bombadil")
.settings(app_settings)
.version(env!("CARGO_PKG_VERSION"))
.author("Paul D. <[email protected]>")
.about("A dotfile template manager")
.long_about("Toml is a dotfile template manager, written in rust. \
For more info on how to configure it please go to https://github.com/oknozor/toml-bombadil")
.subcommand(SubCommand::with_name(INSTALL)
.settings(subcommand_settings)
.about("Link a given bombadil config to XDG_CONFIG_DIR/bombadil.toml")
.arg(Arg::with_name("CONFIG")
.help("path to your bombadil.toml config file inside your dotfiles directory")
.short("c")
.long("config")
.takes_value(true)
.required(true)))
.subcommand(SubCommand::with_name(LINK)
.settings(subcommand_settings)
.about("Symlink a copy of your dotfiles and inject variables according to bombadil.toml config")
.arg(Arg::with_name("profiles")
.help("A list of comma separated profiles to activate")
.short("p")
.long("profiles")
.possible_values(profile_names.as_slice())
.takes_value(true)
.multiple(true)
.required(false)))
.subcommand(SubCommand::with_name(UNLINK)
.settings(subcommand_settings)
.about("Remove all symlinks defined in your bombadil.toml"))
.subcommand(SubCommand::with_name(ADD_SECRET)
.settings(subcommand_settings)
.about("Add a secret var to bombadil environment")
.arg(Arg::with_name("key")
.help("Key of the secret variable to create")
.short("k")
.long("key")
.takes_value(true)
.required(true))
.arg(Arg::with_name("value")
.help("Value of the secret variable to create")
.short("v")
.long("value")
.takes_value(true)
.required_unless("ask"))
.arg(Arg::with_name("ask")
.help("Get the secret value from stdin")
.short("a")
.long("ask")
.takes_value(false)
.required_unless("value"))
.arg(Arg::with_name("file")
.help("Path of the var file to modify")
.short("f")
.long("file")
.takes_value(true)
.required(true)))
.subcommand(SubCommand::with_name(GET)
.settings(subcommand_settings)
.about("Get metadata about dots, hooks, path, profiles, or vars")
.arg(Arg::with_name("value")
.possible_values(&["dots", "hooks", "path", "profiles", "vars", "secrets"])
.default_value("dots")
.takes_value(true)
.help("Metadata to get"))
.arg(Arg::with_name("profiles")
.short("p")
.long("profiles")
.takes_value(true)
.possible_values(profile_names.as_slice())
.multiple(true)
.help("Get metadata for specific profiles")
)
)
.subcommand(SubCommand::with_name(GENERATE_COMPLETIONS)
.settings(subcommand_settings)
.about("Generate shell completions")
.arg(Arg::with_name("type")
.possible_values(&["bash", "elvish", "fish", "zsh"])
.required(true)
.takes_value(true)
.help("Type of completions to generate")
)
)
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use std::fs;
use std::os::unix;
use std::path::{Path, PathBuf};

pub mod cli;
mod dots;
mod gpg;
mod hook;
Expand Down