Matthias Beyer
b66371f79b
With this change applied, libimagstore does not export backend representing types anymore. This change is necessar because when we want to switch the `StoreId` implementation from "complex and stateful" to "Stateless and Cow<'_, &str>", we need to be able to have a type which represents a "`StoreId` plus the path of the Store itself". This "the path of the Store itself" is passed around as reference, to minimize runtime impact. Because such a type should not be exported by the libimagstore crate, we make it `pub(crate)` internally. But because the backend APIs also have to use this type, we would export the (private) type in the APIs of the backend. Because of that we make the backend API also non-visible to crate users, which also decreases the surface of the libimagstore API itself. Besides: Remove function `Link::with_base()` which is not needed anymore (was used in tests only). Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
170 lines
5.1 KiB
Rust
170 lines
5.1 KiB
Rust
//
|
|
// imag - the personal information management suite for the commandline
|
|
// Copyright (C) 2015-2019 Matthias Beyer <mail@beyermatthias.de> and contributors
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; version
|
|
// 2.1 of the License.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
//
|
|
|
|
use types::*;
|
|
|
|
use libimagstore::store::Entry;
|
|
|
|
use toml_query::read::TomlValueReadExt;
|
|
use toml_query::insert::TomlValueInsertExt;
|
|
use toml_query::delete::TomlValueDeleteExt;
|
|
use failure::ResultExt;
|
|
use failure::Fallible as Result;
|
|
use failure::Error;
|
|
|
|
pub trait GPSEntry {
|
|
|
|
fn set_coordinates(&mut self, c: Coordinates) -> Result<()>;
|
|
fn get_coordinates(&self) -> Result<Option<Coordinates>>;
|
|
|
|
/// Remove the coordinates from the entry
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// The return type is a bit complicated, but that has a reason:
|
|
///
|
|
/// The outer Result<_> is used for notifying a failure during the header read/write action.
|
|
/// If the Option<_> is Some(_), the value was deleted.
|
|
/// The inner Result<_> is used for parsing failures during the parsing of the deleted value.
|
|
///
|
|
/// So:
|
|
///
|
|
/// * Ok(Some(Ok(_))) if the coordinates were deleted, returning the deleted value
|
|
/// * Ok(Some(Err(_))) if the coordinates were deleted, but the deleted value couldn't be parsed
|
|
/// * Ok(None) if there were no coordinates to delete
|
|
/// * Err(e) if the deleting failed
|
|
///
|
|
fn remove_coordinates(&mut self) -> Result<Option<Result<Coordinates>>>;
|
|
|
|
}
|
|
|
|
impl GPSEntry for Entry {
|
|
|
|
fn set_coordinates(&mut self, c: Coordinates) -> Result<()> {
|
|
self.get_header_mut()
|
|
.insert("gps.coordinates", c.into())
|
|
.map(|_| ())
|
|
.map_err(Error::from)
|
|
}
|
|
|
|
fn get_coordinates(&self) -> Result<Option<Coordinates>> {
|
|
match self.get_header().read("gps.coordinates").map_err(Error::from)? {
|
|
Some(hdr) => Coordinates::from_value(hdr).map(Some),
|
|
None => Ok(None),
|
|
}
|
|
}
|
|
|
|
fn remove_coordinates(&mut self) -> Result<Option<Result<Coordinates>>> {
|
|
let coordinates = self.get_coordinates();
|
|
|
|
let patterns = [
|
|
"gps.coordinates.latitude.degree",
|
|
"gps.coordinates.latitude.minutes",
|
|
"gps.coordinates.latitude.seconds",
|
|
"gps.coordinates.longitude.degree",
|
|
"gps.coordinates.longitude.minutes",
|
|
"gps.coordinates.longitude.seconds",
|
|
"gps.coordinates.latitude",
|
|
"gps.coordinates.longitude",
|
|
"gps.coordinates",
|
|
"gps",
|
|
];
|
|
|
|
let hdr = self.get_header_mut();
|
|
for pattern in patterns.iter() {
|
|
let _ = hdr.delete(pattern)
|
|
.map_err(Error::from)
|
|
.context("Error writing header")?;
|
|
}
|
|
|
|
match coordinates {
|
|
Ok(None) => Ok(None),
|
|
Ok(Some(some)) => Ok(Some(Ok(some))),
|
|
Err(e) => Ok(Some(Err(e))),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::path::PathBuf;
|
|
|
|
use libimagstore::store::Store;
|
|
|
|
use entry::*;
|
|
|
|
fn setup_logging() {
|
|
let _ = ::env_logger::try_init;
|
|
}
|
|
|
|
fn get_store() -> Store {
|
|
Store::new_inmemory(PathBuf::from("/"), &None).unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn test_set_gps() {
|
|
setup_logging();
|
|
|
|
let store = get_store();
|
|
|
|
let mut entry = store.create(PathBuf::from("test_set_gps")).unwrap();
|
|
|
|
let coordinates = Coordinates {
|
|
latitude: GPSValue::new(0, 0, 0),
|
|
longitude: GPSValue::new(0, 0, 0),
|
|
};
|
|
|
|
let res = entry.set_coordinates(coordinates);
|
|
|
|
assert!(res.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_setget_gps() {
|
|
setup_logging();
|
|
|
|
let store = get_store();
|
|
|
|
let mut entry = store.create(PathBuf::from("test_setget_gps")).unwrap();
|
|
|
|
let coordinates = Coordinates {
|
|
latitude: GPSValue::new(0, 0, 0),
|
|
longitude: GPSValue::new(0, 0, 0),
|
|
};
|
|
|
|
let res = entry.set_coordinates(coordinates);
|
|
assert!(res.is_ok());
|
|
|
|
let coordinates = entry.get_coordinates();
|
|
|
|
assert!(coordinates.is_ok());
|
|
let coordinates = coordinates.unwrap();
|
|
|
|
assert!(coordinates.is_some());
|
|
let coordinates = coordinates.unwrap();
|
|
|
|
assert_eq!(0, coordinates.longitude.degree);
|
|
assert_eq!(0, coordinates.longitude.minutes);
|
|
assert_eq!(0, coordinates.longitude.seconds);
|
|
assert_eq!(0, coordinates.latitude.degree);
|
|
assert_eq!(0, coordinates.latitude.minutes);
|
|
assert_eq!(0, coordinates.latitude.seconds);
|
|
}
|
|
}
|
|
|