Merge branch 'minor'

This merges some important fixes:

* libimagstore: Remove calls to filesystem-accessing functions but use
  abstractions instead.
* Make Debug for FileLockEntry more verbose
* Fix In-Memory test backend bug where the backend did not remove the
  old entry on "move"

as well as some nice formatting stuff and refactorings to simplify code
and similar improvements.

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
This commit is contained in:
Matthias Beyer 2018-11-06 20:25:15 +01:00
commit 7fd3350146
13 changed files with 166 additions and 198 deletions

View File

@ -119,14 +119,11 @@ fn list(rt: &Runtime) {
.all_contacts() .all_contacts()
.map_err_trace_exit_unwrap(1) .map_err_trace_exit_unwrap(1)
.into_get_iter(rt.store()) .into_get_iter(rt.store())
.map(|fle| { .trace_unwrap_exit(1)
let fle = fle .map(|fle| fle.ok_or_else(|| Error::from(err_msg("StoreId not found".to_owned()))))
.map_err_trace_exit_unwrap(1) .trace_unwrap_exit(1)
.ok_or_else(|| Error::from(err_msg("StoreId not found".to_owned()))) .map(|e| e.deser())
.map_err_trace_exit_unwrap(1); .trace_unwrap_exit(1)
fle.deser().map_err_trace_exit_unwrap(1)
})
.enumerate(); .enumerate();
if scmd.is_present("json") { if scmd.is_present("json") {
@ -140,21 +137,19 @@ fn list(rt: &Runtime) {
} }
} }
} else { } else {
iterator let rendered = iterator
.map(|(i, deservcard)| { .map(|(i, dvcard)| build_data_object_for_handlebars(i, &dvcard))
let data = build_data_object_for_handlebars(i, &deservcard); .map(|data| list_format.render("format", &data).map_err(Error::from))
.trace_unwrap_exit(1)
.collect::<Vec<String>>();
// collect, so that we can have rendered all the things and printing is faster.
list_format.render("format", &data) let output = rt.stdout();
.map_err(Error::from) let mut output = output.lock();
.map_err_trace_exit_unwrap(1)
})
// collect, so that we can have rendered all the things and printing is faster. rendered.into_iter().for_each(|s| {
.collect::<Vec<String>>() writeln!(output, "{}", s).to_exit_code().unwrap_or_exit()
.into_iter() });
.for_each(|s| {
writeln!(rt.stdout(), "{}", s).to_exit_code().unwrap_or_exit()
});
} }
} }

View File

@ -19,7 +19,7 @@
use std::process::exit; use std::process::exit;
use chrono::naive::NaiveDateTime; use chrono::naive::NaiveDateTime as NDT;
use libimagdiary::diaryid::DiaryId; use libimagdiary::diaryid::DiaryId;
use libimagrt::runtime::Runtime; use libimagrt::runtime::Runtime;
@ -44,17 +44,10 @@ pub fn delete(rt: &Runtime) {
.value_of("datetime") .value_of("datetime")
.map(|dt| { debug!("DateTime = {:?}", dt); dt }) .map(|dt| { debug!("DateTime = {:?}", dt); dt })
.and_then(DateTime::parse) .and_then(DateTime::parse)
.map(|dt| dt.into()) .map(Into::into)
.ok_or_else(|| { .ok_or_else(|| warn_exit("Not deleting entries: missing date/time specification", 1))
warn!("Not deleting entries, because missing date/time specification"); .and_then(|dt: NDT| DiaryId::from_datetime(diaryname.clone(), dt).into_storeid())
exit(1); .and_then(|id| rt.store().retrieve(id))
})
.and_then(|dt: NaiveDateTime| {
DiaryId::from_datetime(diaryname.clone(), dt)
.into_storeid()
.map(|id| rt.store().retrieve(id))
.map_err_trace_exit_unwrap(1)
})
.map_err_trace_exit_unwrap(1) .map_err_trace_exit_unwrap(1)
.get_location() .get_location()
.clone(); .clone();

View File

@ -51,10 +51,9 @@ pub fn list(rt: &Runtime) {
[id.year() as u32, id.month(), id.day(), id.hour(), id.minute(), id.second()] [id.year() as u32, id.month(), id.day(), id.hour(), id.minute(), id.second()]
}); });
for id in ids.into_iter().map(|id| id.into_storeid().map_err_trace_exit_unwrap(1)) { ids.into_iter()
writeln!(rt.stdout(), "{}", id) .map(IntoStoreId::into_storeid)
.to_exit_code() .trace_unwrap_exit(1)
.unwrap_or_exit(); .for_each(|id| writeln!(rt.stdout(), "{}", id).to_exit_code().unwrap_or_exit());
}
} }

