#![forbid(unsafe_code)]
extern crate clap;
extern crate regex;
extern crate filters;
#[macro_use] extern crate log;
#[macro_use] extern crate failure;
extern crate resiter;
extern crate libimagrt;
extern crate libimagerror;
extern crate libimagstore;
extern crate libimagwiki;
extern crate libimagentryedit;
extern crate libimagentrylink;
extern crate libimagutil;
use std::io::Write;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::Error;
use failure::err_msg;
use clap::App;
use resiter::AndThen;
use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagentryedit::edit::{Edit, EditHeader};
use libimagwiki::store::WikiStore;
use libimagwiki::entry::WikiEntry;
mod ui;
pub enum ImagWiki {}
impl ImagApplication for ImagWiki {
fn run(rt: Runtime) -> Result<()> {
let wiki_name = rt.cli().value_of("wikiname").unwrap_or("default");
trace!("wiki_name = {}", wiki_name);
trace!("calling = {:?}", rt.cli().subcommand_name());
match rt.cli().subcommand_name().ok_or_else(|| err_msg("No subcommand called"))? {
"list" => list(&rt, wiki_name),
"idof" => idof(&rt, wiki_name),
"create" => create(&rt, wiki_name),
"create-wiki" => create_wiki(&rt),
"show" => show(&rt, wiki_name),
"delete" => delete(&rt, wiki_name),
other => {
debug!("Unknown command");
if rt.handle_unknown_subcommand("imag-wiki", other, rt.cli())?.success() {
Ok(())
} else {
Err(err_msg("Failed to handle unknown subcommand"))
}
}
} }
fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
ui::build_ui(app)
}
fn name() -> &'static str {
env!("CARGO_PKG_NAME")
}
fn description() -> &'static str {
"Personal wiki"
}
fn version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
}
fn list(rt: &Runtime, wiki_name: &str) -> Result<()> {
let scmd = rt.cli().subcommand_matches("list").unwrap(); let prefix = if scmd.is_present("list-full") {
format!("{}/", rt.store().path().display())
} else {
String::from("")
};
let out = rt.stdout();
let mut outlock = out.lock();
rt.store()
.get_wiki(wiki_name)?
.ok_or_else(|| format_err!("No wiki '{}' found", wiki_name))?
.all_ids()?
.and_then_ok(|id| writeln!(outlock, "{}{}", prefix, id).map_err(Error::from))
.collect::<Result<Vec<_>>>()
.map(|_| ())
}
fn idof(rt: &Runtime, wiki_name: &str) -> Result<()> {
let scmd = rt.cli().subcommand_matches("idof").unwrap();
let entryname = scmd
.value_of("idof-name")
.map(String::from)
.unwrap();
let out = rt.stdout();
let mut lock = out.lock();
rt.store()
.get_wiki(wiki_name)?
.ok_or_else(|| format_err!("No wiki '{}' found", wiki_name))?
.get_entry(&entryname)?
.ok_or_else(|| format_err!("Entry '{}' in wiki '{}' not found!", entryname, wiki_name))
.and_then(|entry| {
let id = entry.get_location().clone();
let prefix = if scmd.is_present("idof-full") {
format!("{}/", rt.store().path().display())
} else {
String::from("")
};
writeln!(lock, "{}{}", prefix, id).map_err(Error::from)
})
}
fn create(rt: &Runtime, wiki_name: &str) -> Result<()> {
let scmd = rt.cli().subcommand_matches("create").unwrap(); let name = String::from(scmd.value_of("create-name").unwrap());
let wiki = rt
.store()
.get_wiki(&wiki_name)?
.ok_or_else(|| format_err!("No wiki '{}' found", wiki_name))?;
let mut entry = wiki.create_entry(name)?;
if !scmd.is_present("create-noedit") {
if scmd.is_present("create-editheader") {
entry.edit_header_and_content(rt)?;
} else {
entry.edit_content(rt)?;
}
}
if let Err(e) = entry
.autolink(rt.store())
.context("Linking has failed. Trying to safe the entry now. Please investigate by hand if this succeeds.")
{
rt.store().update(&mut entry).context("Safed entry")?;
return Err(e).map_err(Error::from)
}
let id = entry.get_location();
if scmd.is_present("create-printid") {
let out = rt.stdout();
let mut lock = out.lock();
writeln!(lock, "{}", id)?;
}
rt.report_touched(&id).map_err(Error::from)
}
fn create_wiki(rt: &Runtime) -> Result<()> {
let scmd = rt.cli().subcommand_matches("create-wiki").unwrap(); let wiki_name = String::from(scmd.value_of("create-wiki-name").unwrap()); let (_, index) = rt.store().create_wiki(&wiki_name)?;
rt.report_touched(index.get_location()).map_err(Error::from)
}
fn show(rt: &Runtime, wiki_name: &str) -> Result<()> {
use filters::filter::Filter;
let scmd = rt.cli().subcommand_matches("show").unwrap();
struct NameFilter(Option<Vec<String>>);
impl Filter<String> for NameFilter {
fn filter(&self, e: &String) -> bool {
match self.0 {
Some(ref v) => v.contains(e),
None => false,
}
}
}
let namefilter = NameFilter(scmd
.values_of("show-name")
.map(|v| v.map(String::from).collect::<Vec<String>>()));
let wiki = rt
.store()
.get_wiki(&wiki_name)?
.ok_or_else(|| format_err!("No wiki '{}' found", wiki_name))?;
let out = rt.stdout();
let mut outlock = out.lock();
scmd.values_of("show-name")
.unwrap() .map(String::from)
.filter(|e| namefilter.filter(e))
.map(|name| {
let entry = wiki
.get_entry(&name)?
.ok_or_else(|| format_err!("No wiki entry '{}' found in wiki '{}'", name, wiki_name))?;
writeln!(outlock, "{}", entry.get_location())?;
writeln!(outlock, "{}", entry.get_content())?;
rt.report_touched(entry.get_location()).map_err(Error::from)
})
.collect::<Result<Vec<_>>>()
.map(|_| ())
}
fn delete(rt: &Runtime, wiki_name: &str) -> Result<()> {
use libimagentrylink::linkable::Linkable;
let scmd = rt.cli().subcommand_matches("delete").unwrap(); let name = String::from(scmd.value_of("delete-name").unwrap()); let unlink = !scmd.is_present("delete-no-remove-linkings");
let wiki = rt
.store()
.get_wiki(&wiki_name)?
.ok_or_else(|| format_err!("No wiki '{}' found", wiki_name))?;
if unlink {
wiki.get_entry(&name)?
.ok_or_else(|| format_err!("No wiki entry '{}' in '{}' found", name, wiki_name))?
.unlink(rt.store())?;
}
wiki.delete_entry(&name)
}