Added support for calendar colors

This commit is contained in:
daladim 2021-07-08 15:57:45 +02:00
parent 2b339a7aff
commit 17716575d8
11 changed files with 198 additions and 27 deletions

153
Cargo.lock generated
View file

@ -103,6 +103,16 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "encoding_rs"
version = "0.8.28"
@ -196,6 +206,17 @@ dependencies = [
"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]]
name = "getrandom"
version = "0.2.2"
@ -204,7 +225,7 @@ checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
dependencies = [
"cfg-if",
"libc",
"wasi",
"wasi 0.10.2+wasi-snapshot-preview1",
]
[[package]]
@ -382,6 +403,7 @@ dependencies = [
"async-trait",
"bitflags",
"chrono",
"csscolorparser",
"env_logger",
"ical",
"ics",
@ -568,6 +590,50 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "pin-project"
version = "1.0.5"
@ -612,6 +678,12 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
version = "1.0.24"
@ -639,6 +711,20 @@ dependencies = [
"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]]
name = "rand"
version = "0.8.3"
@ -646,9 +732,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
"rand_chacha 0.3.0",
"rand_core 0.6.2",
"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]]
@ -658,7 +754,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
"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]]
@ -667,7 +772,16 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
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]]
@ -676,7 +790,16 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
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]]
@ -841,6 +964,12 @@ dependencies = [
"serde",
]
[[package]]
name = "siphasher"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27"
[[package]]
name = "slab"
version = "0.4.2"
@ -877,7 +1006,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
"cfg-if",
"libc",
"rand",
"rand 0.8.3",
"redox_syscall",
"remove_dir_all",
"winapi",
@ -1082,7 +1211,7 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [
"getrandom",
"getrandom 0.2.2",
]
[[package]]
@ -1101,6 +1230,12 @@ dependencies = [
"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]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"

View file

@ -26,3 +26,4 @@ sanitize-filename = "0.3"
ical = "0.7"
ics = "0.5"
chrono = { version = "0.4", features = ["serde"] }
csscolorparser = { version = "0.5", features = ["serde"] }

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_name = "A brave new calendar".to_string();
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 {
println!("Unable to add calendar, maybe it exists already. We're not adding it after all.");
}

View file

@ -9,6 +9,7 @@ use std::ffi::OsStr;
use serde::{Deserialize, Serialize};
use async_trait::async_trait;
use csscolorparser::Color;
use crate::traits::CalDavSource;
use crate::traits::BaseCalendar;
@ -208,12 +209,12 @@ impl CalDavSource<CachedCalendar> for Cache {
self.get_calendar_sync(id)
}
async fn create_calendar(&mut self, id: CalendarId, name: String, supported_components: SupportedComponents) -> Result<Arc<Mutex<CachedCalendar>>, Box<dyn Error>> {
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);
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
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));
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
@ -244,12 +245,14 @@ mod tests {
Url::parse("https://caldav.com/shopping").unwrap(),
"My shopping list".to_string(),
SupportedComponents::TODO,
Some(csscolorparser::parse("lime").unwrap()),
).await.unwrap();
let bucket_list = cache.create_calendar(
Url::parse("https://caldav.com/bucket-list").unwrap(),
"My bucket list".to_string(),
SupportedComponents::TODO,
Some(csscolorparser::parse("#ff8000").unwrap()),
).await.unwrap();
{
@ -288,11 +291,12 @@ mod tests {
let cache_path = PathBuf::from(String::from("test_cache/sanity_tests"));
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(
Url::parse("https://caldav.com/shopping").unwrap(),
"My shopping list".to_string(),
SupportedComponents::TODO,
None,
).await;
assert!(second_addition_same_calendar.is_err());
}

View file

@ -3,6 +3,7 @@ use std::error::Error;
use serde::{Deserialize, Serialize};
use async_trait::async_trait;
use csscolorparser::Color;
use crate::item::SyncStatus;
use crate::traits::{BaseCalendar, CompleteCalendar};
@ -24,6 +25,7 @@ pub struct CachedCalendar {
name: String,
id: CalendarId,
supported_components: SupportedComponents,
color: Option<Color>,
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
#[serde(skip)]
mock_behaviour: Option<Arc<Mutex<MockBehaviour>>>,
@ -85,7 +87,9 @@ impl CachedCalendar {
pub async fn has_same_observable_content_as(&self, other: &CachedCalendar) -> Result<bool, Box<dyn Error>> {
if self.name != other.name
|| 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");
return Ok(false);
}
@ -156,6 +160,10 @@ impl BaseCalendar for CachedCalendar {
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>> {
if self.items.contains_key(item.id()) {
return Err(format!("Item {:?} cannot be added, it exists already", item.id()).into());
@ -181,9 +189,9 @@ impl BaseCalendar for CachedCalendar {
#[async_trait]
impl CompleteCalendar for CachedCalendar {
fn new(name: String, id: CalendarId, supported_components: SupportedComponents) -> Self {
fn new(name: String, id: CalendarId, supported_components: SupportedComponents, color: Option<Color>) -> Self {
Self {
name, id, supported_components,
name, id, supported_components, color,
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
mock_behaviour: None,
items: HashMap::new(),
@ -253,8 +261,8 @@ use crate::{item::VersionTag,
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
#[async_trait]
impl DavCalendar for CachedCalendar {
fn new(name: String, resource: Resource, supported_components: SupportedComponents) -> Self {
crate::traits::CompleteCalendar::new(name, resource.url().clone(), supported_components)
fn new(name: String, resource: Resource, supported_components: SupportedComponents, color: Option<Color>) -> Self {
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>> {

View file

@ -4,6 +4,7 @@ use std::sync::Mutex;
use async_trait::async_trait;
use reqwest::{header::CONTENT_TYPE, header::CONTENT_LENGTH};
use csscolorparser::Color;
use crate::traits::BaseCalendar;
use crate::traits::DavCalendar;
@ -37,6 +38,7 @@ pub struct RemoteCalendar {
name: String,
resource: Resource,
supported_components: SupportedComponents,
color: Option<Color>,
cached_version_tags: Mutex<Option<HashMap<ItemId, VersionTag>>>,
}
@ -48,6 +50,9 @@ impl BaseCalendar for RemoteCalendar {
fn supported_components(&self) -> crate::calendar::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>> {
let ical_text = crate::ical::build_from(&item)?;
@ -114,9 +119,9 @@ impl BaseCalendar for RemoteCalendar {
#[async_trait]
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 {
name, resource, supported_components,
name, resource, supported_components, color,
cached_version_tags: Mutex::new(None),
}
}

View file

@ -10,6 +10,7 @@ use reqwest::{Method, StatusCode};
use reqwest::header::CONTENT_TYPE;
use minidom::Element;
use url::Url;
use csscolorparser::Color;
use crate::resource::Resource;
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:prop>
<d:displayname />
<E:calendar-color xmlns:E="http://apple.com/ns/ical/"/>
<d:resourcetype />
<c:supported-calendar-component-set />
</d:prop>
@ -205,7 +207,14 @@ impl Client {
},
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());
calendars.insert(this_calendar.id().clone(), Arc::new(Mutex::new(this_calendar)));
}
@ -243,7 +252,7 @@ impl CalDavSource<RemoteCalendar> for Client {
.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?;
match self.cached_replies.lock().unwrap().calendars.as_ref() {

View file

@ -390,10 +390,12 @@ where
let src = needle.lock().unwrap();
let name = src.name().to_string();
let supported_comps = src.supported_components();
let color = src.color();
if let Err(err) = haystack.create_calendar(
cal_id.clone(),
name,
supported_comps,
color.cloned(),
).await{
return Err(err);
}

View file

@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use csscolorparser::Color;
use crate::item::SyncStatus;
use crate::item::Item;
@ -21,7 +22,7 @@ pub trait CalDavSource<T: BaseCalendar> {
/// Returns the calendar matching the ID
async fn get_calendar(&self, id: &CalendarId) -> Option<Arc<Mutex<T>>>;
/// 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>>;
// Removing a calendar is not supported yet
@ -39,6 +40,9 @@ pub trait BaseCalendar {
/// Returns the supported kinds of components for this calendar
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.
/// For local calendars, the sync status is not modified.
/// For remote calendars, the sync status is updated by the server
@ -64,7 +68,7 @@ pub trait BaseCalendar {
#[async_trait]
pub trait DavCalendar : BaseCalendar {
/// 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
async fn get_item_version_tags(&self) -> Result<HashMap<ItemId, VersionTag>, Box<dyn Error>>;
@ -94,7 +98,7 @@ pub trait DavCalendar : BaseCalendar {
#[async_trait]
pub trait CompleteCalendar : BaseCalendar {
/// 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
async fn get_item_ids(&self) -> Result<HashSet<ItemId>, Box<dyn Error>>;

View file

@ -50,14 +50,15 @@ async fn show_calendars() {
}
#[tokio::test]
#[ignore]
async fn create_cal() {
let _ = env_logger::builder().is_test(true).try_init();
let mut client = Client::new(URL, USERNAME, PASSWORD).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;
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]

View file

@ -692,11 +692,13 @@ async fn get_or_insert_calendar(source: &mut Cache, id: &CalendarId)
None => {
let new_name = format!("Test calendar for ID {}", id);
let supported_components = SupportedComponents::TODO;
let color = csscolorparser::parse("#ff8000"); // TODO: we should rather have specific colors, depending on the calendars
source.create_calendar(
id.clone(),
new_name.to_string(),
supported_components,
None,
).await
}
}