libimagannotatecmd/
lib.rs1#![forbid(unsafe_code)]
21
22#![deny(
23 non_camel_case_types,
24 non_snake_case,
25 path_statements,
26 trivial_numeric_casts,
27 unstable_features,
28 unused_allocation,
29 unused_import_braces,
30 unused_imports,
31 unused_must_use,
32 unused_mut,
33 unused_qualifications,
34 while_true,
35)]
36
37extern crate clap;
38#[macro_use]
39extern crate log;
40#[macro_use]
41extern crate failure;
42extern crate toml_query;
43extern crate resiter;
44
45extern crate libimagentryannotation;
46extern crate libimagentryedit;
47extern crate libimagerror;
48extern crate libimagrt;
49extern crate libimagstore;
50extern crate libimagutil;
51extern crate libimagentrylink;
52
53use std::io::Write;
54
55use failure::Error;
56use failure::Fallible as Result;
57use failure::ResultExt;
58use failure::err_msg;
59use resiter::IterInnerOkOrElse;
60use resiter::AndThen;
61use resiter::Map;
62use toml_query::read::TomlValueReadTypeExt;
63use clap::App;
64
65use libimagentryannotation::annotateable::*;
66use libimagentryannotation::annotation_fetcher::*;
67use libimagentryedit::edit::*;
68use libimagerror::errors::ErrorMsg as EM;
69use libimagrt::runtime::Runtime;
70use libimagrt::application::ImagApplication;
71use libimagstore::store::FileLockEntry;
72use libimagstore::iter::get::StoreIdGetIteratorExtension;
73use libimagentrylink::linkable::Linkable;
74use libimagrt::iter::ReportTouchedResultEntry;
75
76mod ui;
77
78pub enum ImagAnnotate {}
79impl ImagApplication for ImagAnnotate {
80 fn run(rt: Runtime) -> Result<()> {
81 match rt.cli().subcommand_name().ok_or_else(|| err_msg("No command called"))? {
82 "add" => add(&rt),
83 "remove" => remove(&rt),
84 "list" => list(&rt),
85 other => {
86 debug!("Unknown command");
87 if rt.handle_unknown_subcommand("imag-annotation", other, rt.cli())?.success() {
88 Ok(())
89 } else {
90 Err(err_msg("Failed to handle unknown subcommand"))
91 }
92 },
93 }
94 }
95
96 fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
97 ui::build_ui(app)
98 }
99
100 fn name() -> &'static str {
101 env!("CARGO_PKG_NAME")
102 }
103
104 fn description() -> &'static str {
105 "Add annotations to entries"
106 }
107
108 fn version() -> &'static str {
109 env!("CARGO_PKG_VERSION")
110 }
111}
112
113fn add(rt: &Runtime) -> Result<()> {
114 let scmd = rt.cli().subcommand_matches("add").unwrap(); let mut ids = rt
116 .ids::<crate::ui::PathProvider>()
117 .context("No StoreId supplied")?
118 .ok_or_else(|| err_msg("No ids supplied"))?
119 .into_iter();
120
121 if let Some(first) = ids.next() {
122 let mut annotation = rt.store()
123 .get(first.clone())?
124 .ok_or_else(|| EM::EntryNotFound(first.local_display_string()))?
125 .annotate(rt.store())?;
126
127 annotation.edit_content(&rt)?;
128
129 rt.report_touched(&first)?; ids.map(Ok).into_get_iter(rt.store())
131 .map_inner_ok_or_else(|| err_msg("Did not find one entry"))
132 .and_then_ok(|mut entry| entry.add_link(&mut annotation).map(|_| entry))
133 .map_report_touched(&rt)
134 .map_ok(|_| ())
135 .collect::<Result<Vec<_>>>()?;
136
137 if !scmd.is_present("dont-print-name") {
138 if let Some(annotation_id) = annotation
139 .get_header()
140 .read_string("annotation.name")?
141 {
142 writeln!(rt.stdout(), "Name of the annotation: {}", annotation_id)?;
143 } else {
144 Err(format_err!("Unnamed annotation: {:?}", annotation.get_location()))
145 .context("This is most likely a BUG, please report!")?;
146 }
147 }
148
149 rt.report_touched(annotation.get_location())?;
150 } else {
151 debug!("No entries to annotate");
152 }
153
154 Ok(())
155}
156
157fn remove(rt: &Runtime) -> Result<()> {
158 let scmd = rt.cli().subcommand_matches("remove").unwrap(); let annotation_name = scmd.value_of("annotation_name").unwrap(); let delete = scmd.is_present("delete-annotation");
161
162 rt.ids::<crate::ui::PathProvider>()
163 .context("No ids supplied")?
164 .ok_or_else(|| err_msg("No ids supplied"))?
165 .into_iter()
166 .map(|id| {
167 let mut entry = rt.store()
168 .get(id.clone())?
169 .ok_or_else(|| EM::EntryNotFound(id.local_display_string()))?;
170
171 let annotation = entry.denotate(rt.store(), annotation_name)?;
172
173 if delete {
174 debug!("Deleting annotation object");
175 if let Some(an) = annotation {
176 let loc = an.get_location().clone();
177 drop(an);
178
179 rt.store().delete(loc)?;
180 } else {
181 warn!("Not having annotation object, cannot delete!");
182 }
183 } else {
184 debug!("Not deleting annotation object");
185 }
186
187 rt.report_touched(entry.get_location()).map_err(Error::from)
188 })
189 .collect()
190}
191
192fn list(rt: &Runtime) -> Result<()> {
193 let scmd = rt.cli().subcommand_matches("list").unwrap(); let with_text = scmd.is_present("list-with-text");
195 let ids = rt
196 .ids::<crate::ui::PathProvider>()
197 .context("No ids supplied")?
198 .ok_or_else(|| err_msg("No ids supplied"))?;
199
200 if ids.is_empty() {
201 ids.into_iter()
202 .map(|id| -> Result<_> {
203 let lds = id.local_display_string();
204 Ok(rt.store()
205 .get(id)?
206 .ok_or_else(|| EM::EntryNotFound(lds))?
207 .annotations()?
208 .into_get_iter(rt.store())
209 .map(|el| el.and_then(|o| o.ok_or_else(|| format_err!("Cannot find entry"))))
210 .enumerate()
211 .map(|(i, entry)| entry.and_then(|e| list_annotation(&rt, i, &e, with_text).map(|_| e)))
212 .map_report_touched(&rt)
213 .map_ok(|_| ())
214 .collect())
215 })
216 .flatten()
217 .collect()
218 } else { rt.store()
221 .all_annotations()?
222 .into_get_iter()
223 .map(|el| el.and_then(|opt| opt.ok_or_else(|| format_err!("Cannot find entry"))))
224 .enumerate()
225 .map(|(i, entry)| entry.and_then(|e| list_annotation(&rt, i, &e, with_text).map(|_| e)))
226 .map_report_touched(&rt)
227 .map_ok(|_| ())
228 .collect()
229 }
230}
231
232fn list_annotation<'a>(rt: &Runtime, i: usize, a: &FileLockEntry<'a>, with_text: bool) -> Result<()> {
233 if with_text {
234 writeln!(rt.stdout(),
235 "--- {i: >5} | {id}\n{text}\n\n",
236 i = i,
237 id = a.get_location(),
238 text = a.get_content())
239 } else {
240 writeln!(rt.stdout(), "{: >5} | {}", i, a.get_location())
241 }.map_err(Error::from)
242}
243