Merge pull request #1179 from matthiasbeyer/imag-link/intelligent-positional-args
Rewrite imag-link commandline to be intelligent
This commit is contained in:
commit
4819061d68
3 changed files with 227 additions and 366 deletions
|
@ -51,21 +51,20 @@ extern crate libimagutil;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
extern crate libimagutil;
|
extern crate libimagutil;
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use libimagentrylink::external::ExternalLinker;
|
||||||
|
use libimagentrylink::internal::InternalLinker;
|
||||||
|
use libimagentrylink::internal::store_check::StoreLinkConsistentExt;
|
||||||
|
use libimagentrylink::error::LinkError as LE;
|
||||||
|
use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
|
||||||
use libimagrt::runtime::Runtime;
|
use libimagrt::runtime::Runtime;
|
||||||
use libimagrt::setup::generate_runtime_setup;
|
use libimagrt::setup::generate_runtime_setup;
|
||||||
use libimagstore::error::StoreError;
|
use libimagstore::error::StoreError;
|
||||||
use libimagstore::store::Entry;
|
|
||||||
use libimagstore::store::FileLockEntry;
|
use libimagstore::store::FileLockEntry;
|
||||||
use libimagstore::store::Store;
|
|
||||||
use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
|
|
||||||
use libimagentrylink::external::ExternalLinker;
|
|
||||||
use libimagentrylink::internal::InternalLinker;
|
|
||||||
use libimagutil::warn_result::*;
|
|
||||||
use libimagutil::warn_exit::warn_exit;
|
use libimagutil::warn_exit::warn_exit;
|
||||||
use libimagutil::info_result::*;
|
use libimagutil::warn_result::*;
|
||||||
use clap::ArgMatches;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
mod ui;
|
mod ui;
|
||||||
|
@ -77,271 +76,183 @@ fn main() {
|
||||||
&version!()[..],
|
&version!()[..],
|
||||||
"Link entries",
|
"Link entries",
|
||||||
build_ui);
|
build_ui);
|
||||||
|
if rt.cli().is_present("check-consistency") {
|
||||||
|
let exit_code = match rt.store().check_link_consistency() {
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Store is consistent");
|
||||||
|
0
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
trace_error(&e);
|
||||||
|
1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
::std::process::exit(exit_code);
|
||||||
|
}
|
||||||
|
|
||||||
rt.cli()
|
let _ = rt.cli()
|
||||||
.subcommand_name()
|
.subcommand_name()
|
||||||
.map(|name| {
|
.map(|name| {
|
||||||
match name {
|
match name {
|
||||||
"internal" => handle_internal_linking(&rt),
|
"remove" => remove_linking(&rt),
|
||||||
"external" => handle_external_linking(&rt),
|
"list" => list_linkings(&rt),
|
||||||
_ => warn_exit("No commandline call", 1)
|
_ => panic!("BUG"),
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_internal_linking(rt: &Runtime) {
|
|
||||||
use libimagentrylink::internal::store_check::StoreLinkConsistentExt;
|
|
||||||
|
|
||||||
debug!("Handle internal linking call");
|
|
||||||
let cmd = rt.cli().subcommand_matches("internal").unwrap();
|
|
||||||
|
|
||||||
if cmd.is_present("check-consistency") {
|
|
||||||
match rt.store().check_link_consistency() {
|
|
||||||
Ok(_) => {
|
|
||||||
info!("Store is consistent");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
trace_error(&e);
|
|
||||||
::std::process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match cmd.subcommand_name() {
|
|
||||||
Some("list") => {
|
|
||||||
cmd.subcommand_matches("list")
|
|
||||||
.map(|matches| handle_internal_linking_list_call(rt, cmd, matches));
|
|
||||||
},
|
|
||||||
Some("add") => {
|
|
||||||
let (mut from, to) = get_from_to_entry(&rt, "add");
|
|
||||||
for mut to_entry in to {
|
|
||||||
if let Err(e) = to_entry.add_internal_link(&mut from) {
|
|
||||||
trace_error_exit(&e, 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
Some("remove") => {
|
|
||||||
let (mut from, to) = get_from_to_entry(&rt, "remove");
|
|
||||||
for mut to_entry in to {
|
|
||||||
if let Err(e) = to_entry.remove_internal_link(&mut from) {
|
|
||||||
trace_error_exit(&e, 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn handle_internal_linking_list_call(rt: &Runtime, cmd: &ArgMatches, list: &ArgMatches) {
|
|
||||||
use libimagentrylink::external::is_external_link_storeid;
|
|
||||||
|
|
||||||
debug!("List...");
|
|
||||||
for entry in list.values_of("entries").unwrap() { // clap has our back
|
|
||||||
debug!("Listing for '{}'", entry);
|
|
||||||
match get_entry_by_name(rt, entry) {
|
|
||||||
Ok(Some(e)) => {
|
|
||||||
e.get_internal_links()
|
|
||||||
.map(|iter| {
|
|
||||||
iter.filter(move |id| {
|
|
||||||
cmd.is_present("list-externals-too") || !is_external_link_storeid(&id)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map(|links| {
|
|
||||||
let i = links
|
|
||||||
.filter_map(|l| {
|
|
||||||
l.to_str()
|
|
||||||
.map_warn_err(|e| format!("Failed to convert StoreId to string: {:?}", e))
|
|
||||||
.ok()
|
|
||||||
})
|
|
||||||
.enumerate();
|
|
||||||
|
|
||||||
for (i, link) in i {
|
|
||||||
println!("{: <3}: {}", i, link);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map_err_trace()
|
|
||||||
.ok();
|
|
||||||
},
|
|
||||||
|
|
||||||
Ok(None) => {
|
|
||||||
warn!("Entry not found: {:?}", entry);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(e) => {
|
|
||||||
trace_error(&e);
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug!("Listing ready!");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_from_to_entry<'a>(rt: &'a Runtime, subcommand: &str) -> (FileLockEntry<'a>, Vec<FileLockEntry<'a>>) {
|
|
||||||
let from = match get_from_entry(&rt, subcommand) {
|
|
||||||
None => warn_exit("No 'from' entry", 1),
|
|
||||||
Some(s) => s,
|
|
||||||
};
|
|
||||||
debug!("Link from = {:?}", from.deref());
|
|
||||||
|
|
||||||
let to = match get_to_entries(&rt, subcommand) {
|
|
||||||
None => warn_exit("No 'to' entry", 1),
|
|
||||||
Some(to) => to,
|
|
||||||
};
|
|
||||||
debug!("Link to = {:?}", to.iter().map(|f| f.deref()).collect::<Vec<&Entry>>());
|
|
||||||
|
|
||||||
(from, to)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_from_entry<'a>(rt: &'a Runtime, subcommand: &str) -> Option<FileLockEntry<'a>> {
|
|
||||||
rt.cli()
|
|
||||||
.subcommand_matches("internal")
|
|
||||||
.unwrap() // safe, we know there is an "internal" subcommand"
|
|
||||||
.subcommand_matches(subcommand)
|
|
||||||
.unwrap() // safe, we know there is an "add" subcommand
|
|
||||||
.value_of("from")
|
|
||||||
.and_then(|from_name| {
|
|
||||||
match get_entry_by_name(rt, from_name) {
|
|
||||||
Err(e) => {
|
|
||||||
debug!("We couldn't get the entry from name: '{:?}'", from_name);
|
|
||||||
trace_error(&e); None
|
|
||||||
},
|
|
||||||
Ok(Some(e)) => Some(e),
|
|
||||||
Ok(None) => None,
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
.or_else(|| {
|
||||||
|
if let (Some(from), Some(to)) = (rt.cli().value_of("from"), rt.cli().values_of("to")) {
|
||||||
fn get_to_entries<'a>(rt: &'a Runtime, subcommand: &str) -> Option<Vec<FileLockEntry<'a>>> {
|
Some(link_from_to(&rt, from, to))
|
||||||
rt.cli()
|
} else {
|
||||||
.subcommand_matches("internal")
|
warn_exit("No commandline call", 1)
|
||||||
.unwrap() // safe, we know there is an "internal" subcommand"
|
|
||||||
.subcommand_matches(subcommand)
|
|
||||||
.unwrap() // safe, we know there is an "add" subcommand
|
|
||||||
.values_of("to")
|
|
||||||
.map(|values| {
|
|
||||||
let mut v = vec![];
|
|
||||||
for entry in values.map(|v| get_entry_by_name(rt, v)) {
|
|
||||||
match entry {
|
|
||||||
Err(e) => trace_error(&e),
|
|
||||||
Ok(Some(e)) => v.push(e),
|
|
||||||
Ok(None) => warn!("Entry not found: {:?}", v),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
v
|
|
||||||
})
|
})
|
||||||
|
.ok_or(LE::from("No commandline call".to_owned()))
|
||||||
|
.map_err_trace_exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<Option<FileLockEntry<'a>>, StoreError> {
|
fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<Option<FileLockEntry<'a>>, StoreError> {
|
||||||
use std::path::PathBuf;
|
|
||||||
use libimagstore::storeid::StoreId;
|
use libimagstore::storeid::StoreId;
|
||||||
|
|
||||||
StoreId::new(Some(rt.store().path().clone()), PathBuf::from(name))
|
StoreId::new(Some(rt.store().path().clone()), PathBuf::from(name))
|
||||||
.and_then(|id| rt.store().get(id))
|
.and_then(|id| rt.store().get(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_external_linking(rt: &Runtime) {
|
fn link_from_to<'a, I>(rt: &'a Runtime, from: &'a str, to: I)
|
||||||
let scmd = rt.cli().subcommand_matches("external").unwrap();
|
where I: Iterator<Item = &'a str>
|
||||||
let entry_name = scmd.value_of("id").unwrap(); // enforced by clap
|
{
|
||||||
let mut entry = match get_entry_by_name(rt, entry_name) {
|
let mut from_entry = match get_entry_by_name(rt, from) {
|
||||||
Err(e) => trace_error_exit(&e, 1),
|
Ok(Some(e)) => e,
|
||||||
Ok(None) => {
|
Ok(None) => warn_exit("No 'from' entry", 1),
|
||||||
warn!("Entry not found: {:?}", entry_name);
|
Err(e) => trace_error_exit(&e, 1),
|
||||||
return;
|
|
||||||
},
|
|
||||||
Ok(Some(entry)) => entry
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if scmd.is_present("add") {
|
for entry in to {
|
||||||
debug!("Adding link to entry!");
|
if PathBuf::from(entry).exists() {
|
||||||
add_link_to_entry(rt.store(), scmd, &mut entry);
|
debug!("Linking externally: {:?} -> {:?}", from, entry);
|
||||||
return;
|
let url = Url::parse(entry).map_err_trace_exit_unwrap(1);
|
||||||
}
|
let _ = from_entry
|
||||||
|
.add_external_link(rt.store(), url)
|
||||||
if scmd.is_present("remove") {
|
.map_err_trace_exit_unwrap(1);
|
||||||
debug!("Removing link from entry!");
|
} else {
|
||||||
remove_link_from_entry(rt.store(), scmd, &mut entry);
|
debug!("Linking internally: {:?} -> {:?}", from, entry);
|
||||||
return;
|
let mut to_entry = match get_entry_by_name(rt, entry) {
|
||||||
}
|
Ok(Some(e)) => e,
|
||||||
|
Ok(None) => {
|
||||||
if scmd.is_present("set") {
|
warn!("No 'to' entry: {}", entry);
|
||||||
debug!("Setting links in entry!");
|
::std::process::exit(1)
|
||||||
set_links_for_entry(rt.store(), scmd, &mut entry);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if scmd.is_present("list") {
|
|
||||||
debug!("Listing links in entry!");
|
|
||||||
list_links_for_entry(rt.store(), &mut entry);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
panic!("Clap failed to enforce one of 'add', 'remove', 'set' or 'list'");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_link_to_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
|
|
||||||
Url::parse(matches.value_of("add").unwrap())
|
|
||||||
.map_err_trace_exit(1)
|
|
||||||
.map(|link| entry.add_external_link(store, link).map_err_trace().map_info_str("Ok"))
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_link_from_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
|
|
||||||
Url::parse(matches.value_of("remove").unwrap())
|
|
||||||
.map_err_trace_exit(1)
|
|
||||||
.map(|link| entry.remove_external_link(store, link).map_err_trace().map_info_str("Ok"))
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_links_for_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
|
|
||||||
let links = matches
|
|
||||||
.value_of("links")
|
|
||||||
.map(String::from)
|
|
||||||
.unwrap()
|
|
||||||
.split(',')
|
|
||||||
.map(|uri| {
|
|
||||||
match Url::parse(uri) {
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Could not parse '{}' as URL, ignoring", uri);
|
|
||||||
trace_error(&e);
|
|
||||||
None
|
|
||||||
},
|
},
|
||||||
Ok(u) => Some(u),
|
Err(e) => trace_error_exit(&e, 1),
|
||||||
}
|
};
|
||||||
})
|
let _ = from_entry
|
||||||
.filter_map(|x| x)
|
.add_internal_link(&mut to_entry)
|
||||||
.collect();
|
.map_err_trace_exit_unwrap(1);
|
||||||
|
}
|
||||||
|
|
||||||
entry.set_external_links(store, links)
|
info!("Ok: {} -> {}", from, entry);
|
||||||
.map_err_trace()
|
}
|
||||||
.map_info_str("Ok")
|
|
||||||
.ok();
|
info!("Ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_links_for_entry(store: &Store, entry: &mut FileLockEntry) {
|
fn remove_linking(rt: &Runtime) {
|
||||||
entry.get_external_links(store)
|
|
||||||
.and_then(|links| {
|
fn get_from_entry<'a>(rt: &'a Runtime) -> Option<FileLockEntry<'a>> {
|
||||||
for (i, link) in links.enumerate() {
|
rt.cli()
|
||||||
match link {
|
.subcommand_matches("remove")
|
||||||
Ok(link) => println!("{: <3}: {}", i, link),
|
.unwrap() // safe, we know there is an "remove" subcommand
|
||||||
Err(e) => trace_error(&e),
|
.value_of("from")
|
||||||
|
.and_then(|from_name| {
|
||||||
|
match get_entry_by_name(rt, from_name) {
|
||||||
|
Err(e) => {
|
||||||
|
debug!("We couldn't get the entry from name: '{:?}'", from_name);
|
||||||
|
trace_error(&e); None
|
||||||
|
},
|
||||||
|
Ok(Some(e)) => Some(e),
|
||||||
|
Ok(None) => None,
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut from = match get_from_entry(&rt) {
|
||||||
|
None => warn_exit("No 'from' entry", 1),
|
||||||
|
Some(s) => s,
|
||||||
|
};
|
||||||
|
|
||||||
|
rt.cli()
|
||||||
|
.subcommand_matches("remove")
|
||||||
|
.unwrap()
|
||||||
|
.values_of("to")
|
||||||
|
.map(|values| {
|
||||||
|
for (entry, value) in values.map(|v| (get_entry_by_name(rt, v), v)) {
|
||||||
|
match entry {
|
||||||
|
Err(e) => trace_error(&e),
|
||||||
|
Ok(Some(mut to_entry)) => {
|
||||||
|
if let Err(e) = to_entry.remove_internal_link(&mut from) {
|
||||||
|
trace_error_exit(&e, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Ok(None) => {
|
||||||
|
// looks like this is not an entry, but a filesystem URI and therefor an
|
||||||
|
// external link...?
|
||||||
|
if PathBuf::from(value).is_file() {
|
||||||
|
let url = Url::parse(value).map_err_trace_exit_unwrap(1);
|
||||||
|
from.remove_external_link(rt.store(), url).map_err_trace_exit_unwrap(1);
|
||||||
|
info!("Ok: {}", value);
|
||||||
|
} else {
|
||||||
|
warn!("Entry not found: {:?}", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
});
|
||||||
})
|
}
|
||||||
.map_err_trace()
|
|
||||||
.map_info_str("Ok")
|
fn list_linkings(rt: &Runtime) {
|
||||||
.ok();
|
let cmd = rt.cli()
|
||||||
|
.subcommand_matches("list")
|
||||||
|
.unwrap(); // safed by clap
|
||||||
|
|
||||||
|
let list_externals = cmd.is_present("list-externals-too");
|
||||||
|
|
||||||
|
for entry in cmd.values_of("entries").unwrap() { // safed by clap
|
||||||
|
match rt.store().get(PathBuf::from(entry)) {
|
||||||
|
Ok(Some(entry)) => {
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
for link in entry.get_internal_links().map_err_trace_exit_unwrap(1) {
|
||||||
|
let link = link
|
||||||
|
.to_str()
|
||||||
|
.map_warn_err(|e| format!("Failed to convert StoreId to string: {:?}", e))
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
if let Some(link) = link {
|
||||||
|
println!("{: <3}: {}", i, link);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if list_externals {
|
||||||
|
for link in entry.get_external_links(rt.store()).map_err_trace_exit_unwrap(1) {
|
||||||
|
let link = link
|
||||||
|
.map_err_trace_exit_unwrap(1)
|
||||||
|
.into_string();
|
||||||
|
|
||||||
|
println!("{: <3}: {}", i, link);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Ok(None) => warn!("Not found: {}", entry),
|
||||||
|
Err(e) => trace_error(&e),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use handle_internal_linking;
|
use super::link_from_to;
|
||||||
|
use super::remove_linking;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
@ -399,7 +310,7 @@ mod tests {
|
||||||
let test_id1 = create_test_default_entry(&rt, "test1").unwrap();
|
let test_id1 = create_test_default_entry(&rt, "test1").unwrap();
|
||||||
let test_id2 = create_test_default_entry(&rt, "test2").unwrap();
|
let test_id2 = create_test_default_entry(&rt, "test2").unwrap();
|
||||||
|
|
||||||
handle_internal_linking(&rt);
|
link_from_to(&rt, "test1", vec!["test2"].into_iter());
|
||||||
|
|
||||||
let test_entry1 = rt.store().get(test_id1).unwrap().unwrap();
|
let test_entry1 = rt.store().get(test_id1).unwrap().unwrap();
|
||||||
let test_links1 = get_entry_links(&test_entry1).unwrap();
|
let test_links1 = get_entry_links(&test_entry1).unwrap();
|
||||||
|
@ -419,7 +330,7 @@ mod tests {
|
||||||
let test_id1 = create_test_default_entry(&rt, "test1").unwrap();
|
let test_id1 = create_test_default_entry(&rt, "test1").unwrap();
|
||||||
let test_id2 = create_test_default_entry(&rt, "test2").unwrap();
|
let test_id2 = create_test_default_entry(&rt, "test2").unwrap();
|
||||||
|
|
||||||
handle_internal_linking(&rt);
|
link_from_to(&rt, "test1", vec!["test2"].into_iter());
|
||||||
|
|
||||||
let test_entry1 = rt.store().get(test_id1).unwrap().unwrap();
|
let test_entry1 = rt.store().get(test_id1).unwrap().unwrap();
|
||||||
let test_links1 = get_entry_links(&test_entry1).unwrap();
|
let test_links1 = get_entry_links(&test_entry1).unwrap();
|
||||||
|
@ -439,8 +350,8 @@ mod tests {
|
||||||
let test_id1 = create_test_default_entry(&rt, "test1").unwrap();
|
let test_id1 = create_test_default_entry(&rt, "test1").unwrap();
|
||||||
let test_id2 = create_test_default_entry(&rt, "test2").unwrap();
|
let test_id2 = create_test_default_entry(&rt, "test2").unwrap();
|
||||||
|
|
||||||
handle_internal_linking(&rt);
|
link_from_to(&rt, "test1", vec!["test2"].into_iter());
|
||||||
handle_internal_linking(&rt);
|
link_from_to(&rt, "test1", vec!["test2"].into_iter());
|
||||||
|
|
||||||
let test_entry1 = rt.store().get(test_id1).unwrap().unwrap();
|
let test_entry1 = rt.store().get(test_id1).unwrap().unwrap();
|
||||||
let test_links1 = get_entry_links(&test_entry1).unwrap();
|
let test_links1 = get_entry_links(&test_entry1).unwrap();
|
||||||
|
@ -461,8 +372,8 @@ mod tests {
|
||||||
let test_id2 = create_test_default_entry(&rt, "test2").unwrap();
|
let test_id2 = create_test_default_entry(&rt, "test2").unwrap();
|
||||||
let test_id3 = create_test_default_entry(&rt, "test3").unwrap();
|
let test_id3 = create_test_default_entry(&rt, "test3").unwrap();
|
||||||
|
|
||||||
handle_internal_linking(&rt);
|
link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter());
|
||||||
handle_internal_linking(&rt);
|
link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter());
|
||||||
|
|
||||||
let test_entry1 = rt.store().get(test_id1).unwrap().unwrap();
|
let test_entry1 = rt.store().get(test_id1).unwrap().unwrap();
|
||||||
let test_links1 = get_entry_links(&test_entry1).unwrap();
|
let test_links1 = get_entry_links(&test_entry1).unwrap();
|
||||||
|
@ -488,12 +399,12 @@ mod tests {
|
||||||
let test_id1 = create_test_default_entry(&rt, "test1").unwrap();
|
let test_id1 = create_test_default_entry(&rt, "test1").unwrap();
|
||||||
let test_id2 = create_test_default_entry(&rt, "test2").unwrap();
|
let test_id2 = create_test_default_entry(&rt, "test2").unwrap();
|
||||||
|
|
||||||
handle_internal_linking(&rt);
|
link_from_to(&rt, "test1", vec!["test2"].into_iter());
|
||||||
|
|
||||||
let rt = reset_test_runtime(vec!["internal", "remove", "test1", "test2"], rt)
|
let rt = reset_test_runtime(vec!["remove", "test1", "test2"], rt)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
handle_internal_linking(&rt);
|
remove_linking(&rt);
|
||||||
|
|
||||||
let test_entry1 = rt.store().get(test_id1).unwrap().unwrap();
|
let test_entry1 = rt.store().get(test_id1).unwrap().unwrap();
|
||||||
let test_links1 = get_entry_links(&test_entry1).unwrap();
|
let test_links1 = get_entry_links(&test_entry1).unwrap();
|
||||||
|
@ -514,12 +425,12 @@ mod tests {
|
||||||
let test_id2 = create_test_default_entry(&rt, "test2").unwrap();
|
let test_id2 = create_test_default_entry(&rt, "test2").unwrap();
|
||||||
let test_id3 = create_test_default_entry(&rt, "test3").unwrap();
|
let test_id3 = create_test_default_entry(&rt, "test3").unwrap();
|
||||||
|
|
||||||
handle_internal_linking(&rt);
|
link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter());
|
||||||
|
|
||||||
let rt = reset_test_runtime(vec!["internal", "remove", "test1", "test2", "test3"], rt)
|
let rt = reset_test_runtime(vec!["remove", "test1", "test2", "test3"], rt)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
handle_internal_linking(&rt);
|
remove_linking(&rt);
|
||||||
|
|
||||||
let test_entry1 = rt.store().get(test_id1).unwrap().unwrap();
|
let test_entry1 = rt.store().get(test_id1).unwrap().unwrap();
|
||||||
let test_links1 = get_entry_links(&test_entry1).unwrap();
|
let test_links1 = get_entry_links(&test_entry1).unwrap();
|
||||||
|
|
|
@ -17,121 +17,69 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
//
|
//
|
||||||
|
|
||||||
use clap::{Arg, ArgGroup, App, SubCommand};
|
use clap::{Arg, App, SubCommand};
|
||||||
|
|
||||||
pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
|
pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
|
||||||
app
|
app
|
||||||
.subcommand(SubCommand::with_name("internal")
|
.subcommand(SubCommand::with_name("remove")
|
||||||
.about("Add, remove and list internal links")
|
.about("Remove a link between two or more entries")
|
||||||
.version("0.1")
|
.version("0.1")
|
||||||
.subcommand(SubCommand::with_name("add")
|
.arg(Arg::with_name("from")
|
||||||
.about("Add link from one entry to another (and vice-versa)")
|
.index(1)
|
||||||
.version("0.1")
|
.takes_value(true)
|
||||||
.arg(Arg::with_name("from")
|
.required(true)
|
||||||
.index(1)
|
.multiple(false)
|
||||||
.takes_value(true)
|
.help("Remove Link from this entry")
|
||||||
.required(true)
|
.value_name("ENTRY"))
|
||||||
.multiple(false)
|
.arg(Arg::with_name("to")
|
||||||
.help("Link from this entry")
|
.index(2)
|
||||||
.value_name("ENTRY"))
|
.takes_value(true)
|
||||||
.arg(Arg::with_name("to")
|
.required(true)
|
||||||
.index(2)
|
.multiple(true)
|
||||||
.takes_value(true)
|
.help("Remove links to these entries")
|
||||||
.required(true)
|
.value_name("ENTRIES"))
|
||||||
.multiple(true)
|
)
|
||||||
.help("Link to this entries")
|
|
||||||
.value_name("ENTRIES"))
|
|
||||||
)
|
|
||||||
|
|
||||||
.subcommand(SubCommand::with_name("remove")
|
.subcommand(SubCommand::with_name("list")
|
||||||
.about("Remove a link between two or more entries")
|
.about("List links to this entry")
|
||||||
.version("0.1")
|
.version("0.1")
|
||||||
.arg(Arg::with_name("from")
|
.arg(Arg::with_name("entries")
|
||||||
.index(1)
|
.index(1)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(true)
|
.multiple(true)
|
||||||
.multiple(false)
|
.required(true)
|
||||||
.help("Remove Link from this entry")
|
.help("List these entries, seperate by comma")
|
||||||
.value_name("ENTRY"))
|
.value_name("ENTRIES"))
|
||||||
.arg(Arg::with_name("to")
|
|
||||||
.index(2)
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true)
|
|
||||||
.multiple(true)
|
|
||||||
.help("Remove links to these entries")
|
|
||||||
.value_name("ENTRIES"))
|
|
||||||
)
|
|
||||||
|
|
||||||
.subcommand(SubCommand::with_name("list")
|
.arg(Arg::with_name("list-externals-too")
|
||||||
.about("List links to this entry")
|
.long("list-external")
|
||||||
.version("0.1")
|
.takes_value(false)
|
||||||
.arg(Arg::with_name("entries")
|
.required(false)
|
||||||
.index(1)
|
.help("Also list external links (debugging helper that might be removed at some point"))
|
||||||
.takes_value(true)
|
)
|
||||||
.multiple(true)
|
|
||||||
.required(true)
|
|
||||||
.help("List these entries, seperate by comma")
|
|
||||||
.value_name("ENTRIES"))
|
|
||||||
|
|
||||||
.arg(Arg::with_name("list-externals-too")
|
.arg(Arg::with_name("check-consistency")
|
||||||
.long("list-external")
|
.long("check-consistency")
|
||||||
.takes_value(false)
|
.short("C")
|
||||||
.required(false)
|
.takes_value(false)
|
||||||
.help("If --list is provided, also list external links (debugging helper that might be removed at some point"))
|
.required(false)
|
||||||
)
|
.help("Check the link-consistency in the store (might be time-consuming)"))
|
||||||
|
|
||||||
.arg(Arg::with_name("check-consistency")
|
.arg(Arg::with_name("from")
|
||||||
.long("check-consistency")
|
.index(1)
|
||||||
.short("C")
|
.takes_value(true)
|
||||||
.takes_value(false)
|
.required(false)
|
||||||
.required(false)
|
.multiple(false)
|
||||||
.help("Check the link-consistency in the store (might be time-consuming)"))
|
.help("Link from this entry")
|
||||||
)
|
.requires("to")
|
||||||
.subcommand(SubCommand::with_name("external")
|
.value_name("ENTRY"))
|
||||||
.about("Add and remove external links")
|
|
||||||
.version("0.1")
|
|
||||||
|
|
||||||
.arg(Arg::with_name("id")
|
.arg(Arg::with_name("to")
|
||||||
.long("id")
|
.index(2)
|
||||||
.short("i")
|
.takes_value(true)
|
||||||
.takes_value(true)
|
.required(false)
|
||||||
.required(true)
|
.multiple(true)
|
||||||
.help("Modify external link of this entry")
|
.help("Link to this entries")
|
||||||
.value_name("ENTRY"))
|
.requires("from")
|
||||||
|
.value_name("ENTRIES"))
|
||||||
.arg(Arg::with_name("add")
|
|
||||||
.long("add")
|
|
||||||
.short("a")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(false)
|
|
||||||
.help("Add this URI as external link")
|
|
||||||
.value_name("URI"))
|
|
||||||
|
|
||||||
.arg(Arg::with_name("remove")
|
|
||||||
.long("remove")
|
|
||||||
.short("r")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true)
|
|
||||||
.help("Remove one external link"))
|
|
||||||
|
|
||||||
.arg(Arg::with_name("set")
|
|
||||||
.long("set")
|
|
||||||
.short("s")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(false)
|
|
||||||
.help("Set these URIs as external link (seperate by comma)")
|
|
||||||
.value_name("URIs"))
|
|
||||||
|
|
||||||
.arg(Arg::with_name("list")
|
|
||||||
.long("list")
|
|
||||||
.short("l")
|
|
||||||
.takes_value(false)
|
|
||||||
.required(false)
|
|
||||||
.help("List external links"))
|
|
||||||
|
|
||||||
.group(ArgGroup::with_name("external-link-group")
|
|
||||||
.args(&["add", "remove", "set", "list"])
|
|
||||||
.required(true))
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@ This section contains the changelog from the last release to the next release.
|
||||||
variable still possible.
|
variable still possible.
|
||||||
* `imag-contact` was added (with basic contact support so far).
|
* `imag-contact` was added (with basic contact support so far).
|
||||||
* `imag-habit` was introduced
|
* `imag-habit` was introduced
|
||||||
|
* `imag-link` commandline was redesigned to be easier but with the same
|
||||||
|
features.
|
||||||
|
|
||||||
* Minor changes
|
* Minor changes
|
||||||
* `libimagentryannotation` got a rewrite, is not based on `libimagnotes`
|
* `libimagentryannotation` got a rewrite, is not based on `libimagnotes`
|
||||||
|
|
Loading…
Reference in a new issue