17 Commits
Author SHA1 Message Date
daladim 2e4be4a754 License, readme and metadata 2021-09-29 23:47:00 +02:00
daladim 4c54515b79 More logs 2021-09-29 08:38:27 +02:00
daladim e563cb3b2f Comparison functions are no longer provided 2021-08-12 20:05:58 +02:00
daladim 92b3089e5e [typo] 2021-08-12 19:06:17 +02:00
daladim db662d2f4a Non-async versions of deletion functions 2021-08-12 19:06:17 +02:00
daladim 9234d57cdc Non-async versions of add_item/update_item 2021-08-12 19:06:17 +02:00
daladim b6e8ce2421 [minor] Removed useless asyncs 2021-08-12 19:06:17 +02:00
daladim 6d984cdc7f [doc] 2021-08-12 19:06:17 +02:00
daladim 78bb78f61f [fixup] Doc and usability 2021-07-08 16:05:15 +02:00
daladim 17716575d8 Added support for calendar colors 2021-07-08 16:01:54 +02:00
daladim 2b339a7aff Added sorting utilities 2021-07-05 11:28:06 +02:00
daladim fbd076875a [doc] 2021-07-05 11:08:18 +02:00
daladim 72e05dc6c9 [deps] Update 2021-07-05 11:07:49 +02:00
daladim e6dda89d01 pub use Cache 2021-07-05 11:06:55 +02:00
daladim bf68d81ec8 More derive(Debug) 2021-07-05 11:06:55 +02:00
daladim 58cfb42f64 Non-async version of some functions + renamed a function 2021-07-05 11:06:55 +02:00
daladim ab8f6a9413 Moved a file 2021-07-05 11:06:55 +02:00
19 changed files with 368 additions and 117 deletions
Generated
+155 -19
View File
@@ -103,6 +103,16 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
[[package]]
name = "csscolorparser"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2fb3bd93ef32553e3d5b9f8020028f41ac64ff8a230033d5d548b8222d21fbe"
dependencies = [
"phf",
"serde",
]
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.28" version = "0.8.28"
@@ -158,43 +168,55 @@ dependencies = [
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.12" version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2"
dependencies = [ dependencies = [
"futures-core", "futures-core",
] ]
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.12" version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1"
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.12" version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.12" version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.12" version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967"
dependencies = [ dependencies = [
"autocfg",
"futures-core", "futures-core",
"futures-task", "futures-task",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
] ]
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.2" version = "0.2.2"
@@ -203,7 +225,7 @@ checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi", "wasi 0.10.2+wasi-snapshot-preview1",
] ]
[[package]] [[package]]
@@ -381,6 +403,7 @@ dependencies = [
"async-trait", "async-trait",
"bitflags", "bitflags",
"chrono", "chrono",
"csscolorparser",
"env_logger", "env_logger",
"ical", "ical",
"ics", "ics",
@@ -567,6 +590,50 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "phf"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [
"phf_macros",
"phf_shared",
"proc-macro-hack",
]
[[package]]
name = "phf_generator"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
dependencies = [
"phf_shared",
"rand 0.7.3",
]
[[package]]
name = "phf_macros"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
dependencies = [
"siphasher",
]
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.0.5" version = "1.0.5"
@@ -611,6 +678,12 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.24" version = "1.0.24"
@@ -638,6 +711,20 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom 0.1.16",
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc 0.2.0",
"rand_pcg",
]
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.8.3" version = "0.8.3"
@@ -645,9 +732,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [ dependencies = [
"libc", "libc",
"rand_chacha", "rand_chacha 0.3.0",
"rand_core", "rand_core 0.6.2",
"rand_hc", "rand_hc 0.3.0",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core 0.5.1",
] ]
[[package]] [[package]]
@@ -657,7 +754,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [ dependencies = [
"ppv-lite86", "ppv-lite86",
"rand_core", "rand_core 0.6.2",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom 0.1.16",
] ]
[[package]] [[package]]
@@ -666,7 +772,16 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
dependencies = [ dependencies = [
"getrandom", "getrandom 0.2.2",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core 0.5.1",
] ]
[[package]] [[package]]
@@ -675,7 +790,16 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [ dependencies = [
"rand_core", "rand_core 0.6.2",
]
[[package]]
name = "rand_pcg"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
dependencies = [
"rand_core 0.5.1",
] ]
[[package]] [[package]]
@@ -840,6 +964,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "siphasher"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.2" version = "0.4.2"
@@ -876,7 +1006,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"rand", "rand 0.8.3",
"redox_syscall", "redox_syscall",
"remove_dir_all", "remove_dir_all",
"winapi", "winapi",
@@ -1081,7 +1211,7 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [ dependencies = [
"getrandom", "getrandom 0.2.2",
] ]
[[package]] [[package]]
@@ -1100,6 +1230,12 @@ dependencies = [
"try-lock", "try-lock",
] ]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.10.2+wasi-snapshot-preview1" version = "0.10.2+wasi-snapshot-preview1"
+6
View File
@@ -3,6 +3,11 @@ name = "kitchen-fridge"
version = "0.1.0" version = "0.1.0"
authors = ["daladim"] authors = ["daladim"]
edition = "2018" edition = "2018"
description = "A CalDAV (ical file management over WebDAV) library"
repository = "https://github.com/daladim/kitchen-fridge"
license = "MIT"
keywords = ["CalDAV", "client", "WebDAV", "todo", "RFC4791"]
categories = ["network-programming", "web-programming::http-client"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -26,3 +31,4 @@ sanitize-filename = "0.3"
ical = "0.7" ical = "0.7"
ics = "0.5" ics = "0.5"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
csscolorparser = { version = "0.5", features = ["serde"] }
+7
View File
@@ -0,0 +1,7 @@
Copyright © 2021 kitchen-fridge contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+5
View File
@@ -0,0 +1,5 @@
# kitchen-fridge
kitchen-fridge is a CalDAV (iCal file transfer over WebDAV) Rust library.
Its [documentation](https://docs.rs/kitchen-fridge/) is available on docs.rs.
+1 -1
View File
@@ -69,7 +69,7 @@ async fn add_items_and_sync_again(provider: &mut CalDavProvider)
let new_calendar_id: CalendarId = EXAMPLE_CREATED_CALENDAR_URL.parse().unwrap(); let new_calendar_id: CalendarId = EXAMPLE_CREATED_CALENDAR_URL.parse().unwrap();
let new_calendar_name = "A brave new calendar".to_string(); let new_calendar_name = "A brave new calendar".to_string();
if let Err(_err) = provider.local_mut() if let Err(_err) = provider.local_mut()
.create_calendar(new_calendar_id.clone(), new_calendar_name.clone(), SupportedComponents::TODO) .create_calendar(new_calendar_id.clone(), new_calendar_name.clone(), SupportedComponents::TODO, None)
.await { .await {
println!("Unable to add calendar, maybe it exists already. We're not adding it after all."); println!("Unable to add calendar, maybe it exists already. We're not adding it after all.");
} }
+23 -8
View File
@@ -9,6 +9,7 @@ use std::ffi::OsStr;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use async_trait::async_trait; use async_trait::async_trait;
use csscolorparser::Color;
use crate::traits::CalDavSource; use crate::traits::CalDavSource;
use crate::traits::BaseCalendar; use crate::traits::BaseCalendar;
@@ -25,6 +26,8 @@ const MAIN_FILE: &str = "data.json";
/// A CalDAV source that stores its items in a local folder. /// A CalDAV source that stores its items in a local folder.
/// ///
/// It automatically updates the content of the folder when dropped (see its `Drop` implementation), but you can also manually call [`Cache::save_to_folder`] /// It automatically updates the content of the folder when dropped (see its `Drop` implementation), but you can also manually call [`Cache::save_to_folder`]
///
/// Most of its methods are part of the `CalDavSource` trait implementation
#[derive(Debug)] #[derive(Debug)]
pub struct Cache { pub struct Cache {
backing_folder: PathBuf, backing_folder: PathBuf,
@@ -180,10 +183,8 @@ impl Drop for Cache {
} }
} }
impl Cache {
#[async_trait] pub fn get_calendars_sync(&self) -> Result<HashMap<CalendarId, Arc<Mutex<CachedCalendar>>>, Box<dyn Error>> {
impl CalDavSource<CachedCalendar> for Cache {
async fn get_calendars(&self) -> Result<HashMap<CalendarId, Arc<Mutex<CachedCalendar>>>, Box<dyn Error>> {
#[cfg(feature = "local_calendar_mocks_remote_calendars")] #[cfg(feature = "local_calendar_mocks_remote_calendars")]
self.mock_behaviour.as_ref().map_or(Ok(()), |b| b.lock().unwrap().can_get_calendars())?; self.mock_behaviour.as_ref().map_or(Ok(()), |b| b.lock().unwrap().can_get_calendars())?;
@@ -193,16 +194,27 @@ impl CalDavSource<CachedCalendar> for Cache {
) )
} }
async fn get_calendar(&self, id: &CalendarId) -> Option<Arc<Mutex<CachedCalendar>>> { pub fn get_calendar_sync(&self, id: &CalendarId) -> Option<Arc<Mutex<CachedCalendar>>> {
self.data.calendars.get(id).map(|arc| arc.clone()) self.data.calendars.get(id).map(|arc| arc.clone())
} }
}
async fn create_calendar(&mut self, id: CalendarId, name: String, supported_components: SupportedComponents) -> Result<Arc<Mutex<CachedCalendar>>, Box<dyn Error>> { #[async_trait]
impl CalDavSource<CachedCalendar> for Cache {
async fn get_calendars(&self) -> Result<HashMap<CalendarId, Arc<Mutex<CachedCalendar>>>, Box<dyn Error>> {
self.get_calendars_sync()
}
async fn get_calendar(&self, id: &CalendarId) -> Option<Arc<Mutex<CachedCalendar>>> {
self.get_calendar_sync(id)
}
async fn create_calendar(&mut self, id: CalendarId, name: String, supported_components: SupportedComponents, color: Option<Color>) -> Result<Arc<Mutex<CachedCalendar>>, Box<dyn Error>> {
log::debug!("Inserting local calendar {}", id); log::debug!("Inserting local calendar {}", id);
#[cfg(feature = "local_calendar_mocks_remote_calendars")] #[cfg(feature = "local_calendar_mocks_remote_calendars")]
self.mock_behaviour.as_ref().map_or(Ok(()), |b| b.lock().unwrap().can_create_calendar())?; self.mock_behaviour.as_ref().map_or(Ok(()), |b| b.lock().unwrap().can_create_calendar())?;
let new_calendar = CachedCalendar::new(name, id.clone(), supported_components); let new_calendar = CachedCalendar::new(name, id.clone(), supported_components, color);
let arc = Arc::new(Mutex::new(new_calendar)); let arc = Arc::new(Mutex::new(new_calendar));
#[cfg(feature = "local_calendar_mocks_remote_calendars")] #[cfg(feature = "local_calendar_mocks_remote_calendars")]
@@ -233,12 +245,14 @@ mod tests {
Url::parse("https://caldav.com/shopping").unwrap(), Url::parse("https://caldav.com/shopping").unwrap(),
"My shopping list".to_string(), "My shopping list".to_string(),
SupportedComponents::TODO, SupportedComponents::TODO,
Some(csscolorparser::parse("lime").unwrap()),
).await.unwrap(); ).await.unwrap();
let bucket_list = cache.create_calendar( let bucket_list = cache.create_calendar(
Url::parse("https://caldav.com/bucket-list").unwrap(), Url::parse("https://caldav.com/bucket-list").unwrap(),
"My bucket list".to_string(), "My bucket list".to_string(),
SupportedComponents::TODO, SupportedComponents::TODO,
Some(csscolorparser::parse("#ff8000").unwrap()),
).await.unwrap(); ).await.unwrap();
{ {
@@ -277,11 +291,12 @@ mod tests {
let cache_path = PathBuf::from(String::from("test_cache/sanity_tests")); let cache_path = PathBuf::from(String::from("test_cache/sanity_tests"));
let mut cache = populate_cache(&cache_path).await; let mut cache = populate_cache(&cache_path).await;
// We should not be able to add twice the same calendar // We should not be able to add a second calendar with the same id
let second_addition_same_calendar = cache.create_calendar( let second_addition_same_calendar = cache.create_calendar(
Url::parse("https://caldav.com/shopping").unwrap(), Url::parse("https://caldav.com/shopping").unwrap(),
"My shopping list".to_string(), "My shopping list".to_string(),
SupportedComponents::TODO, SupportedComponents::TODO,
None,
).await; ).await;
assert!(second_addition_same_calendar.is_err()); assert!(second_addition_same_calendar.is_err());
} }
+118 -67
View File
@@ -3,6 +3,7 @@ use std::error::Error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use async_trait::async_trait; use async_trait::async_trait;
use csscolorparser::Color;
use crate::item::SyncStatus; use crate::item::SyncStatus;
use crate::traits::{BaseCalendar, CompleteCalendar}; use crate::traits::{BaseCalendar, CompleteCalendar};
@@ -17,11 +18,14 @@ use crate::mock_behaviour::MockBehaviour;
/// A calendar used by the [`cache`](crate::cache) module /// A calendar used by the [`cache`](crate::cache) module
///
/// Most of its methods are part of traits implementations
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CachedCalendar { pub struct CachedCalendar {
name: String, name: String,
id: CalendarId, id: CalendarId,
supported_components: SupportedComponents, supported_components: SupportedComponents,
color: Option<Color>,
#[cfg(feature = "local_calendar_mocks_remote_calendars")] #[cfg(feature = "local_calendar_mocks_remote_calendars")]
#[serde(skip)] #[serde(skip)]
mock_behaviour: Option<Arc<Mutex<MockBehaviour>>>, mock_behaviour: Option<Arc<Mutex<MockBehaviour>>>,
@@ -38,27 +42,27 @@ impl CachedCalendar {
#[cfg(feature = "local_calendar_mocks_remote_calendars")] #[cfg(feature = "local_calendar_mocks_remote_calendars")]
async fn add_item_maybe_mocked(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> { fn add_item_maybe_mocked(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
if self.mock_behaviour.is_some() { if self.mock_behaviour.is_some() {
self.mock_behaviour.as_ref().map_or(Ok(()), |b| b.lock().unwrap().can_add_item())?; self.mock_behaviour.as_ref().map_or(Ok(()), |b| b.lock().unwrap().can_add_item())?;
self.add_or_update_item_force_synced(item).await self.add_or_update_item_force_synced(item)
} else { } else {
self.regular_add_or_update_item(item).await self.regular_add_or_update_item(item)
} }
} }
#[cfg(feature = "local_calendar_mocks_remote_calendars")] #[cfg(feature = "local_calendar_mocks_remote_calendars")]
async fn update_item_maybe_mocked(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> { fn update_item_maybe_mocked(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
if self.mock_behaviour.is_some() { if self.mock_behaviour.is_some() {
self.mock_behaviour.as_ref().map_or(Ok(()), |b| b.lock().unwrap().can_update_item())?; self.mock_behaviour.as_ref().map_or(Ok(()), |b| b.lock().unwrap().can_update_item())?;
self.add_or_update_item_force_synced(item).await self.add_or_update_item_force_synced(item)
} else { } else {
self.regular_add_or_update_item(item).await self.regular_add_or_update_item(item)
} }
} }
/// Add or update an item /// Add or update an item
async fn regular_add_or_update_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> { fn regular_add_or_update_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
let ss_clone = item.sync_status().clone(); let ss_clone = item.sync_status().clone();
log::debug!("Adding or updating an item with {:?}", ss_clone); log::debug!("Adding or updating an item with {:?}", ss_clone);
self.items.insert(item.id().clone(), item); self.items.insert(item.id().clone(), item);
@@ -67,7 +71,7 @@ impl CachedCalendar {
/// Add or update an item, but force a "synced" SyncStatus. This is the normal behaviour that would happen on a server /// Add or update an item, but force a "synced" SyncStatus. This is the normal behaviour that would happen on a server
#[cfg(feature = "local_calendar_mocks_remote_calendars")] #[cfg(feature = "local_calendar_mocks_remote_calendars")]
async fn add_or_update_item_force_synced(&mut self, mut item: Item) -> Result<SyncStatus, Box<dyn Error>> { fn add_or_update_item_force_synced(&mut self, mut item: Item) -> Result<SyncStatus, Box<dyn Error>> {
log::debug!("Adding or updating an item, but forces a synced SyncStatus"); log::debug!("Adding or updating an item, but forces a synced SyncStatus");
match item.sync_status() { match item.sync_status() {
SyncStatus::Synced(_) => (), SyncStatus::Synced(_) => (),
@@ -83,7 +87,9 @@ impl CachedCalendar {
pub async fn has_same_observable_content_as(&self, other: &CachedCalendar) -> Result<bool, Box<dyn Error>> { pub async fn has_same_observable_content_as(&self, other: &CachedCalendar) -> Result<bool, Box<dyn Error>> {
if self.name != other.name if self.name != other.name
|| self.id != other.id || self.id != other.id
|| self.supported_components != other.supported_components { || self.supported_components != other.supported_components
|| self.color != other.color
{
log::debug!("Calendar properties mismatch"); log::debug!("Calendar properties mismatch");
return Ok(false); return Ok(false);
} }
@@ -111,80 +117,59 @@ impl CachedCalendar {
Ok(true) Ok(true)
} }
}
/// The non-async version of [`Self::get_item_ids`]
#[async_trait] pub fn get_item_ids_sync(&self) -> Result<HashSet<ItemId>, Box<dyn Error>> {
impl BaseCalendar for CachedCalendar {
fn name(&self) -> &str {
&self.name
}
fn id(&self) -> &CalendarId {
&self.id
}
fn supported_components(&self) -> SupportedComponents {
self.supported_components
}
async fn add_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
if self.items.contains_key(item.id()) {
return Err(format!("Item {:?} cannot be added, it exists already", item.id()).into());
}
#[cfg(not(feature = "local_calendar_mocks_remote_calendars"))]
return self.regular_add_or_update_item(item).await;
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
return self.add_item_maybe_mocked(item).await;
}
async fn update_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
if self.items.contains_key(item.id()) == false {
return Err(format!("Item {:?} cannot be updated, it does not already exist", item.id()).into());
}
#[cfg(not(feature = "local_calendar_mocks_remote_calendars"))]
return self.regular_add_or_update_item(item).await;
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
return self.update_item_maybe_mocked(item).await;
}
}
#[async_trait]
impl CompleteCalendar for CachedCalendar {
fn new(name: String, id: CalendarId, supported_components: SupportedComponents) -> Self {
Self {
name, id, supported_components,
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
mock_behaviour: None,
items: HashMap::new(),
}
}
async fn get_item_ids(&self) -> Result<HashSet<ItemId>, Box<dyn Error>> {
Ok(self.items.iter() Ok(self.items.iter()
.map(|(id, _)| id.clone()) .map(|(id, _)| id.clone())
.collect() .collect()
) )
} }
async fn get_items(&self) -> Result<HashMap<ItemId, &Item>, Box<dyn Error>> { /// The non-async version of [`Self::get_items`]
pub fn get_items_sync(&self) -> Result<HashMap<ItemId, &Item>, Box<dyn Error>> {
Ok(self.items.iter() Ok(self.items.iter()
.map(|(id, item)| (id.clone(), item)) .map(|(id, item)| (id.clone(), item))
.collect() .collect()
) )
} }
async fn get_item_by_id_ref<'a>(&'a self, id: &ItemId) -> Option<&'a Item> { /// The non-async version of [`Self::get_item_by_id`]
pub fn get_item_by_id_sync<'a>(&'a self, id: &ItemId) -> Option<&'a Item> {
self.items.get(id) self.items.get(id)
} }
async fn get_item_by_id_mut<'a>(&'a mut self, id: &ItemId) -> Option<&'a mut Item> { /// The non-async version of [`Self::get_item_by_id_mut`]
pub fn get_item_by_id_mut_sync<'a>(&'a mut self, id: &ItemId) -> Option<&'a mut Item> {
self.items.get_mut(id) self.items.get_mut(id)
} }
async fn mark_for_deletion(&mut self, item_id: &ItemId) -> Result<(), Box<dyn Error>> { /// The non-async version of [`Self::add_item`]
pub fn add_item_sync(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
if self.items.contains_key(item.id()) {
return Err(format!("Item {:?} cannot be added, it exists already", item.id()).into());
}
#[cfg(not(feature = "local_calendar_mocks_remote_calendars"))]
return self.regular_add_or_update_item(item);
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
return self.add_item_maybe_mocked(item);
}
/// The non-async version of [`Self::update_item`]
pub fn update_item_sync(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
if self.items.contains_key(item.id()) == false {
return Err(format!("Item {:?} cannot be updated, it does not already exist", item.id()).into());
}
#[cfg(not(feature = "local_calendar_mocks_remote_calendars"))]
return self.regular_add_or_update_item(item);
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
return self.update_item_maybe_mocked(item);
}
/// The non-async version of [`Self::mark_for_deletion`]
pub fn mark_for_deletion_sync(&mut self, item_id: &ItemId) -> Result<(), Box<dyn Error>> {
match self.items.get_mut(item_id) { match self.items.get_mut(item_id) {
None => Err("no item for this key".into()), None => Err("no item for this key".into()),
Some(item) => { Some(item) => {
@@ -211,12 +196,78 @@ impl CompleteCalendar for CachedCalendar {
} }
} }
async fn immediately_delete_item(&mut self, item_id: &ItemId) -> Result<(), Box<dyn Error>> { /// The non-async version of [`Self::immediately_delete_item`]
pub fn immediately_delete_item_sync(&mut self, item_id: &ItemId) -> Result<(), Box<dyn Error>> {
match self.items.remove(item_id) { match self.items.remove(item_id) {
None => Err(format!("Item {} is absent from this calendar", item_id).into()), None => Err(format!("Item {} is absent from this calendar", item_id).into()),
Some(_) => Ok(()) Some(_) => Ok(())
} }
} }
}
#[async_trait]
impl BaseCalendar for CachedCalendar {
fn name(&self) -> &str {
&self.name
}
fn id(&self) -> &CalendarId {
&self.id
}
fn supported_components(&self) -> SupportedComponents {
self.supported_components
}
fn color(&self) -> Option<&Color> {
self.color.as_ref()
}
async fn add_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
self.add_item_sync(item)
}
async fn update_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
self.update_item_sync(item)
}
}
#[async_trait]
impl CompleteCalendar for CachedCalendar {
fn new(name: String, id: CalendarId, supported_components: SupportedComponents, color: Option<Color>) -> Self {
Self {
name, id, supported_components, color,
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
mock_behaviour: None,
items: HashMap::new(),
}
}
async fn get_item_ids(&self) -> Result<HashSet<ItemId>, Box<dyn Error>> {
self.get_item_ids_sync()
}
async fn get_items(&self) -> Result<HashMap<ItemId, &Item>, Box<dyn Error>> {
self.get_items_sync()
}
async fn get_item_by_id<'a>(&'a self, id: &ItemId) -> Option<&'a Item> {
self.get_item_by_id_sync(id)
}
async fn get_item_by_id_mut<'a>(&'a mut self, id: &ItemId) -> Option<&'a mut Item> {
self.get_item_by_id_mut_sync(id)
}
async fn mark_for_deletion(&mut self, item_id: &ItemId) -> Result<(), Box<dyn Error>> {
self.mark_for_deletion_sync(item_id)
}
async fn immediately_delete_item(&mut self, item_id: &ItemId) -> Result<(), Box<dyn Error>> {
self.immediately_delete_item_sync(item_id)
}
} }
@@ -231,8 +282,8 @@ use crate::{item::VersionTag,
#[cfg(feature = "local_calendar_mocks_remote_calendars")] #[cfg(feature = "local_calendar_mocks_remote_calendars")]
#[async_trait] #[async_trait]
impl DavCalendar for CachedCalendar { impl DavCalendar for CachedCalendar {
fn new(name: String, resource: Resource, supported_components: SupportedComponents) -> Self { fn new(name: String, resource: Resource, supported_components: SupportedComponents, color: Option<Color>) -> Self {
crate::traits::CompleteCalendar::new(name, resource.url().clone(), supported_components) crate::traits::CompleteCalendar::new(name, resource.url().clone(), supported_components, color)
} }
async fn get_item_version_tags(&self) -> Result<HashMap<ItemId, VersionTag>, Box<dyn Error>> { async fn get_item_version_tags(&self) -> Result<HashMap<ItemId, VersionTag>, Box<dyn Error>> {
+8 -2
View File
@@ -4,6 +4,7 @@ use std::sync::Mutex;
use async_trait::async_trait; use async_trait::async_trait;
use reqwest::{header::CONTENT_TYPE, header::CONTENT_LENGTH}; use reqwest::{header::CONTENT_TYPE, header::CONTENT_LENGTH};
use csscolorparser::Color;
use crate::traits::BaseCalendar; use crate::traits::BaseCalendar;
use crate::traits::DavCalendar; use crate::traits::DavCalendar;
@@ -32,10 +33,12 @@ static TASKS_BODY: &str = r#"
/// A CalDAV calendar created by a [`Client`](crate::client::Client). /// A CalDAV calendar created by a [`Client`](crate::client::Client).
#[derive(Debug)]
pub struct RemoteCalendar { pub struct RemoteCalendar {
name: String, name: String,
resource: Resource, resource: Resource,
supported_components: SupportedComponents, supported_components: SupportedComponents,
color: Option<Color>,
cached_version_tags: Mutex<Option<HashMap<ItemId, VersionTag>>>, cached_version_tags: Mutex<Option<HashMap<ItemId, VersionTag>>>,
} }
@@ -47,6 +50,9 @@ impl BaseCalendar for RemoteCalendar {
fn supported_components(&self) -> crate::calendar::SupportedComponents { fn supported_components(&self) -> crate::calendar::SupportedComponents {
self.supported_components self.supported_components
} }
fn color(&self) -> Option<&Color> {
self.color.as_ref()
}
async fn add_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> { async fn add_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
let ical_text = crate::ical::build_from(&item)?; let ical_text = crate::ical::build_from(&item)?;
@@ -113,9 +119,9 @@ impl BaseCalendar for RemoteCalendar {
#[async_trait] #[async_trait]
impl DavCalendar for RemoteCalendar { impl DavCalendar for RemoteCalendar {
fn new(name: String, resource: Resource, supported_components: SupportedComponents) -> Self { fn new(name: String, resource: Resource, supported_components: SupportedComponents, color: Option<Color>) -> Self {
Self { Self {
name, resource, supported_components, name, resource, supported_components, color,
cached_version_tags: Mutex::new(None), cached_version_tags: Mutex::new(None),
} }
} }
+13 -3
View File
@@ -10,6 +10,7 @@ use reqwest::{Method, StatusCode};
use reqwest::header::CONTENT_TYPE; use reqwest::header::CONTENT_TYPE;
use minidom::Element; use minidom::Element;
use url::Url; use url::Url;
use csscolorparser::Color;
use crate::resource::Resource; use crate::resource::Resource;
use crate::utils::{find_elem, find_elems}; use crate::utils::{find_elem, find_elems};
@@ -42,6 +43,7 @@ static CAL_BODY: &str = r#"
<d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav" > <d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav" >
<d:prop> <d:prop>
<d:displayname /> <d:displayname />
<E:calendar-color xmlns:E="http://apple.com/ns/ical/"/>
<d:resourcetype /> <d:resourcetype />
<c:supported-calendar-component-set /> <c:supported-calendar-component-set />
</d:prop> </d:prop>
@@ -97,6 +99,7 @@ pub(crate) async fn sub_request_and_extract_elems(resource: &Resource, method: &
/// A CalDAV data source that fetches its data from a CalDAV server /// A CalDAV data source that fetches its data from a CalDAV server
#[derive(Debug)]
pub struct Client { pub struct Client {
resource: Resource, resource: Resource,
@@ -106,7 +109,7 @@ pub struct Client {
} }
#[derive(Default)] #[derive(Debug, Default)]
struct CachedReplies { struct CachedReplies {
principal: Option<Resource>, principal: Option<Resource>,
calendar_home_set: Option<Resource>, calendar_home_set: Option<Resource>,
@@ -204,7 +207,14 @@ impl Client {
}, },
Ok(sc) => sc, Ok(sc) => sc,
}; };
let this_calendar = RemoteCalendar::new(display_name, this_calendar_url, supported_components);
let this_calendar_color = find_elem(&rep, "calendar-color")
.and_then(|col| {
col.texts().next()
.and_then(|t| csscolorparser::parse(t).ok())
});
let this_calendar = RemoteCalendar::new(display_name, this_calendar_url, supported_components, this_calendar_color);
log::info!("Found calendar {}", this_calendar.name()); log::info!("Found calendar {}", this_calendar.name());
calendars.insert(this_calendar.id().clone(), Arc::new(Mutex::new(this_calendar))); calendars.insert(this_calendar.id().clone(), Arc::new(Mutex::new(this_calendar)));
} }
@@ -242,7 +252,7 @@ impl CalDavSource<RemoteCalendar> for Client {
.map(|cal| cal.clone()) .map(|cal| cal.clone())
} }
async fn create_calendar(&mut self, id: CalendarId, name: String, supported_components: SupportedComponents) -> Result<Arc<Mutex<RemoteCalendar>>, Box<dyn Error>> { async fn create_calendar(&mut self, id: CalendarId, name: String, supported_components: SupportedComponents, color: Option<Color>) -> Result<Arc<Mutex<RemoteCalendar>>, Box<dyn Error>> {
self.populate_calendars().await?; self.populate_calendars().await?;
match self.cached_replies.lock().unwrap().calendars.as_ref() { match self.cached_replies.lock().unwrap().calendars.as_ref() {
+1 -1
View File
@@ -206,7 +206,7 @@ impl VersionTag {
/// Desribes whether this item has been synced already, or modified since the last time it was synced /// Describes whether this item has been synced already, or modified since the last time it was synced
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum SyncStatus { pub enum SyncStatus {
/// This item has ben locally created, and never synced yet /// This item has ben locally created, and never synced yet
+1
View File
@@ -35,6 +35,7 @@ pub mod mock_behaviour;
pub mod client; pub mod client;
pub use client::Client; pub use client::Client;
pub mod cache; pub mod cache;
pub use cache::Cache;
pub mod ical; pub mod ical;
pub mod settings; pub mod settings;
+9 -4
View File
@@ -45,6 +45,7 @@ impl SyncResult {
/// ///
/// Usually, you will only need to use a provider between a server and a local cache, that is to say a [`CalDavProvider`](crate::CalDavProvider), i.e. a `Provider<Cache, CachedCalendar, Client, RemoteCalendar>`. \ /// Usually, you will only need to use a provider between a server and a local cache, that is to say a [`CalDavProvider`](crate::CalDavProvider), i.e. a `Provider<Cache, CachedCalendar, Client, RemoteCalendar>`. \
/// However, providers can be used for integration tests, where the remote source is mocked by a `Cache`. /// However, providers can be used for integration tests, where the remote source is mocked by a `Cache`.
#[derive(Debug)]
pub struct Provider<L, T, R, U> pub struct Provider<L, T, R, U>
where where
L: CalDavSource<T>, L: CalDavSource<T>,
@@ -84,7 +85,7 @@ where
pub fn local_mut(&mut self) -> &mut L { &mut self.local } pub fn local_mut(&mut self) -> &mut L { &mut self.local }
/// Returns the data source described as `remote`. /// Returns the data source described as `remote`.
/// ///
/// Apart in tests, there are very few (if any) reasons to access `remote` directly. /// Apart from tests, there are very few (if any) reasons to access `remote` directly.
/// Usually, you should rather use the `local` source, which (usually) is a much faster local cache. /// Usually, you should rather use the `local` source, which (usually) is a much faster local cache.
/// To be sure `local` accurately mirrors the `remote` source, you can run [`Provider::sync`] /// To be sure `local` accurately mirrors the `remote` source, you can run [`Provider::sync`]
pub fn remote(&self) -> &R { &self.remote } pub fn remote(&self) -> &R { &self.remote }
@@ -105,7 +106,7 @@ where
} }
async fn run_sync(&mut self, result: &mut SyncResult) -> Result<(), Box<dyn Error>> { async fn run_sync(&mut self, result: &mut SyncResult) -> Result<(), Box<dyn Error>> {
result.info("Starting a sync."); result.info("Starting a sync");
let mut handled_calendars = HashSet::new(); let mut handled_calendars = HashSet::new();
@@ -148,6 +149,8 @@ where
} }
} }
result.info("Sync ended");
Ok(()) Ok(())
} }
@@ -177,7 +180,7 @@ where
let mut local_items_to_handle = cal_local.get_item_ids().await?; let mut local_items_to_handle = cal_local.get_item_ids().await?;
for (id, remote_tag) in remote_items { for (id, remote_tag) in remote_items {
result.trace(&format!("***** Considering remote item {}...", id)); result.trace(&format!("***** Considering remote item {}...", id));
match cal_local.get_item_by_id_ref(&id).await { match cal_local.get_item_by_id(&id).await {
None => { None => {
// This was created on the remote // This was created on the remote
result.debug(&format!("* {} is a remote addition", id)); result.debug(&format!("* {} is a remote addition", id));
@@ -230,7 +233,7 @@ where
// Also iterate on the local tasks that are not on the remote // Also iterate on the local tasks that are not on the remote
for id in local_items_to_handle { for id in local_items_to_handle {
result.trace(&format!("##### Considering local item {}...", id)); result.trace(&format!("##### Considering local item {}...", id));
let local_item = match cal_local.get_item_by_id_ref(&id).await { let local_item = match cal_local.get_item_by_id(&id).await {
None => { None => {
result.error(&format!("Inconsistent state: missing task {} from the local tasks", id)); result.error(&format!("Inconsistent state: missing task {} from the local tasks", id));
continue; continue;
@@ -389,10 +392,12 @@ where
let src = needle.lock().unwrap(); let src = needle.lock().unwrap();
let name = src.name().to_string(); let name = src.name().to_string();
let supported_comps = src.supported_components(); let supported_comps = src.supported_components();
let color = src.color();
if let Err(err) = haystack.create_calendar( if let Err(err) = haystack.create_calendar(
cal_id.clone(), cal_id.clone(),
name, name,
supported_comps, supported_comps,
color.cloned(),
).await{ ).await{
return Err(err); return Err(err);
} }
+1 -1
View File
@@ -1,7 +1,7 @@
use url::Url; use url::Url;
/// Just a wrapper around a URL and credentials /// Just a wrapper around a URL and credentials
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct Resource { pub struct Resource {
url: Url, url: Url,
username: String, username: String,
+1 -1
View File
@@ -69,7 +69,7 @@ impl Task {
Self::new_with_parameters(name, new_uid, new_item_id, new_completion_status, new_sync_status, new_creation_date, new_last_modified) Self::new_with_parameters(name, new_uid, new_item_id, new_completion_status, new_sync_status, new_creation_date, new_last_modified)
} }
/// Create a new Task instance, that may be synced already /// Create a new Task instance, that may be synced on the server already
pub fn new_with_parameters(name: String, uid: String, id: ItemId, pub fn new_with_parameters(name: String, uid: String, id: ItemId,
completion_status: CompletionStatus, completion_status: CompletionStatus,
sync_status: SyncStatus, creation_date: Option<DateTime<Utc>>, last_modified: DateTime<Utc>) -> Self sync_status: SyncStatus, creation_date: Option<DateTime<Utc>>, last_modified: DateTime<Utc>) -> Self
+10 -4
View File
@@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use async_trait::async_trait; use async_trait::async_trait;
use csscolorparser::Color;
use crate::item::SyncStatus; use crate::item::SyncStatus;
use crate::item::Item; use crate::item::Item;
@@ -21,7 +22,7 @@ pub trait CalDavSource<T: BaseCalendar> {
/// Returns the calendar matching the ID /// Returns the calendar matching the ID
async fn get_calendar(&self, id: &CalendarId) -> Option<Arc<Mutex<T>>>; async fn get_calendar(&self, id: &CalendarId) -> Option<Arc<Mutex<T>>>;
/// Create a calendar if it did not exist, and return it /// Create a calendar if it did not exist, and return it
async fn create_calendar(&mut self, id: CalendarId, name: String, supported_components: SupportedComponents) async fn create_calendar(&mut self, id: CalendarId, name: String, supported_components: SupportedComponents, color: Option<Color>)
-> Result<Arc<Mutex<T>>, Box<dyn Error>>; -> Result<Arc<Mutex<T>>, Box<dyn Error>>;
// Removing a calendar is not supported yet // Removing a calendar is not supported yet
@@ -39,6 +40,9 @@ pub trait BaseCalendar {
/// Returns the supported kinds of components for this calendar /// Returns the supported kinds of components for this calendar
fn supported_components(&self) -> crate::calendar::SupportedComponents; fn supported_components(&self) -> crate::calendar::SupportedComponents;
/// Returns the user-defined color of this calendar
fn color(&self) -> Option<&Color>;
/// Add an item into this calendar, and return its new sync status. /// Add an item into this calendar, and return its new sync status.
/// For local calendars, the sync status is not modified. /// For local calendars, the sync status is not modified.
/// For remote calendars, the sync status is updated by the server /// For remote calendars, the sync status is updated by the server
@@ -64,7 +68,7 @@ pub trait BaseCalendar {
#[async_trait] #[async_trait]
pub trait DavCalendar : BaseCalendar { pub trait DavCalendar : BaseCalendar {
/// Create a new calendar /// Create a new calendar
fn new(name: String, resource: Resource, supported_components: SupportedComponents) -> Self; fn new(name: String, resource: Resource, supported_components: SupportedComponents, color: Option<Color>) -> Self;
/// Get the IDs and the version tags of every item in this calendar /// Get the IDs and the version tags of every item in this calendar
async fn get_item_version_tags(&self) -> Result<HashMap<ItemId, VersionTag>, Box<dyn Error>>; async fn get_item_version_tags(&self) -> Result<HashMap<ItemId, VersionTag>, Box<dyn Error>>;
@@ -94,16 +98,18 @@ pub trait DavCalendar : BaseCalendar {
#[async_trait] #[async_trait]
pub trait CompleteCalendar : BaseCalendar { pub trait CompleteCalendar : BaseCalendar {
/// Create a new calendar /// Create a new calendar
fn new(name: String, id: CalendarId, supported_components: SupportedComponents) -> Self; fn new(name: String, id: CalendarId, supported_components: SupportedComponents, color: Option<Color>) -> Self;
/// Get the IDs of all current items in this calendar /// Get the IDs of all current items in this calendar
async fn get_item_ids(&self) -> Result<HashSet<ItemId>, Box<dyn Error>>; async fn get_item_ids(&self) -> Result<HashSet<ItemId>, Box<dyn Error>>;
/// Returns all items that this calendar contains /// Returns all items that this calendar contains
///
/// See [`crate::utils::comparison`] for helper functions that help sorting the results
async fn get_items(&self) -> Result<HashMap<ItemId, &Item>, Box<dyn Error>>; async fn get_items(&self) -> Result<HashMap<ItemId, &Item>, Box<dyn Error>>;
/// Returns a particular item /// Returns a particular item
async fn get_item_by_id_ref<'a>(&'a self, id: &ItemId) -> Option<&'a Item>; async fn get_item_by_id<'a>(&'a self, id: &ItemId) -> Option<&'a Item>;
/// Returns a particular item /// Returns a particular item
async fn get_item_by_id_mut<'a>(&'a mut self, id: &ItemId) -> Option<&'a mut Item>; async fn get_item_by_id_mut<'a>(&'a mut self, id: &ItemId) -> Option<&'a mut Item>;
View File
+3 -2
View File
@@ -50,14 +50,15 @@ async fn show_calendars() {
} }
#[tokio::test] #[tokio::test]
#[ignore]
async fn create_cal() { async fn create_cal() {
let _ = env_logger::builder().is_test(true).try_init(); let _ = env_logger::builder().is_test(true).try_init();
let mut client = Client::new(URL, USERNAME, PASSWORD).unwrap(); let mut client = Client::new(URL, USERNAME, PASSWORD).unwrap();
let id: Url = kitchen_fridge::settings::EXAMPLE_CREATED_CALENDAR_URL.parse().unwrap(); let id: Url = kitchen_fridge::settings::EXAMPLE_CREATED_CALENDAR_URL.parse().unwrap();
let name = "prout".into(); let name = "a created calendar".into();
let supported_components = SupportedComponents::TODO; let supported_components = SupportedComponents::TODO;
client.create_calendar(id, name, supported_components).await.unwrap(); client.create_calendar(id, name, supported_components, Some(csscolorparser::parse("gold").unwrap())).await.unwrap();
} }
#[tokio::test] #[tokio::test]
+5 -3
View File
@@ -22,12 +22,12 @@ use kitchen_fridge::traits::CompleteCalendar;
use kitchen_fridge::traits::DavCalendar; use kitchen_fridge::traits::DavCalendar;
use kitchen_fridge::cache::Cache; use kitchen_fridge::cache::Cache;
use kitchen_fridge::Item; use kitchen_fridge::Item;
use kitchen_fridge::ItemId; use kitchen_fridge::item::ItemId;
use kitchen_fridge::SyncStatus; use kitchen_fridge::item::SyncStatus;
use kitchen_fridge::Task; use kitchen_fridge::Task;
use kitchen_fridge::task::CompletionStatus; use kitchen_fridge::task::CompletionStatus;
use kitchen_fridge::calendar::cached_calendar::CachedCalendar; use kitchen_fridge::calendar::cached_calendar::CachedCalendar;
use kitchen_fridge::Provider; use kitchen_fridge::provider::Provider;
use kitchen_fridge::mock_behaviour::MockBehaviour; use kitchen_fridge::mock_behaviour::MockBehaviour;
pub enum LocatedState { pub enum LocatedState {
@@ -692,11 +692,13 @@ async fn get_or_insert_calendar(source: &mut Cache, id: &CalendarId)
None => { None => {
let new_name = format!("Test calendar for ID {}", id); let new_name = format!("Test calendar for ID {}", id);
let supported_components = SupportedComponents::TODO; let supported_components = SupportedComponents::TODO;
let color = csscolorparser::parse("#ff8000"); // TODO: we should rather have specific colors, depending on the calendars
source.create_calendar( source.create_calendar(
id.clone(), id.clone(),
new_name.to_string(), new_name.to_string(),
supported_components, supported_components,
None,
).await ).await
} }
} }
+1 -1
View File
@@ -340,7 +340,7 @@ async fn test_errors_in_regular_sync12() {
#[cfg(feature = "integration_tests")] #[cfg(feature = "integration_tests")]
use kitchen_fridge::{traits::CalDavSource, use kitchen_fridge::{traits::CalDavSource,
Provider, provider::Provider,
cache::Cache, cache::Cache,
calendar::cached_calendar::CachedCalendar, calendar::cached_calendar::CachedCalendar,
}; };