From f324deb41ec92040a703605c167ff4761b6f6a15 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 2 Jan 2018 13:02:09 +0100 Subject: [PATCH] imag-init: initial import --- Cargo.toml | 1 + bin/core/imag-init/Cargo.toml | 28 ++++ bin/core/imag-init/README.md | 1 + bin/core/imag-init/src/main.rs | 226 +++++++++++++++++++++++++++++++++ bin/core/imag-init/src/ui.rs | 49 +++++++ doc/src/04020-module-init.md | 9 ++ 6 files changed, 314 insertions(+) create mode 100644 bin/core/imag-init/Cargo.toml create mode 120000 bin/core/imag-init/README.md create mode 100644 bin/core/imag-init/src/main.rs create mode 100644 bin/core/imag-init/src/ui.rs create mode 100644 doc/src/04020-module-init.md diff --git a/Cargo.toml b/Cargo.toml index 6416893e..d0e3aad3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "bin/core/imag-diagnostics", "bin/core/imag-gps", "bin/core/imag-grep", + "bin/core/imag-init", "bin/core/imag-link", "bin/core/imag-mv", "bin/core/imag-ref", diff --git a/bin/core/imag-init/Cargo.toml b/bin/core/imag-init/Cargo.toml new file mode 100644 index 00000000..547e36b2 --- /dev/null +++ b/bin/core/imag-init/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "imag-init" +version = "0.6.0" +authors = ["Matthias Beyer "] + +description = "Part of the imag core distribution: imag-init command" + +keywords = ["imag", "PIM", "personal", "information", "management"] +readme = "../../../README.md" +license = "LGPL-2.1" + +documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html" +repository = "https://github.com/matthiasbeyer/imag" +homepage = "http://imag-pim.org" + +[badges] +travis-ci = { repository = "matthiasbeyer/imag" } +is-it-maintained-issue-resolution = { repository = "matthiasbeyer/imag" } +is-it-maintained-open-issues = { repository = "matthiasbeyer/imag" } +maintenance = { status = "actively-developed" } + +[dependencies] +clap = ">=2.17" +version = "2.0.1" + +[dev-dependencies] +toml = "0.4" + diff --git a/bin/core/imag-init/README.md b/bin/core/imag-init/README.md new file mode 120000 index 00000000..9a0031bf --- /dev/null +++ b/bin/core/imag-init/README.md @@ -0,0 +1 @@ +../../../doc/src/04020-module-init.md \ No newline at end of file diff --git a/bin/core/imag-init/src/main.rs b/bin/core/imag-init/src/main.rs new file mode 100644 index 00000000..03908d91 --- /dev/null +++ b/bin/core/imag-init/src/main.rs @@ -0,0 +1,226 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +#![deny( + non_camel_case_types, + non_snake_case, + path_statements, + trivial_numeric_casts, + unstable_features, + unused_allocation, + unused_import_braces, + unused_imports, + unused_must_use, + unused_mut, + unused_qualifications, + while_true, +)] + +extern crate clap; +#[macro_use] extern crate version; + +#[cfg(test)] +extern crate toml; + +mod ui; + +use std::fs::OpenOptions; +use std::io::Write; +use std::path::PathBuf; +use std::path::Path; +use std::process::Command; + +const CONFIGURATION_STR : &'static str = include_str!("../../../../imagrc.toml"); +const GITIGNORE_STR : &'static str = r#" +# We ignore the imagrc.toml file by default +# +# That is because we expect the user to put +# this dotfile into his dotfile repository +# and symlink it here. +# If you do not do this, feel free to remove +# this line from the gitignore and add the +# configuration to this git repository. + +imagrc.toml +"#; + +fn main() { + let app = ui::build_ui(); + let matches = app.get_matches(); + + let path = matches + .value_of("path") + .map(String::from) + .map(PathBuf::from) + .unwrap_or_else(|| { + ::std::env::var("HOME") + .map(PathBuf::from) + .map(|mut p| { p.push(".imag"); p }) + .map(|path| if path.exists() { + println!("Path '{:?}' already exists!", path); + println!("Cannot continue."); + ::std::process::exit(1) + } else { + path + }) + .expect("Failed to retrieve/build path for imag directory.") + }); + + let _ = ::std::fs::create_dir_all(path.clone()) + .expect("Failed to create directory"); + + let config_path = { + let mut config_path = path.clone(); + config_path.push("imagrc.toml"); + config_path + }; + + let _ = OpenOptions::new() + .write(true) + .create(true) + .open(config_path) + .map(|mut f| { + let content = if matches.is_present("devel") { + get_config_devel() + } else { + get_config() + }; + + let _ = f.write_all(content.as_bytes()) + .expect("Failed to write complete config to file"); + }) + .expect("Failed to open new configuration file"); + + if find_command("git").is_some() && !matches.is_present("nogit") { + // we initialize a git repository + println!("Going to initialize a git repository in the imag directory..."); + + let gitignore_path = { + let mut gitignore_path = path.clone(); + gitignore_path.push(".gitignore"); + gitignore_path.to_str().map(String::from).expect("Cannot convert path to string") + }; + + let _ = OpenOptions::new() + .write(true) + .create(true) + .open(gitignore_path.clone()) + .map(|mut f| { + let _ = f.write_all(GITIGNORE_STR.as_bytes()) + .expect("Failed to write complete gitignore to file"); + }) + .expect("Failed to open new configuration file"); + + let path_str = path.to_str().map(String::from).expect("Cannot convert path to string"); + let worktree = format!("--work-tree={}", path_str); + let gitdir = format!("--git-dir={}", path_str); + + { + let output = Command::new("git") + .args(&[&worktree, &gitdir, "--no-pager", "init"]) + .output() + .expect("Calling 'git init' failed"); + + if output.status.success() { + println!("{}", String::from_utf8(output.stdout).expect("No UTF-8 output")); + println!("'git {} {} --no-pager init' succeeded", worktree, gitdir); + } else { + println!("{}", String::from_utf8(output.stderr).expect("No UTF-8 output")); + ::std::process::exit(output.status.code().unwrap_or(1)); + } + } + + { + let output = Command::new("git") + .args(&[&worktree, &gitdir, "--no-pager", "add", &gitignore_path]) + .output() + .expect("Calling 'git add' failed"); + if output.status.success() { + println!("{}", String::from_utf8(output.stdout).expect("No UTF-8 output")); + println!("'git {} {} --no-pager add {}' succeeded", worktree, gitdir, gitignore_path); + } else { + println!("{}", String::from_utf8(output.stderr).expect("No UTF-8 output")); + ::std::process::exit(output.status.code().unwrap_or(1)); + } + } + + { + let output = Command::new("git") + .args(&[&worktree, &gitdir, "--no-pager", "commit", &gitignore_path, "-m", "'Initial import'"]) + .output() + .expect("Calling 'git commit' failed"); + if output.status.success() { + println!("{}", String::from_utf8(output.stdout).expect("No UTF-8 output")); + println!("'git {} {} --no-pager commit {} -m 'Initial import'' succeeded", worktree, gitdir, gitignore_path); + } else { + println!("{}", String::from_utf8(output.stderr).expect("No UTF-8 output")); + ::std::process::exit(output.status.code().unwrap_or(1)); + } + } + + println!("git stuff finished!"); + } else { + println!("No git repository will be initialized"); + } + + println!("Ready. Have fun with imag!"); +} + +fn get_config() -> String { + get_config_devel() + .replace( + r#"level = "debug""#, + r#"level = "info""# + ) +} + +fn get_config_devel() -> String { + String::from(CONFIGURATION_STR) +} + +fn find_command>(exe_name: P) -> Option { + ::std::env::var_os("PATH") + .and_then(|paths| { + ::std::env::split_paths(&paths) + .filter_map(|dir| { + let full_path = dir.join(&exe_name); + if full_path.is_file() { + Some(full_path) + } else { + None + } + }) + .next() + }) +} + +#[cfg(test)] +mod tests { + use toml::from_str; + use toml::Value; + use super::get_config; + use super::get_config_devel; + + #[test] + fn test_config() { + assert!(from_str::(&get_config()[..]).is_ok()); + assert!(from_str::(&get_config_devel()[..]).is_ok()); + } + +} diff --git a/bin/core/imag-init/src/ui.rs b/bin/core/imag-init/src/ui.rs new file mode 100644 index 00000000..9bcb7d63 --- /dev/null +++ b/bin/core/imag-init/src/ui.rs @@ -0,0 +1,49 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use clap::{Arg, App}; + +pub fn build_ui<'a>() -> App<'a, 'a> { + App::new("imag-init") + .version(&version!()[..]) + .author("Matthias Beyer ") + .about("Initialize a ~/.imag repository. Optionally with git") + + .arg(Arg::with_name("devel") + .long("dev") + .takes_value(false) + .required(false) + .multiple(false) + .help("Put dev configuration into the generated repo (with debugging enabled)")) + + .arg(Arg::with_name("nogit") + .long("no-git") + .takes_value(false) + .required(false) + .multiple(false) + .help("Do not initialize git repository, even if 'git' executable is in $PATH")) + + .arg(Arg::with_name("path") + .long("path") + .takes_value(true) + .required(false) + .multiple(false) + .help("Alternative path where to put the repository. Default: ~/.imag")) +} + diff --git a/doc/src/04020-module-init.md b/doc/src/04020-module-init.md new file mode 100644 index 00000000..235a5f50 --- /dev/null +++ b/doc/src/04020-module-init.md @@ -0,0 +1,9 @@ +## Init {#sec:modules:init} + +This is the only `imag-*` command which does _not_ set up a runtime and check +whether the store is available. This command can be used to set up a imag store. + +It also puts a default configuration in the right place and initializes a git +repository, if there is a `git` command in `$PATH` (via calling git on the +commandline, not via `libgit2` or some other library). +