imag-category: Initial import

This commit is contained in:
Matthias Beyer 2018-04-26 11:19:14 +02:00
parent 2e2bce77a0
commit 61d0136c26
7 changed files with 408 additions and 0 deletions

View file

@ -2,6 +2,7 @@
members = [ members = [
"bin/core/imag", "bin/core/imag",
"bin/core/imag-annotate", "bin/core/imag-annotate",
"bin/core/imag-category",
"bin/core/imag-diagnostics", "bin/core/imag-diagnostics",
"bin/core/imag-edit", "bin/core/imag-edit",
"bin/core/imag-git", "bin/core/imag-git",

View file

@ -0,0 +1,39 @@
[package]
name = "imag-category"
version = "0.8.0"
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
description = "Part of the imag core distribution: imag-category command"
keywords = ["imag", "PIM", "personal", "information", "management"]
readme = "../../../README.md"
license = "LGPL-2.1"
documentation = "https://imag-pim.org/doc/"
repository = "https://github.com/matthiasbeyer/imag"
homepage = "http://imag-pim.org"
build = "../../../build.rs"
[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]
log = "0.4.0"
toml = "0.4"
toml-query = "0.6"
libimagstore = { version = "0.8.0", path = "../../../lib/core/libimagstore" }
libimagrt = { version = "0.8.0", path = "../../../lib/core/libimagrt" }
libimagerror = { version = "0.8.0", path = "../../../lib/core/libimagerror" }
libimagentrycategory = { version = "0.8.0", path = "../../../lib/entry/libimagentrycategory" }
libimaginteraction = { version = "0.8.0", path = "../../../lib/etc/libimaginteraction" }
[dependencies.clap]
version = "^2.29"
default-features = false
features = ["color", "suggestions", "wrap_help"]

View file

@ -0,0 +1 @@
../../../doc/src/04020-module-category.md

View file

@ -0,0 +1,240 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> 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 log;
extern crate libimagentrycategory;
extern crate libimagerror;
#[macro_use] extern crate libimagrt;
extern crate libimagstore;
extern crate libimaginteraction;
use libimagerror::trace::MapErrTrace;
use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;
use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagstore::storeid::IntoStoreId;
mod ui;
use std::io::Write;
use std::io::Read;
use std::path::PathBuf;
use libimagentrycategory::store::CategoryStore;
use libimagstore::storeid::StoreIdIterator;
use libimagstore::iter::get::StoreIdGetIteratorExtension;
use libimagerror::iter::TraceIterator;
use libimagentrycategory::entry::EntryCategory;
use libimagentrycategory::category::Category;
fn main() {
let version = make_imag_version!();
let rt = generate_runtime_setup("imag-category",
&version,
"Add a category to entries and manage categories",
ui::build_ui);
rt.cli()
.subcommand_name()
.map(|name| {
match name {
"set" => set(&rt),
"get" => get(&rt),
"list-category" => list_category(&rt),
"create-category" => create_category(&rt),
"delete-category" => delete_category(&rt),
"list-categories" => list_categories(&rt),
other => {
debug!("Unknown command");
let _ = rt.handle_unknown_subcommand("imag-category", other, rt.cli())
.map_err_trace_exit_unwrap(1)
.code()
.map(::std::process::exit);
},
}
});
}
fn set(rt: &Runtime) {
let scmd = rt.cli().subcommand_matches("set").unwrap(); // safed by main()
let name = scmd.value_of("set-name").map(String::from).unwrap(); // safed by clap
let sids = match scmd.value_of("set-ids") {
Some(path) => vec![PathBuf::from(path).into_storeid().map_err_trace_exit_unwrap(1)],
None => if rt.cli().is_present("entries-from-stdin") {
let stdin = rt.stdin().unwrap_or_else(|| {
error!("Cannot get handle to stdin");
::std::process::exit(1)
});
let mut buf = String::new();
let _ = stdin.lock().read_to_string(&mut buf).unwrap_or_else(|_| {
error!("Failed to read from stdin");
::std::process::exit(1)
});
buf.lines()
.map(PathBuf::from)
.map(|p| p.into_storeid().map_err_trace_exit_unwrap(1))
.collect()
} else {
error!("Something weird happened. I was not able to find the path of the entries to edit");
::std::process::exit(1)
}
};
StoreIdIterator::new(Box::new(sids.into_iter().map(Ok)))
.into_get_iter(rt.store())
.trace_unwrap_exit(1)
.map(|o| o.unwrap_or_else(|| {
error!("Did not find one entry");
::std::process::exit(1)
}))
.for_each(|mut entry| {
let _ = entry
.set_category_checked(rt.store(), &name)
.map_err_trace_exit_unwrap(1);
})
}
fn get(rt: &Runtime) {
let scmd = rt.cli().subcommand_matches("get").unwrap(); // safed by main()
let sids = match scmd.value_of("get-ids") {
Some(path) => vec![PathBuf::from(path).into_storeid().map_err_trace_exit_unwrap(1)],
None => if rt.cli().is_present("entries-from-stdin") {
let stdin = rt.stdin().unwrap_or_else(|| {
error!("Cannot get handle to stdin");
::std::process::exit(1)
});
let mut buf = String::new();
let _ = stdin.lock().read_to_string(&mut buf).unwrap_or_else(|_| {
error!("Failed to read from stdin");
::std::process::exit(1)
});
buf.lines()
.map(PathBuf::from)
.map(|p| p.into_storeid().map_err_trace_exit_unwrap(1))
.collect()
} else {
error!("Something weird happened. I was not able to find the path of the entries to edit");
::std::process::exit(1)
}
};
let out = rt.stdout();
let mut outlock = out.lock();
StoreIdIterator::new(Box::new(sids.into_iter().map(Ok)))
.into_get_iter(rt.store())
.trace_unwrap_exit(1)
.map(|o| o.unwrap_or_else(|| {
error!("Did not find one entry");
::std::process::exit(1)
}))
.map(|entry| entry.get_category().map_err_trace_exit_unwrap(1))
.for_each(|name| {
let _ = writeln!(outlock, "{}", name).to_exit_code().unwrap_or_exit();
})
}
fn list_category(rt: &Runtime) {
let scmd = rt.cli().subcommand_matches("list-category").unwrap(); // safed by main()
let name = scmd.value_of("list-category-name").map(String::from).unwrap(); // safed by clap
if let Some(category) = rt.store().get_category_by_name(&name).map_err_trace_exit_unwrap(1) {
let out = rt.stdout();
let mut outlock = out.lock();
category
.get_entries(rt.store())
.map_err_trace_exit_unwrap(1)
.for_each(|entry| {
writeln!(outlock, "{}", entry.map_err_trace_exit_unwrap(1).get_location())
.to_exit_code()
.unwrap_or_exit();
})
} else {
info!("No category named '{}'", name);
::std::process::exit(1)
}
}
fn create_category(rt: &Runtime) {
let scmd = rt.cli().subcommand_matches("create-category").unwrap(); // safed by main()
let name = scmd.value_of("create-category-name").map(String::from).unwrap(); // safed by clap
let _ = rt
.store()
.create_category(&name)
.map_err_trace_exit_unwrap(1);
}
fn delete_category(rt: &Runtime) {
use libimaginteraction::ask::ask_bool;
let scmd = rt.cli().subcommand_matches("delete-category").unwrap(); // safed by main()
let name = scmd.value_of("delete-category-name").map(String::from).unwrap(); // safed by clap
let ques = format!("Do you really want to delete category '{}' and remove links to all categorized enties?", name);
let answer = ask_bool(&ques, Some(false));
if answer {
info!("Deleting category '{}'", name);
let _ = rt
.store()
.delete_category(&name)
.map_err_trace_exit_unwrap(1);
} else {
info!("Not doing anything");
}
}
fn list_categories(rt: &Runtime) {
let out = rt.stdout();
let mut outlock = out.lock();
rt.store()
.all_category_names()
.map_err_trace_exit_unwrap(1)
.for_each(|name| {
writeln!(outlock, "{}", name.map_err_trace_exit_unwrap(1))
.to_exit_code()
.unwrap_or_exit();
})
}

View file

@ -0,0 +1,118 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> 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, ArgGroup, App, SubCommand};
pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
app
.subcommand(SubCommand::with_name("create-category")
.about("Create a new category")
.version("0.1")
.arg(Arg::with_name("create-category-name")
.index(1)
.takes_value(true)
.required(true)
.multiple(false)
.help("The name of the new category")
.value_name("NAME"))
)
.subcommand(SubCommand::with_name("delete-category")
.about("Delete a new category")
.version("0.1")
.arg(Arg::with_name("delete-category-name")
.index(1)
.takes_value(true)
.required(true)
.multiple(false)
.help("The name of the category to delete")
.value_name("NAME"))
)
.subcommand(SubCommand::with_name("list-categories")
.about("Show all category names")
.version("0.1"))
.subcommand(SubCommand::with_name("list-category")
.about("List all entries for a category")
.version("0.1")
.arg(Arg::with_name("list-category-name")
.index(1)
.takes_value(true)
.required(true)
.multiple(false)
.help("The name of the category to list all entries for")
.value_name("NAME"))
)
.subcommand(SubCommand::with_name("set")
.about("Set the category of entries")
.version("0.1")
.arg(Arg::with_name("set-name")
.index(1)
.takes_value(true)
.required(true)
.multiple(false)
.help("The name of the category to list all entries for")
.value_name("NAME"))
.arg(Arg::with_name("set-ids")
.index(2)
.takes_value(true)
.required(false)
.multiple(true)
.help("The entries to set the category for")
.value_name("ID"))
.arg(Arg::with_name("entries-from-stdin")
.long("ids-from-stdin")
.short("I")
.takes_value(false)
.required(false)
.multiple(false)
.help("Read the ids for the entries from stdin"))
.group(ArgGroup::with_name("input-method")
.args(&["set-ids", "entries-from-stdin"])
.required(true))
)
.subcommand(SubCommand::with_name("get")
.about("Get the category of the entry")
.version("0.1")
.arg(Arg::with_name("get-ids")
.index(1)
.takes_value(true)
.required(false)
.multiple(true)
.help("The id of the Entry to get the category for")
.value_name("ID"))
.arg(Arg::with_name("entries-from-stdin")
.long("ids-from-stdin")
.short("I")
.takes_value(false)
.required(false)
.multiple(false)
.help("Read the ids for the entries from stdin"))
.group(ArgGroup::with_name("input-method")
.args(&["get-ids", "entries-from-stdin"])
.required(true))
)
}

View file

@ -0,0 +1,8 @@
## Category {#sec:modules:category}
A tool to create categories and set/get them for entries.
The difference between a category and a tag is that a category must exist
before it can be used and all entries of a category are linked to the
"category entry" internally.

View file

@ -62,6 +62,7 @@ CRATES=(
./bin/core/imag-edit ./bin/core/imag-edit
./bin/core/imag-ids ./bin/core/imag-ids
./bin/core/imag-git ./bin/core/imag-git
./bin/core/imag-category
./bin/core/imag ./bin/core/imag
) )