View File

@ -69,6 +69,7 @@ use libimagstore::store::FileLockEntry;
use libimagstore::store::Store; use libimagstore::store::Store;
use libimagstore::storeid::StoreId; use libimagstore::storeid::StoreId;
use libimaginteraction::ask::ask_bool; use libimaginteraction::ask::ask_bool;
use libimagutil::debug_result::DebugResult;
mod ui; mod ui;
@ -115,20 +116,16 @@ fn create(rt: &Runtime) {
let date = scmd.value_of("create-date").unwrap(); // safe by clap let date = scmd.value_of("create-date").unwrap(); // safe by clap
let parsedate = |d, pname| match kairos_parse(d).map_err_trace_exit_unwrap(1) { let parsedate = |d, pname| match kairos_parse(d).map_err_trace_exit_unwrap(1) {
Parsed::TimeType(tt) => match tt.calculate() { Parsed::TimeType(tt) => tt.calculate()
Ok(tt) => match tt.get_moment() { .map_dbg(|y| format!("TimeType yielded: '{:?}'", y))
Some(mom) => mom.date(), .map_err_trace_exit_unwrap(1)
None => { .get_moment()
debug!("TimeType yielded: '{:?}'", tt); .ok_or_else(|| {
error!("Error: '{}' parameter does not yield a point in time", pname); error!("Error: '{}' parameter does not yield a point in time", pname);
exit(1); exit(1)
}, })
}, .unwrap() // safe by above
Err(e) => { .date(),
error!("Error: '{:?}'", e);
exit(1);
}
},
_ => { _ => {
error!("Error: '{}' parameter does not yield a point in time", pname); error!("Error: '{}' parameter does not yield a point in time", pname);
exit(1); exit(1);

View File

@ -68,13 +68,9 @@ impl Debug for OutputProxy {
impl OutputProxy { impl OutputProxy {
pub fn lock(&self) -> LockedOutputProxy { pub fn lock(&self) -> LockedOutputProxy {
match *self { match *self {
OutputProxy::Out(ref r) => { OutputProxy::Out(ref r) => LockedOutputProxy::Out(r.lock()),
LockedOutputProxy::Out(r.lock()) OutputProxy::Err(ref r) => LockedOutputProxy::Err(r.lock()),
}, OutputProxy::Sink => LockedOutputProxy::Sink,
OutputProxy::Err(ref r) => {
LockedOutputProxy::Err(r.lock())
},
OutputProxy::Sink => LockedOutputProxy::Sink,
} }
} }
} }

View File

@ -88,34 +88,12 @@ impl ImagLogger {
::libimaginteraction::format::register_all_format_helpers(&mut handlebars); ::libimaginteraction::format::register_all_format_helpers(&mut handlebars);
{ {
let fmt = aggregate_global_format_trace(config)?; use self::log_lvl_aggregate::*;
handlebars.register_template_string("TRACE", fmt) let _ = aggregate::<Trace>(&mut handlebars, config, "TRACE")?;
.map_err(Error::from) let _ = aggregate::<Debug>(&mut handlebars, config, "DEBUG")?;
.context(err_msg("Handlebars template error"))?; // name must be uppercase let _ = aggregate::<Info>(&mut handlebars, config, "INFO")?;
} let _ = aggregate::<Warn>(&mut handlebars, config, "WARN")?;
{ let _ = aggregate::<Error>(&mut handlebars, config, "ERROR")?;
let fmt = aggregate_global_format_debug(config)?;
handlebars.register_template_string("DEBUG", fmt)
.map_err(Error::from)
.context(err_msg("Handlebars template error"))?; // name must be uppercase
}
{
let fmt = aggregate_global_format_info(config)?;
handlebars.register_template_string("INFO", fmt)
.map_err(Error::from)
.context(err_msg("Handlebars template error"))?; // name must be uppercase
}
{
let fmt = aggregate_global_format_warn(config)?;
handlebars.register_template_string("WARN", fmt)
.map_err(Error::from)
.context(err_msg("Handlebars template error"))?; // name must be uppercase
}
{
let fmt = aggregate_global_format_error(config)?;
handlebars.register_template_string("ERROR", fmt)
.map_err(Error::from)
.context(err_msg("Handlebars template error"))?; // name must be uppercase
} }
Ok(ImagLogger { Ok(ImagLogger {
@ -289,9 +267,8 @@ fn translate_destinations(raw: &Vec<Value>) -> Result<Vec<LogDestination>> {
acc.and_then(|mut v| { acc.and_then(|mut v| {
let dest = val.as_str() let dest = val.as_str()
.ok_or_else(|| { .ok_or_else(|| {
let path = "imag.logging.modules.<mod>.destinations"; let msg = "Type error at 'imag.logging.modules.<mod>.destinations', expected Array<String>";
let ty = "Array<String>"; Error::from(err_msg(msg))
Error::from(format_err!("Type error at {}, expected {}", path, ty))
}) })
.and_then(translate_destination)?; .and_then(translate_destination)?;
v.push(dest); v.push(dest);
@ -312,9 +289,8 @@ fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Value>)
.ok_or_else(|| err_msg("Global log destination config missing"))? .ok_or_else(|| err_msg("Global log destination config missing"))?
.as_array() .as_array()
.ok_or_else(|| { .ok_or_else(|| {
let path = "imag.logging.destinations"; let msg = "Type error at 'imag.logging.destinations', expected 'Array'";
let ty = "Array"; Error::from(err_msg(msg))
Error::from(format_err!("Type error at {}, expected {}", path, ty))
}) })
.and_then(translate_destinations), .and_then(translate_destinations),
None => { None => {
@ -335,54 +311,55 @@ fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Value>)
} }
} }
macro_rules! aggregate_global_format { mod log_lvl_aggregate {
($read_str:expr, $error_msg_if_missing:expr, $config:expr) => { use failure::Fallible as Result;
try!($config.ok_or_else(|| Error::from(err_msg($error_msg_if_missing)))) use failure::Error as E;
.read_string($read_str) use failure::ResultExt;
.map_err(Error::from) use failure::err_msg;
.context(EM::TomlQueryError)? use toml::Value;
.ok_or_else(|| Error::from(err_msg($error_msg_if_missing))) use toml_query::read::TomlValueReadTypeExt;
}; use handlebars::Handlebars;
}
fn aggregate_global_format_trace(config: Option<&Value>) use libimagerror::errors::ErrorMsg as EM;
-> Result<String>
{
aggregate_global_format!("imag.logging.format.trace",
"Config missing: Logging format: Trace",
config)
}
fn aggregate_global_format_debug(config: Option<&Value>) macro_rules! aggregate_global_format_with {
-> Result<String> ($t:ident, $read_str:expr) => {
{ pub struct $t;
aggregate_global_format!("imag.logging.format.debug", impl LogLevelAggregator for $t {
"Config missing: Logging format: Debug", fn aggregate(config: Option<&Value>) -> Result<String> {
config) config.ok_or_else(|| {
} E::from(err_msg(concat!("Config missing: Logging format: ", stringify!($t))))
})?
.read_string($read_str)
.map_err(E::from)
.context(EM::TomlQueryError)?
.ok_or_else(|| {
E::from(err_msg(concat!("Config missing: Logging format: ", stringify!($t))))
})
}
}
};
}
fn aggregate_global_format_info(config: Option<&Value>) pub trait LogLevelAggregator {
-> Result<String> fn aggregate(config: Option<&Value>) -> Result<String>;
{ }
aggregate_global_format!("imag.logging.format.info",
"Config missing: Logging format: Info",
config)
}
fn aggregate_global_format_warn(config: Option<&Value>) pub fn aggregate<T: LogLevelAggregator>(hb: &mut Handlebars, config: Option<&Value>, lvlstr: &str)
-> Result<String> -> Result<()>
{ {
aggregate_global_format!("imag.logging.format.warn", hb.register_template_string(lvlstr, T::aggregate(config)?)
"Config missing: Logging format: Warn", .map_err(E::from)
config) .context(err_msg("Handlebars template error"))
} .map_err(E::from)
}
aggregate_global_format_with!(Trace, "imag.logging.format.trace");
aggregate_global_format_with!(Debug, "imag.logging.format.debug");
aggregate_global_format_with!(Info, "imag.logging.format.info");
aggregate_global_format_with!(Warn, "imag.logging.format.warn");
aggregate_global_format_with!(Error, "imag.logging.format.error");
fn aggregate_global_format_error(config: Option<&Value>)
-> Result<String>
{
aggregate_global_format!("imag.logging.format.error",
"Config missing: Logging format: Error",
config)
} }
fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>) fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)
@ -414,9 +391,8 @@ fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)
.map(|val| { .map(|val| {
val.as_array() val.as_array()
.ok_or_else(|| { .ok_or_else(|| {
let path = "imag.logging.modules.<mod>.destinations"; let msg = "Type error at 'imag.logging.modules.<mod>.destinations', expected 'Array'";
let ty = "Array"; Error::from(err_msg(msg))
Error::from(format_err!("Type error at {}, expected {}", path, ty))
}) })
.and_then(translate_destinations) .and_then(translate_destinations)
}) })
@ -434,9 +410,8 @@ fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)
.context(EM::TomlQueryError)? .context(EM::TomlQueryError)?
.map(|v| v.as_bool().unwrap_or(false)) .map(|v| v.as_bool().unwrap_or(false))
.ok_or_else(|| { .ok_or_else(|| {
let path = "imag.logging.modules.<mod>.enabled"; let msg = "Type error at 'imag.logging.modules.<mod>.enabled', expected 'Boolean'";
let ty = "Boolean"; Error::from(err_msg(msg))
Error::from(format_err!("Type error at {}, expected {}", path, ty))
})?; })?;
let module_settings = ModuleSettings { let module_settings = ModuleSettings {
@ -452,9 +427,8 @@ fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)
Ok(settings) Ok(settings)
}, },
Ok(Some(_)) => { Ok(Some(_)) => {
let path = "imag.logging.modules"; let msg = "Type error at 'imag.logging.modules', expected 'Table'";
let ty = "Table"; Err(Error::from(err_msg(msg)))
Err(Error::from(format_err!("Type error at {}, expected {}", path, ty)))
}, },
Ok(None) => { Ok(None) => {
// No modules configured. This is okay! // No modules configured. This is okay!

View File

@ -137,13 +137,11 @@ impl<'a> Runtime<'a> {
Store::new(storepath, &config) Store::new(storepath, &config)
}; };
store_result.map(|store| { store_result.map(|store| Runtime {
Runtime { cli_matches: matches,
cli_matches: matches, configuration: config,
configuration: config, rtp: rtp,
rtp: rtp, store: store,
store: store,
}
}) })
.context(err_msg("Cannot instantiate runtime")) .context(err_msg("Cannot instantiate runtime"))
.map_err(Error::from) .map_err(Error::from)
@ -390,15 +388,17 @@ impl<'a> Runtime<'a> {
self.cli() self.cli()
.value_of("editor") .value_of("editor")
.map(String::from) .map(String::from)
.or_else(|| { .ok_or_else(|| {
self.config() self.config()
.and_then(|v| match v.read("rt.editor") { .ok_or_else(|| Error::from(err_msg("No Configuration!")))
Ok(Some(&Value::String(ref s))) => Some(s.clone()), .and_then(|v| match v.read("rt.editor")? {
_ => None, // FIXME silently ignore errors in config is bad Some(&Value::String(ref s)) => Ok(Some(s.clone())),
Some(_) => Err(Error::from(err_msg("Type error at 'rt.editor', expected 'String'"))),
None => Ok(None),
}) })
}) })
.or(env::var("EDITOR").ok()) .or(env::var("EDITOR"))
.ok_or_else(|| Error::from(EM::IO)) .map_err(|_| Error::from(EM::IO))
.map_dbg(|s| format!("Editing with '{}'", s)) .map_dbg(|s| format!("Editing with '{}'", s))
.and_then(|s| { .and_then(|s| {
let mut split = s.split_whitespace(); let mut split = s.split_whitespace();

View File

@ -105,15 +105,14 @@ impl FileAbstraction for FSFileAbstraction {
} }
fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<()> { fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
match to.parent() { if let Some(p) = to.parent() {
Some(p) => if !p.exists() { if !p.exists() {
debug!("Creating: {:?}", p); debug!("Creating: {:?}", p);
let _ = create_dir_all(&PathBuf::from(p)).context(EM::DirNotCreated)?; let _ = create_dir_all(&p).context(EM::DirNotCreated)?;
}, }
None => { } else {
debug!("Failed to find parent. This looks like it will fail now"); debug!("Failed to find parent. This looks like it will fail now");
//nothing //nothing
},
} }
debug!("Renaming {:?} to {:?}", from, to); debug!("Renaming {:?} to {:?}", from, to);
@ -149,10 +148,9 @@ impl FileAbstraction for FSFileAbstraction {
/// FileAbstraction::fill implementation that consumes the Drain and writes everything to the /// FileAbstraction::fill implementation that consumes the Drain and writes everything to the
/// filesystem /// filesystem
fn fill(&mut self, mut d: Drain) -> Result<()> { fn fill(&mut self, mut d: Drain) -> Result<()> {
d.iter() d.iter().fold(Ok(()), |acc, (path, element)| {
.fold(Ok(()), |acc, (path, element)| { acc.and_then(|_| self.new_instance(path).write_file_content(&element))
acc.and_then(|_| self.new_instance(path).write_file_content(&element)) })
})
} }
fn pathes_recursively(&self, fn pathes_recursively(&self,
@ -190,11 +188,9 @@ impl PathIterBuilder for WalkDirPathIterBuilder {
fn open_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<Option<File>> { fn open_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<Option<File>> {
match OpenOptions::new().write(true).read(true).open(p) { match OpenOptions::new().write(true).read(true).open(p) {
Err(e) => { Err(e) => match e.kind() {
match e.kind() { ::std::io::ErrorKind::NotFound => return Ok(None),
::std::io::ErrorKind::NotFound => return Ok(None), _ => return Err(e),
_ => return Err(e),
}
}, },
Ok(file) => Ok(Some(file)) Ok(file) => Ok(Some(file))
} }
@ -205,9 +201,7 @@ fn create_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {
trace!("'{}' is directory = {}", parent.display(), parent.is_dir()); trace!("'{}' is directory = {}", parent.display(), parent.is_dir());
if !parent.is_dir() { if !parent.is_dir() {
trace!("Implicitely creating directory: {:?}", parent); trace!("Implicitely creating directory: {:?}", parent);
if let Err(e) = create_dir_all(parent) { let _ = create_dir_all(parent)?;
return Err(e);
}
} }
} }
OpenOptions::new().write(true).read(true).create(true).open(p) OpenOptions::new().write(true).read(true).create(true).open(p)

View File

@ -140,7 +140,7 @@ impl FileAbstraction for InMemoryFileAbstraction {
let mut mtx = self.backend().lock().expect("Locking Mutex failed"); let mut mtx = self.backend().lock().expect("Locking Mutex failed");
let backend = mtx.get_mut(); let backend = mtx.get_mut();
let a = backend.get(from).cloned().ok_or_else(|| EM::FileNotFound)?; let a = backend.remove(from).ok_or_else(|| EM::FileNotFound)?;
backend.insert(to.clone(), a); backend.insert(to.clone(), a);
debug!("Renaming: {:?} -> {:?} worked", from, to); debug!("Renaming: {:?} -> {:?} worked", from, to);
Ok(()) Ok(())

View File

@ -222,11 +222,13 @@ impl Store {
debug!("Creating id: '{}'", id); debug!("Creating id: '{}'", id);
let exists = id.exists()? || self.entries let exists =
.read() self.entries
.map(|map| map.contains_key(&id)) .read()
.map_err(|_| Error::from(EM::LockError)) .map(|map| map.contains_key(&id))
.context(format_err!("CreateCallError: {}", id))?; .map_err(|_| Error::from(EM::LockError))
.context(format_err!("CreateCallError: {}", id))? ||
self.backend.exists(&id.clone().into_pathbuf()?)?;
if exists { if exists {
debug!("Entry exists: {:?}", id); debug!("Entry exists: {:?}", id);
@ -304,11 +306,14 @@ impl Store {
debug!("Getting id: '{}'", id); debug!("Getting id: '{}'", id);
let exists = id.exists()? || self.entries let exists =
.read() self.entries
.map(|map| map.contains_key(&id)) .read()
.map_err(|_| Error::from(EM::LockError)) .map(|map| map.contains_key(&id))
.context(format_err!("GetCallError: {}", id))?; .map_err(|_| Error::from(EM::LockError))
.context(format_err!("CreateCallError: {}", id))? ||
self.backend.exists(&id.clone().into_pathbuf()?)?;
if !exists { if !exists {
debug!("Does not exist in internal cache or filesystem: {:?}", id); debug!("Does not exist in internal cache or filesystem: {:?}", id);
@ -676,8 +681,10 @@ impl<'a> FileLockEntry<'a, > {
impl<'a> Debug for FileLockEntry<'a> { impl<'a> Debug for FileLockEntry<'a> {
fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FMTError> { fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FMTError> {
write!(fmt, "FileLockEntry(Store = {})", self.store.location.to_str() write!(fmt,
.unwrap_or("Unknown Path")) "FileLockEntry(Store = {store}, location = {location:?})",
store = self.store.location.to_str().unwrap_or("Unknown Path"),
location = self.entry.get_location())
} }
} }
@ -1214,10 +1221,13 @@ mod store_tests {
assert!(store.entries.read().unwrap().get(&id_mv_with_base).is_some()); assert!(store.entries.read().unwrap().get(&id_mv_with_base).is_some());
} }
assert!(match store.get(id.clone()) { Ok(None) => true, _ => false }, let res = store.get(id.clone());
"Moved id ({:?}) is still there", id); assert!(match res { Ok(None) => true, _ => false },
assert!(match store.get(id_mv.clone()) { Ok(Some(_)) => true, _ => false }, "Moved id ({:?}) is still there: {:?}", id, res);
"New id ({:?}) is not in store...", id_mv);
let res = store.get(id_mv.clone());
assert!(match res { Ok(Some(_)) => true, _ => false },
"New id ({:?}) is not in store: {:?}", id_mv, res);
} }
} }
} }

View File

@ -69,8 +69,14 @@ pub fn entry_buffer_to_header_content(buf: &str) -> Result<(Value, String)> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
extern crate env_logger;
use super::entry_buffer_to_header_content; use super::entry_buffer_to_header_content;
fn setup_logging() {
let _ = env_logger::try_init();
}
fn mkfile(content: &str) -> String { fn mkfile(content: &str) -> String {
format!(r#"--- format!(r#"---
[imag] [imag]
@ -97,16 +103,17 @@ version = '{version}'
#[test] #[test]
fn test_entry_buffer_to_header_content_2() { fn test_entry_buffer_to_header_content_2() {
setup_logging();
let content = r#"Hai let content = r#"Hai
"#; "#;
let file = mkfile(&content); let file = mkfile(&content);
eprintln!("FILE: <<<{}>>>", file); debug!("FILE: <<<{}>>>", file);
let res = entry_buffer_to_header_content(&file); let res = entry_buffer_to_header_content(&file);
assert!(res.is_ok()); assert!(res.is_ok());
let (_, res_content) = res.unwrap(); let (_, res_content) = res.unwrap();
eprintln!("CONTENT: <<<{}>>>", res_content); debug!("CONTENT: <<<{}>>>", res_content);
assert_eq!(res_content, content) assert_eq!(res_content, content)
} }

View File

@ -194,4 +194,8 @@ impl<'a> Mail<'a> {
self.get_field("In-Reply-To") self.get_field("In-Reply-To")
} }
pub fn fle(&self) -> &FileLockEntry<'a> {
&self.0
}
} }

View File

@ -140,7 +140,6 @@ impl<'a> TaskStore<'a> for Store {
// the change. The (maybe modified) second one is // the change. The (maybe modified) second one is
// expected by taskwarrior. // expected by taskwarrior.
let val = serde_to_string(&ttask).context(err_msg("Import error"))?; let val = serde_to_string(&ttask).context(err_msg("Import error"))?;
println!("{}", val);
// Taskwarrior does not have the concept of deleted tasks, but only modified // Taskwarrior does not have the concept of deleted tasks, but only modified
// ones. // ones.