commit
da4b823048
27 changed files with 301 additions and 253 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
Cargo.lock
|
||||
out
|
||||
target
|
||||
.idea
|
||||
|
|
|
@ -29,8 +29,9 @@ addons:
|
|||
- pkg-config
|
||||
|
||||
script:
|
||||
- cargo build --all --all-features --verbose -j 1
|
||||
- cargo test --all --all-features --verbose -j 1
|
||||
- |
|
||||
cargo build --all --all-features -j 1 || exit 1
|
||||
cargo test --all --all-features -j 1 || exit 1
|
||||
|
||||
notifications:
|
||||
email:
|
||||
|
|
45
README.md
45
README.md
|
@ -23,12 +23,10 @@ Our (long-term) goal is to
|
|||
|
||||
Yes, imag is a rather ambitious project as it tries to reimplement functionality
|
||||
for several "personal information management aspects". It is a hobby project,
|
||||
keep that in mind. We try to use standards like vcard and icalendar wherever
|
||||
possible.
|
||||
keep that in mind. We try to use standards like vcard, icalendar and others
|
||||
wherever possible.
|
||||
|
||||
imag consists of _modules_ (e.g. `imag-notes`, `imag-tag`, `imag-view`), where
|
||||
each module covers one PIM aspect. Have a look at
|
||||
[the documentation](./doc/) for some more words on this.
|
||||
Have a look at [the documentation](./doc/) for some more words on this.
|
||||
|
||||
|
||||
## Building/Running
|
||||
|
@ -48,6 +46,18 @@ Make sure to use a recent `cargo`, at least one with workspace support.
|
|||
Building all crates works with `cargo build --all`, building individual crates
|
||||
by `cd`ing to their directory and calling `cargo build`.
|
||||
|
||||
For building all commandline applications:
|
||||
|
||||
```bash
|
||||
find bin -maxdepth 3 -name Cargo.toml -exec cargo build --manifest-path {} \;
|
||||
```
|
||||
|
||||
For building only the core functionality
|
||||
|
||||
```bash
|
||||
find bin/core -maxdepth 3 -name Cargo.toml -exec cargo build --manifest-path {} \;
|
||||
```
|
||||
|
||||
### Running
|
||||
|
||||
After you build the module you want to play with, you can simply call the binary
|
||||
|
@ -57,48 +67,43 @@ If you installed the module, you can either call `imag-<modulename>` (if the
|
|||
install-directory is in your `$PATH`), or install the `imag` binary to call `imag
|
||||
<modulename>` (also if everything is in your `$PATH`).
|
||||
|
||||
|
||||
## Staying up-to-date
|
||||
|
||||
We have a [official website for imag](https://imag-pim.org), where I post
|
||||
[release notes](http://imag-pim.org/releases/) and monthly(ish) updates what's
|
||||
happening in the source tree ([RSS here](https://imag-pim.org/index.xml)).
|
||||
|
||||
|
||||
We also have a [mailinglist](https://imag-pim.org/mailinglist/) where I post
|
||||
updates and where discussion and questions are encouraged.
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
This is a hobby project, so sometimes things are not optimal and might go
|
||||
unrecognized and slip through. Feel free to open issues about things you notice!
|
||||
|
||||
Though, we have some documentation in [the ./doc subtree](./doc/)
|
||||
We have some documentation in [the ./doc subtree](./doc/)
|
||||
which can be compiled to PDF or a website.
|
||||
These docs are not published anywhere and are not even integrated into our CI,
|
||||
so it might be broken (though it's unlikely).
|
||||
Developer documentation is also available
|
||||
[online on github.io](https://matthiasbeyer.github.io/imag/imag_documentation/index.html)
|
||||
and [on docs.rs](https://docs.rs/releases/search?query=imag), though they might
|
||||
be a bit outdated.
|
||||
It might not be up to date, though.
|
||||
Developer documentation for the last release is available
|
||||
[on docs.rs](https://docs.rs/releases/search?query=imag).
|
||||
|
||||
|
||||
## Please contribute!
|
||||
|
||||
We are looking for contributors!
|
||||
|
||||
Feel free to open issues for asking questions, suggesting features or other
|
||||
things!
|
||||
|
||||
Also have a look at [the CONTRIBUTING.md file](./CONTRIBUTING.md)!
|
||||
|
||||
## Contact
|
||||
|
||||
Have a look at [our website](https://imag-pim.org) where you can find some
|
||||
information on how to get in touch and so on.
|
||||
## Contact
|
||||
|
||||
Feel free to join our new IRC channel at freenode: #imag
|
||||
or our [mailinglist](https://imag-pim.org/mailinglist/).
|
||||
|
||||
|
||||
## License
|
||||
|
||||
We chose to distribute this software under terms of GNU LGPLv2.1.
|
||||
|
||||
|
||||
|
|
|
@ -82,22 +82,18 @@ fn add(rt: &Runtime) {
|
|||
let entry_name = scmd
|
||||
.value_of("entry")
|
||||
.map(PathBuf::from)
|
||||
.map(|pb| pb.into_storeid().map_err_trace_exit(1).unwrap())
|
||||
.map(|pb| pb.into_storeid().map_err_trace_exit_unwrap(1))
|
||||
.unwrap(); // safed by clap
|
||||
|
||||
let _ = rt.store()
|
||||
.get(entry_name)
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.ok_or(AE::from("Entry does not exist".to_owned()))
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.annotate(rt.store(), annotation_name)
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.edit_content(&rt)
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap();
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
|
||||
info!("Ok");
|
||||
}
|
||||
|
@ -109,17 +105,14 @@ fn remove(rt: &Runtime) {
|
|||
let delete = scmd.is_present("delete-annotation");
|
||||
|
||||
let mut entry = rt.store()
|
||||
.get(PathBuf::from(entry_name).into_storeid().map_err_trace_exit(1).unwrap())
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.get(PathBuf::from(entry_name).into_storeid().map_err_trace_exit_unwrap(1))
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.ok_or(AE::from("Entry does not exist".to_owned()))
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap();
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
|
||||
let annotation = entry
|
||||
.denotate(rt.store(), annotation_name)
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap();
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
|
||||
if delete {
|
||||
debug!("Deleting annotation object");
|
||||
|
@ -130,8 +123,7 @@ fn remove(rt: &Runtime) {
|
|||
let _ = rt
|
||||
.store()
|
||||
.delete(loc)
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap();
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
} else {
|
||||
warn!("Not having annotation object, cannot delete!");
|
||||
}
|
||||
|
@ -149,17 +141,14 @@ fn list(rt: &Runtime) {
|
|||
Some(pb) => {
|
||||
let _ = rt
|
||||
.store()
|
||||
.get(pb.into_storeid().map_err_trace_exit(1).unwrap())
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.get(pb.into_storeid().map_err_trace_exit_unwrap(1))
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.ok_or(AE::from("Entry does not exist".to_owned()))
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.annotations(rt.store())
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.enumerate()
|
||||
.map(|(i, a)| list_annotation(i, a.map_err_trace_exit(1).unwrap(), with_text))
|
||||
.map(|(i, a)| list_annotation(i, a.map_err_trace_exit_unwrap(1), with_text))
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
|
||||
|
@ -168,10 +157,9 @@ fn list(rt: &Runtime) {
|
|||
let _ = rt
|
||||
.store()
|
||||
.all_annotations()
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.enumerate()
|
||||
.map(|(i, a)| list_annotation(i, a.map_err_trace_exit(1).unwrap(), with_text))
|
||||
.map(|(i, a)| list_annotation(i, a.map_err_trace_exit_unwrap(1), with_text))
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,8 +100,7 @@ fn main() {
|
|||
|
||||
let diags = rt.store()
|
||||
.entries()
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.into_get_iter(rt.store())
|
||||
.map(|e| {
|
||||
e.map_err_trace_exit_unwrap(1)
|
||||
|
|
|
@ -103,8 +103,7 @@ fn add(rt: &Runtime) {
|
|||
.get(sid)
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.map(|mut entry| {
|
||||
let _ = entry.set_coordinates(c)
|
||||
.map_err_trace_exit(1);
|
||||
let _ = entry.set_coordinates(c).map_err_trace_exit_unwrap(1);
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
error!("No such entry: {}", entry_name);
|
||||
|
|
|
@ -106,7 +106,7 @@ fn main() {
|
|||
}
|
||||
})
|
||||
.ok_or(LE::from("No commandline call".to_owned()))
|
||||
.map_err_trace_exit(1);
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
}
|
||||
|
||||
fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<Option<FileLockEntry<'a>>, StoreError> {
|
||||
|
|
|
@ -75,7 +75,7 @@ fn main() {
|
|||
let _ = rt
|
||||
.store()
|
||||
.move_by_id(sourcename, destname)
|
||||
.map_err_trace_exit(1);
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
|
||||
info!("Ok.");
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ pub fn delete(rt: &Runtime) {
|
|||
let _ = rt.store()
|
||||
.delete(path)
|
||||
.map_warn_err(|e| format!("Error: {:?}", e))
|
||||
.map_err_trace_exit(1);
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -35,7 +35,7 @@ pub fn retrieve(rt: &Runtime) {
|
|||
let id = scmd.value_of("id").unwrap();
|
||||
let path = PathBuf::from(id);
|
||||
let store = Some(rt.store().path().clone());
|
||||
let path = StoreId::new(store, path).map_err_trace_exit(1)?;
|
||||
let path = StoreId::new(store, path).map_err_trace_exit_unwrap(1);
|
||||
debug!("path = {:?}", path);
|
||||
|
||||
rt.store()
|
||||
|
|
|
@ -163,7 +163,7 @@ fn main() {
|
|||
} else {
|
||||
let _ = StdoutViewer::new(view_header, view_content)
|
||||
.view_entry(&entry)
|
||||
.map_err_trace_exit(1);
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -107,28 +107,24 @@ fn list(rt: &Runtime) {
|
|||
let _ = rt
|
||||
.store()
|
||||
.all_contacts()
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap() // safed by above call
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.into_get_iter(rt.store())
|
||||
.map(|fle| {
|
||||
let fle = fle
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.ok_or_else(|| CE::from("StoreId not found".to_owned()))
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap();
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
|
||||
fle
|
||||
.get_contact_data()
|
||||
.map(|cd| (fle, cd))
|
||||
.map(|(fle, cd)| (fle, cd.into_inner()))
|
||||
.map(|(fle, cd)| (fle, Vcard::from_component(cd)))
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
})
|
||||
.enumerate()
|
||||
.map(|(i, (fle, vcard))| {
|
||||
let hash = fle.get_path_hash().map_err_trace_exit(1).unwrap();
|
||||
let hash = fle.get_path_hash().map_err_trace_exit_unwrap(1);
|
||||
let vcard = vcard.unwrap_or_else(|e| {
|
||||
error!("Element is not a VCARD object: {:?}", e);
|
||||
exit(1)
|
||||
|
@ -137,8 +133,7 @@ fn list(rt: &Runtime) {
|
|||
let data = build_data_object_for_handlebars(i, hash, &vcard);
|
||||
|
||||
let s = list_format.render("format", &data)
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap();
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
println!("{}", s);
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
@ -157,18 +152,16 @@ fn import(rt: &Runtime) {
|
|||
let _ = rt
|
||||
.store()
|
||||
.create_from_path(&path)
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap();
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
} else if path.is_dir() {
|
||||
for entry in WalkDir::new(path).min_depth(1).into_iter() {
|
||||
let entry = entry.map_err_trace_exit(1).unwrap();
|
||||
let entry = entry.map_err_trace_exit_unwrap(1);
|
||||
if entry.file_type().is_file() {
|
||||
let pb = PathBuf::from(entry.path());
|
||||
let _ = rt
|
||||
.store()
|
||||
.create_from_path(&pb)
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap();
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
info!("Imported: {}", entry.path().to_str().unwrap_or("<non UTF-8 path>"));
|
||||
} else {
|
||||
warn!("Ignoring non-file: {}", entry.path().to_str().unwrap_or("<non UTF-8 path>"));
|
||||
|
@ -186,14 +179,11 @@ fn show(rt: &Runtime) {
|
|||
|
||||
let contact_data = rt.store()
|
||||
.get_by_hash(hash.clone())
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.ok_or(CE::from(format!("No entry for hash {}", hash)))
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.get_contact_data()
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap()
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.into_inner();
|
||||
let vcard = Vcard::from_component(contact_data)
|
||||
.unwrap_or_else(|e| {
|
||||
|
@ -204,9 +194,7 @@ fn show(rt: &Runtime) {
|
|||
let show_format = get_contact_print_format("contact.show_format", rt, &scmd);
|
||||
let data = build_data_object_for_handlebars(0, hash, &vcard);
|
||||
|
||||
let s = show_format.render("format", &data)
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap();
|
||||
let s = show_format.render("format", &data).map_err_trace_exit_unwrap(1);
|
||||
println!("{}", s);
|
||||
info!("Ok");
|
||||
}
|
||||
|
@ -226,10 +214,7 @@ fn get_contact_print_format(config_value_path: &'static str, rt: &Runtime, scmd:
|
|||
});
|
||||
|
||||
let mut hb = Handlebars::new();
|
||||
let _ = hb
|
||||
.register_template_string("format", fmt)
|
||||
.map_err_trace_exit(1)
|
||||
.unwrap();
|
||||
let _ = hb.register_template_string("format", fmt).map_err_trace_exit_unwrap(1);
|
||||
|
||||
hb.register_escape_fn(::handlebars::no_escape);
|
||||
::libimaginteraction::format::register_all_color_helpers(&mut hb);
|
||||
|
|
|
@ -25,7 +25,7 @@ pub fn build_data_object_for_handlebars<'a>(i: usize, hash: String, vcard: &Vcar
|
|||
{
|
||||
data.insert("i" , format!("{}", i));
|
||||
|
||||
/// The hash (as in libimagentryref) of the contact
|
||||
// The hash (as in libimagentryref) of the contact
|
||||
data.insert("id" , hash);
|
||||
|
||||
data.insert("ADR" , vcard.adr()
|
||||
|
|
|
@ -79,7 +79,7 @@ fn create(rt: &Runtime) {
|
|||
let _ = note
|
||||
.edit_content(rt)
|
||||
.map_warn_err_str("Editing failed")
|
||||
.map_err_trace_exit(1);
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ fn delete(rt: &Runtime) {
|
|||
let _ = rt.store()
|
||||
.delete_note(name_from_cli(rt, "delete"))
|
||||
.map_info_str("Ok")
|
||||
.map_err_trace_exit(1);
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
}
|
||||
|
||||
fn edit(rt: &Runtime) {
|
||||
|
@ -100,7 +100,7 @@ fn edit(rt: &Runtime) {
|
|||
let _ = note
|
||||
.edit_content(rt)
|
||||
.map_warn_err_str("Editing failed")
|
||||
.map_err_trace_exit(1);
|
||||
.map_err_trace_exit_unwrap(1);
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
error!("Cannot find note with name '{}'", name);
|
||||
|
@ -110,27 +110,22 @@ fn edit(rt: &Runtime) {
|
|||
fn list(rt: &Runtime) {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
rt.store()
|
||||
let _ = rt
|
||||
.store()
|
||||
.all_notes()
|
||||
.map_err_trace_exit(1)
|
||||
.map(|iter| {
|
||||
let notes = iter
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.filter_map(|noteid| rt.store().get(noteid).map_err_trace_exit_unwrap(1))
|
||||
.sorted_by(|note_a, note_b| {
|
||||
if let (Ok(a), Ok(b)) = (note_a.get_name(), note_b.get_name()) {
|
||||
.sorted_by(|note_a, note_b| if let (Ok(a), Ok(b)) = (note_a.get_name(), note_b.get_name()) {
|
||||
return a.cmp(&b)
|
||||
} else {
|
||||
return Ordering::Greater;
|
||||
}
|
||||
});
|
||||
|
||||
for note in notes.iter() {
|
||||
})
|
||||
.iter()
|
||||
.for_each(|note| {
|
||||
note.get_name()
|
||||
.map(|name| println!("{}", name))
|
||||
.map_err_trace()
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -31,3 +31,4 @@ libimagstore = { version = "0.6.0", path = "../../../lib/core/libimagstore"
|
|||
libimagrt = { version = "0.6.0", path = "../../../lib/core/libimagrt" }
|
||||
libimagerror = { version = "0.6.0", path = "../../../lib/core/libimagerror" }
|
||||
libimagtimetrack = { version = "0.6.0", path = "../../../lib/domain/libimagtimetrack" }
|
||||
libimagutil = { version = "0.6.0", path = "../../../lib/etc/libimagutil" }
|
||||
|
|
|
@ -30,6 +30,7 @@ extern crate libimagerror;
|
|||
extern crate libimagstore;
|
||||
extern crate libimagrt;
|
||||
extern crate libimagtimetrack;
|
||||
extern crate libimagutil;
|
||||
|
||||
mod cont;
|
||||
mod day;
|
||||
|
|
|
@ -29,9 +29,10 @@ use libimagrt::runtime::Runtime;
|
|||
use libimagtimetrack::timetracking::TimeTracking;
|
||||
use libimagtimetrack::tag::TimeTrackingTag;
|
||||
use libimagtimetrack::timetrackingstore::*;
|
||||
use libimagtimetrack::iter::get::GetTimeTrackIter;
|
||||
use libimagtimetrack::iter::filter::has_end_time;
|
||||
use libimagtimetrack::iter::filter::has_one_of_tags;
|
||||
use libimagutil::warn_result::*;
|
||||
use libimagutil::debug_result::*;
|
||||
|
||||
pub fn stop(rt: &Runtime) -> i32 {
|
||||
let (_, cmd) = rt.cli().subcommand();
|
||||
|
@ -74,41 +75,25 @@ pub fn stop(rt: &Runtime) -> i32 {
|
|||
.collect()
|
||||
});
|
||||
|
||||
let iter : GetTimeTrackIter = match rt.store().get_timetrackings() {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
error!("Getting timetrackings failed");
|
||||
trace_error(&e);
|
||||
return 1
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
let filter = has_end_time.not().and(has_one_of_tags(&tags));
|
||||
rt
|
||||
.store()
|
||||
.get_timetrackings()
|
||||
.map_warn_err_str("Getting timetrackings failed")
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
.trace_unwrap()
|
||||
|
||||
// Filter all timetrackings for the ones that are not yet ended.
|
||||
iter.trace_unwrap()
|
||||
.filter_map(|elem| {
|
||||
if filter.filter(&elem) {
|
||||
Some(elem)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter(|e| filter.filter(e))
|
||||
|
||||
// for each of these timetrackings, end them
|
||||
// for each result, print the backtrace (if any)
|
||||
.fold(0, |acc, mut elem| match elem.set_end_datetime(stop_time.clone()) {
|
||||
Err(e) => { // if there was an error
|
||||
trace_error(&e); // trace
|
||||
1 // set exit code to 1
|
||||
},
|
||||
Ok(_) => {
|
||||
debug!("Setting end time worked: {:?}", elem);
|
||||
|
||||
// Keep the exit code
|
||||
acc
|
||||
}
|
||||
.fold(0, |acc, mut elem| {
|
||||
elem.set_end_datetime(stop_time.clone())
|
||||
.map_dbg(|e| format!("Setting end time worked: {:?}", e))
|
||||
.map(|_| acc)
|
||||
.map_err_trace_exit_unwrap(1)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,6 @@ extern crate libimagtodo;
|
|||
use std::process::{Command, Stdio};
|
||||
use std::io::stdin;
|
||||
|
||||
use toml::Value;
|
||||
|
||||
use libimagrt::runtime::Runtime;
|
||||
use libimagrt::setup::generate_runtime_setup;
|
||||
use libimagtodo::taskstore::TaskStore;
|
||||
|
@ -81,7 +79,6 @@ fn tw_hook(rt: &Runtime) {
|
|||
}
|
||||
|
||||
fn list(rt: &Runtime) {
|
||||
use toml_query::read::TomlValueReadExt;
|
||||
use toml_query::read::TomlValueReadTypeExt;
|
||||
|
||||
let subcmd = rt.cli().subcommand_matches("list").unwrap();
|
||||
|
|
|
@ -58,7 +58,7 @@ in
|
|||
|
||||
pkgs.stdenv.mkDerivation rec {
|
||||
name = "imag-doc";
|
||||
src = ./.;
|
||||
src = /var/empty;
|
||||
version = "0.0.0";
|
||||
|
||||
buildInputs = [ env ];
|
||||
|
|
|
@ -6,30 +6,28 @@ All contributors agree to the
|
|||
[developer certificate of origin](#developer-certificate-of-origin)
|
||||
by contributing to imag.
|
||||
|
||||
If you already have something in mind, go ahead with [the prerequisites
|
||||
section](#prerequisites). If you don't know what you could do, start here.
|
||||
|
||||
## Without Github
|
||||
|
||||
Contributing without a github account is perfectly fine.
|
||||
Contributing without a github account is perfectly fine and actually encouraged
|
||||
as we try to move away from github step by step.
|
||||
Feel free to contact [us via our mailinglist](http://imag-pim.org/mailinglist/)
|
||||
and/or submit patches via mail (use `git format-patch` and
|
||||
`git send-email`, always add a cover letter to describe your submission).
|
||||
|
||||
Also ensure that each commit has
|
||||
Also ensure that each commit submitted via email has
|
||||
[a "Signed-off-by: " line](https://stackoverflow.com/questions/1962094/what-is-the-sign-off-feature-in-git-for).
|
||||
By adding that line, you agree to our
|
||||
[developer certificate of origin](#developer-certificate-of-origin).
|
||||
If you do not add the "Signed-off-by: " line,
|
||||
I reserve the right to reject your patch.
|
||||
If you do not add the "Signed-off-by: " line, I reserve the right to kindly
|
||||
reject your patch.
|
||||
|
||||
Once _I am_ okay with your patchset, I will
|
||||
submit it as PR in the github repository, so more people can review it and CI
|
||||
can test it (the mailinglist is not yet used as much as github). I might come
|
||||
back to you if something broke in CI or someone has a suggestion how to improve
|
||||
your PR. I will keep you as author of the commits.
|
||||
submit it as PR in the github repository (as long as we're using github),
|
||||
so CI can test it.
|
||||
I might come back to you if something broke in CI or someone has a suggestion
|
||||
how to improve your PR. I will keep you as author of the commits.
|
||||
|
||||
The following sections describe the way how to contribute with github.
|
||||
|
||||
## Finding an issue
|
||||
|
||||
|
@ -43,13 +41,17 @@ code... you'll always find things to improve!
|
|||
Also, if you've found bugs or outdated stuff in our documentation, feel free to
|
||||
file issues about them or even better: Write a pull request to fix them!
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* cargo and rust compiler in current version (stable)
|
||||
The prerequisites are simple: `cargo` and `rustc` in current version (stable)
|
||||
or newer (we do not use nighly features though).
|
||||
|
||||
Dependencies are listed in the
|
||||
Build dependencies for building are listed in the
|
||||
[default.nix file](http://git.imag-pim.org/imag/tree/default.nix),
|
||||
though you do not have to have `nix` installed to build imag.
|
||||
though you do not have to have the `nix` package manager installed to build
|
||||
imag.
|
||||
Everything else will be done by `cargo`.
|
||||
|
||||
Note that this software is targeted towards commandline linux users and we do
|
||||
not aim to be portable to Windows or Mac OSX (though I wouldn't mind merging
|
||||
|
@ -61,65 +63,40 @@ If you want to build the documentation (you don't have to) you'll need:
|
|||
* pandoc-citeproc
|
||||
* texlive
|
||||
* lmodern (font package)
|
||||
* make
|
||||
* (gnu) make
|
||||
|
||||
All dependencies are installable with the nix package manager by using a
|
||||
`nix-shell`, if you have the nix package manager installed on your system.
|
||||
|
||||
|
||||
## Commit guidelines
|
||||
|
||||
Please don't refer to issues or PRs from inside a commit message, if possible.
|
||||
Make sure your PR does not contain "Fixup" commits when publishing it, but feel
|
||||
free to push "Fixup" commits in the review process. We will ask you to clean
|
||||
your history before merging! If you're submitting via patch-mail, I will do the
|
||||
fixup squashing myself.
|
||||
fixup squashing myself. If it fails I will come back to you.
|
||||
|
||||
Make sure to prefix your commits with `"doc: "` if you change the document. Do
|
||||
not change document and code in one commit, always separate them.
|
||||
Make sure to prefix your commits with `"doc: "` if you change the documentation.
|
||||
Do not change document and code in one commit, always separate them.
|
||||
|
||||
If your changes are user-visible, make sure to add a note in the
|
||||
`CHANGELOG.md` file.
|
||||
If your changes are user-visible (new commandline flags, other semantics in the
|
||||
commandline, etc), make sure to add a note in the `CHANGELOG.md` file (in the
|
||||
same commit if it is a simple change).
|
||||
|
||||
We do not follow some official Rust styleguide for our codebase, but we try to
|
||||
write minimal and readable code. 100 characters per line, as few lines as
|
||||
possible, avoid noise in the codebase, ... you get it.
|
||||
|
||||
Not all of your commits have to be buildable. But your PR has to be.
|
||||
Not all of your commits have to be buildable. But your PR has to be before it
|
||||
will be merged to master.
|
||||
|
||||
## PR guidelines
|
||||
|
||||
We'd like to have one PR per module change. This means you _should_ only change
|
||||
one imag module in one commit or PR (library plus belonging binary is okay).
|
||||
As this is not always possible, we do not enforce this, though we might ask you
|
||||
to split your commits/PR into two smaller ones.
|
||||
## Feature branches
|
||||
|
||||
Use feature branches. If you could name them "<module name>/<what you do>",
|
||||
for example "libimagstore/add-debugging-calls", that would be awesome.
|
||||
|
||||
You are welcome to publish your PR as soon as there is one commit in your
|
||||
branch. This gives us the possibility to review whether your ideas go into a
|
||||
nice direction or whether there are issues with your approach so we can report
|
||||
them to you quickly. Rewriting a whole PR is not satisfactory and we'd
|
||||
like to make your contribution process enjoyable for you.
|
||||
|
||||
# Merging tools which use the imag core functionality into this repo
|
||||
|
||||
If you're writing an application or module for imag, feel free to propose
|
||||
integrating it into the imag core distribution, if it fulfills the following
|
||||
requirements:
|
||||
|
||||
1. It is written in Rust
|
||||
1. It has a commandline interface which is the main interface to the module
|
||||
OR it is a utility library for creating new kinds of functionality within the
|
||||
imag core.
|
||||
The commandline interface should be structured like the existing interfaces
|
||||
(as in commands, options and arguments).
|
||||
1. It is licensed under the terms of GNU LGPLv2.1 OR all of your contributors
|
||||
approve a commit which changes the license of your codebase to GNU LGPLv2.1
|
||||
(The word "approve" in this sentence is to be defined).
|
||||
|
||||
If your tool does not fulfill these requirements, I won't merge it into the
|
||||
imag core distribution.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
|
@ -129,10 +106,6 @@ We use the same
|
|||
Basically: Be kind, encourage others to ask questions - you are encouraged to
|
||||
ask questions as well!
|
||||
|
||||
## Contact
|
||||
|
||||
Feel free to reach out via mail/[mailinglist](http://imag-pim.org/mailinglist/)
|
||||
or [IRC](irc://irc.freenode.net/#imag).
|
||||
|
||||
## Developer Certificate of Origin
|
||||
|
||||
|
|
|
@ -35,11 +35,17 @@ This section contains the changelog from the last release to the next release.
|
|||
* Minor changes
|
||||
* Internals were refactored from `match`ing all the things into function
|
||||
chaining
|
||||
* `libimagbookmark` does not longer wrap types from the store.
|
||||
* The `toml-query` dependency was updated to 0.5.0
|
||||
* `imag-timetrack list` lists with a table now
|
||||
* `imag-timetrack stop` now stops all runnings tags if none are specified
|
||||
* `imag-timetrack stop` now stops all running tags if none are specified
|
||||
* The `toml-query` dependency was updated to 0.6.0
|
||||
* `ResultExt::map_err_trace_exit()` was removed in favour of
|
||||
`ResultExt::map_err_trace_exit_unwrap()`.
|
||||
* Bugfixes
|
||||
* `libimagbookmark` contained a type which wrapped a `FileLockEntry` from
|
||||
`libimagstore`. This was considered a bug and was fixed.
|
||||
* We depended on a crate which was licensed as GPLv2, which would yield imag
|
||||
GPL as well. The dependency was removed.
|
||||
|
||||
## 0.5.0
|
||||
|
||||
|
|
|
@ -118,7 +118,6 @@ pub trait MapErrTrace {
|
|||
|
||||
fn map_err_trace(self) -> Self;
|
||||
fn map_err_dbg_trace(self) -> Self;
|
||||
fn map_err_trace_exit(self, code: i32) -> Self;
|
||||
fn map_err_trace_exit_unwrap(self, code: i32) -> Self::Output;
|
||||
fn map_err_trace_maxdepth(self, max: u64) -> Self;
|
||||
}
|
||||
|
@ -140,16 +139,9 @@ impl<U, E: Error> MapErrTrace for Result<U, E> {
|
|||
self.map_err(|e| { trace_error_dbg(&e); e })
|
||||
}
|
||||
|
||||
/// Simply call `trace_error_exit(code)` on the Err (if there is one).
|
||||
///
|
||||
/// This does not return if there is an Err(e).
|
||||
fn map_err_trace_exit(self, code: i32) -> Self {
|
||||
self.map_err(|e| { trace_error_exit(&e, code) })
|
||||
}
|
||||
|
||||
/// Helper for calling map_err_trace_exit(n).unwrap() in one call
|
||||
/// Trace the error and exit or unwrap the Ok(_).
|
||||
fn map_err_trace_exit_unwrap(self, code: i32) -> Self::Output {
|
||||
self.map_err_trace_exit(code).unwrap()
|
||||
self.map_err(|e| { trace_error_exit(&e, code) }).unwrap()
|
||||
}
|
||||
|
||||
/// Simply call `trace_error_maxdepth(max)` on the Err (if there is one) and return the error.
|
||||
|
|
|
@ -455,9 +455,17 @@ impl Store {
|
|||
Walk::new(self.path().clone(), mod_name)
|
||||
}
|
||||
|
||||
/// Return the `FileLockEntry` and write to disk
|
||||
/// Write (update) the `FileLockEntry` to disk
|
||||
///
|
||||
/// See `Store::_update()`.
|
||||
/// # Return value
|
||||
///
|
||||
/// On success: Entry
|
||||
///
|
||||
/// On error:
|
||||
/// - UpdateCallError(LockPoisoned()) if the internal write lock cannot be aquierd.
|
||||
/// - IdNotFound() if the entry was not found in the stor
|
||||
/// - Errors Entry::verify() might return
|
||||
/// - Errors StoreEntry::write_entry() might return
|
||||
///
|
||||
pub fn update<'a>(&'a self, entry: &mut FileLockEntry<'a>) -> Result<()> {
|
||||
debug!("Updating FileLockEntry at '{}'", entry.get_location());
|
||||
|
@ -471,16 +479,6 @@ impl Store {
|
|||
/// This method assumes that entry is dropped _right after_ the call, hence
|
||||
/// it is not public.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// On success: Entry
|
||||
///
|
||||
/// On error:
|
||||
/// - UpdateCallError(LockPoisoned()) if the internal write lock cannot be aquierd.
|
||||
/// - IdNotFound() if the entry was not found in the stor
|
||||
/// - Errors Entry::verify() might return
|
||||
/// - Errors StoreEntry::write_entry() might return
|
||||
///
|
||||
fn _update<'a>(&'a self, entry: &mut FileLockEntry<'a>, modify_presence: bool) -> Result<()> {
|
||||
let mut hsmap = self.entries.write().map_err(|_| SE::from_kind(SEK::LockPoisoned))?;
|
||||
|
||||
|
|
|
@ -266,7 +266,7 @@ impl<'a> Iterator for CategoryNameIter<'a> {
|
|||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// TODO: Optimize me with lazy_static
|
||||
let query = String::from(CATEGORY_REGISTER_NAME_FIELD_PATH);
|
||||
let query = CATEGORY_REGISTER_NAME_FIELD_PATH;
|
||||
|
||||
self.1
|
||||
.next()
|
||||
|
@ -275,7 +275,7 @@ impl<'a> Iterator for CategoryNameIter<'a> {
|
|||
.get(sid)?
|
||||
.ok_or_else(|| CE::from_kind(CEK::StoreReadError))?
|
||||
.get_header()
|
||||
.read_string(&query)
|
||||
.read_string(query)
|
||||
.chain_err(|| CEK::HeaderReadError)?
|
||||
.map(Category::from)
|
||||
.ok_or_else(|| CE::from_kind(CEK::StoreReadError))
|
||||
|
|
|
@ -634,12 +634,11 @@ pub mod store_check {
|
|||
//
|
||||
// The lambda returns an error if something fails
|
||||
let aggregate_link_network = |store: &Store| -> Result<HashMap<StoreId, Linking>> {
|
||||
let iter = store
|
||||
store
|
||||
.entries()?
|
||||
.into_get_iter(store);
|
||||
|
||||
let mut map = HashMap::new();
|
||||
for element in iter {
|
||||
.into_get_iter(store)
|
||||
.fold(Ok(HashMap::new()), |map, element| {
|
||||
map.and_then(|mut map| {
|
||||
debug!("Checking element = {:?}", element);
|
||||
let entry = element?.ok_or_else(|| {
|
||||
LE::from(String::from("TODO: Not yet handled"))
|
||||
|
@ -660,9 +659,9 @@ pub mod store_check {
|
|||
}
|
||||
|
||||
map.insert(entry.get_location().clone(), linking);
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
// Helper to check whethre all StoreIds in the network actually exists
|
||||
|
|
10
scripts/commit-template
Normal file
10
scripts/commit-template
Normal file
|
@ -0,0 +1,10 @@
|
|||
One line description of your change
|
||||
|
||||
Motivation:
|
||||
|
||||
Modifications:
|
||||
|
||||
Result:
|
||||
|
||||
# To use this template as commit template,
|
||||
# call `git config commit.template <path of this file>
|
113
scripts/new-crate.sh
Normal file
113
scripts/new-crate.sh
Normal file
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Helper script to create a new crate in the imag workspace
|
||||
|
||||
# 1. Creates a new crate
|
||||
# 2. Adds the required crate meta information
|
||||
# 3. Sets the version of the crate to the same version as libimagstore
|
||||
# 4. Adds the crate to the top-level workspace
|
||||
|
||||
if [[ "$1" == "-h" || "$1" == "--help" ]];
|
||||
then
|
||||
echo "$0 [bin|lib] ./path/to/new/crate"
|
||||
echo
|
||||
echo "Execute _only_ from the top level of the repository"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
crate_type="$1"
|
||||
crate_location="$2"
|
||||
|
||||
exit_if_empty() {
|
||||
[[ -z "$1" ]] && { echo "$2 not passed"; exit 1; }
|
||||
}
|
||||
|
||||
exit_if_empty "$crate_type" "crate type"
|
||||
exit_if_empty "$crate_location" "crate location"
|
||||
|
||||
exists_cmd() {
|
||||
command -v $1 || { echo "No $1 found"; exit 1; }
|
||||
}
|
||||
|
||||
exists_cmd "git"
|
||||
exists_cmd "cargo"
|
||||
|
||||
{ cat ./Cargo.toml 2>/dev/null | head -n 1 | grep -q "[workspace]"; } || {
|
||||
echo "Not in root of repository as it seems. Exiting";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
[[ "$crate_type" == "lib" || "$crate_type" == "bin" ]] || {
|
||||
echo "Invalid crate type, use 'lib' or 'bin'";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
if [[ -e "$crate_location" ]]; then
|
||||
echo "Crate exists: $crate_location"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
IFS=/ read -ra crate_name_parts <<< "$crate_location"
|
||||
crate_name="${crate_name_parts[-1]}"
|
||||
|
||||
if [[ "$crate_type" == "lib" ]];
|
||||
then
|
||||
crate_description="Library for the imag core distribution"
|
||||
else if [[ "$crate_type" == "bin" ]]; then
|
||||
crate_description="Part of the imag core distribution: $crate_name command"
|
||||
fi
|
||||
|
||||
git_name="$(git config user.name)"
|
||||
git_email="$(git config user.email)"
|
||||
|
||||
store="lib/core/libimagstore/Cargo.toml"
|
||||
crate_version=$(grep -m 1 version $store | cut -d '"' -f 2)
|
||||
|
||||
echo "Crate type: $crate_type"
|
||||
echo "Crate location: $crate_location"
|
||||
echo "Crate name: $crate_name"
|
||||
echo "Crate version: $crate_version"
|
||||
echo "Crate description: $crate_description"
|
||||
echo "Crate author: $git_name <$git_email>"
|
||||
|
||||
echo "Not doing anything as this script is not ready yet."
|
||||
echo "Exiting now"
|
||||
exit 1
|
||||
|
||||
pushd "$(dirname $crate_location)"
|
||||
crate new --${crate_type} $crate_name
|
||||
|
||||
cat <<EOS > ./$crate_name/Cargo.toml
|
||||
[package]
|
||||
name = "$crate_name"
|
||||
version = "$crate_version"
|
||||
authors = ["$git_name <$git_email>"]
|
||||
|
||||
description = "$crate_description"
|
||||
|
||||
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]
|
||||
|
||||
EOS
|
||||
|
||||
echo "Cargo.toml written. Please make sure that the README has the right path!"
|
||||
popd
|
||||
|
||||
git add ${crate_location}/*
|
||||
|
||||
sed -i "$ s/]/ \"${crate_location}\",\n]/" Cargo.toml
|
||||
echo "Top-level Cargo.toml modified. Please sort crate list manually!"
|
||||
|
Loading…
Reference in a new issue