diff --git a/Cargo.lock b/Cargo.lock index 73a01a2..46c9a53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3194,7 +3194,6 @@ dependencies = [ "chrono-tz", "derive_more", "ical", - "lazy_static", "regex", "rrule", "rustical_xml", @@ -3234,7 +3233,6 @@ dependencies = [ "headers", "http", "ical", - "lazy_static", "regex", "rrule", "rstest", diff --git a/Cargo.toml b/Cargo.toml index e6b2c9b..b0a916c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,6 @@ pbkdf2 = { version = "0.12", features = ["simple"] } rand_core = { version = "0.9", features = ["std"] } chrono = { version = "0.4", features = ["serde"] } regex = "1.10" -lazy_static = "1.5" rstest = "0.26" rstest_reuse = "0.7" sha2 = "0.10" diff --git a/crates/caldav/src/calendar/methods/get.rs b/crates/caldav/src/calendar/methods/get.rs index ef5d955..2cbe928 100644 --- a/crates/caldav/src/calendar/methods/get.rs +++ b/crates/caldav/src/calendar/methods/get.rs @@ -32,7 +32,6 @@ pub async fn route_get( return Err(crate::Error::Unauthorized); } - let mut timezones = HashMap::new(); let mut vtimezones = HashMap::new(); let objects = cal_store.get_objects(&principal, &calendar_id).await?; @@ -64,31 +63,23 @@ pub async fn route_get( for object in &objects { vtimezones.extend(object.get_vtimezones()); match object.get_data() { - CalendarObjectComponent::Event( - EventObject { - event, - timezones: object_timezones, - .. - }, - overrides, - ) => { - timezones.extend(object_timezones); + CalendarObjectComponent::Event(EventObject { event, .. }, overrides) => { ical_calendar_builder = ical_calendar_builder.add_event(event.clone()); - for _override in overrides { + for ev_override in overrides { ical_calendar_builder = - ical_calendar_builder.add_event(_override.event.clone()); + ical_calendar_builder.add_event(ev_override.event.clone()); } } CalendarObjectComponent::Todo(todo, overrides) => { ical_calendar_builder = ical_calendar_builder.add_todo(todo.clone()); - for _override in overrides { - ical_calendar_builder = ical_calendar_builder.add_todo(_override.clone()); + for ev_override in overrides { + ical_calendar_builder = ical_calendar_builder.add_todo(ev_override.clone()); } } CalendarObjectComponent::Journal(journal, overrides) => { ical_calendar_builder = ical_calendar_builder.add_journal(journal.clone()); - for _override in overrides { - ical_calendar_builder = ical_calendar_builder.add_journal(_override.clone()); + for ev_override in overrides { + ical_calendar_builder = ical_calendar_builder.add_journal(ev_override.clone()); } } } diff --git a/crates/caldav/src/calendar/methods/mkcalendar.rs b/crates/caldav/src/calendar/methods/mkcalendar.rs index 0ec65c9..2d65e0c 100644 --- a/crates/caldav/src/calendar/methods/mkcalendar.rs +++ b/crates/caldav/src/calendar/methods/mkcalendar.rs @@ -89,17 +89,12 @@ pub async fn route_mkcalendar( // TODO: Proper error (calendar-timezone precondition) let calendar = IcalParser::new(tz.as_bytes()) .next() - .ok_or(rustical_dav::Error::BadRequest( - "No timezone data provided".to_owned(), - ))? + .ok_or_else(|| rustical_dav::Error::BadRequest("No timezone data provided".to_owned()))? .map_err(|_| rustical_dav::Error::BadRequest("No timezone data provided".to_owned()))?; - let timezone = calendar - .timezones - .first() - .ok_or(rustical_dav::Error::BadRequest( - "No timezone data provided".to_owned(), - ))?; + let timezone = calendar.timezones.first().ok_or_else(|| { + rustical_dav::Error::BadRequest("No timezone data provided".to_owned()) + })?; let timezone: chrono_tz::Tz = timezone .try_into() .map_err(|_| rustical_dav::Error::BadRequest("No timezone data provided".to_owned()))?; @@ -123,14 +118,16 @@ pub async fn route_mkcalendar( synctoken: 0, subscription_url: request.source.map(|href| href.href), push_topic: uuid::Uuid::new_v4().to_string(), - components: request - .supported_calendar_component_set - .map(Into::into) - .unwrap_or(vec![ - CalendarObjectType::Event, - CalendarObjectType::Todo, - CalendarObjectType::Journal, - ]), + components: request.supported_calendar_component_set.map_or_else( + || { + vec![ + CalendarObjectType::Event, + CalendarObjectType::Todo, + CalendarObjectType::Journal, + ] + }, + Into::into, + ), }; cal_store.insert_calendar(calendar).await?; diff --git a/crates/caldav/src/calendar/methods/post.rs b/crates/caldav/src/calendar/methods/post.rs index 014d10a..d27e829 100644 --- a/crates/caldav/src/calendar/methods/post.rs +++ b/crates/caldav/src/calendar/methods/post.rs @@ -53,7 +53,8 @@ pub async fn route_post( push_resource: request .subscription .web_push_subscription - .push_resource.clone(), + .push_resource + .clone(), topic: calendar_resource.cal.push_topic, expiration: expires.naive_local(), public_key: request diff --git a/crates/caldav/src/calendar/methods/report/calendar_multiget.rs b/crates/caldav/src/calendar/methods/report/calendar_multiget.rs index dd5af84..ae5277a 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_multiget.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_multiget.rs @@ -36,11 +36,9 @@ pub async fn get_objects_calendar_multiget( } } else { not_found.push(href.to_owned()); - continue; } } else { not_found.push(href.to_owned()); - continue; } } diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/elements.rs b/crates/caldav/src/calendar/methods/report/calendar_query/elements.rs index 64ce9c4..37a8648 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/elements.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/elements.rs @@ -36,7 +36,7 @@ pub struct TextMatchElement { pub(crate) negate_condition: Option, } -#[derive(XmlDeserialize, Clone, Debug, PartialEq)] +#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq)] #[allow(dead_code)] // https://www.rfc-editor.org/rfc/rfc4791#section-9.7.2 pub struct PropFilterElement { @@ -76,7 +76,7 @@ impl CompFilterElement { let comp_vcal = self.name == "VCALENDAR"; match (self.is_not_defined, comp_vcal) { // Client wants VCALENDAR to not exist but we are a VCALENDAR - (Some(()), true) => return false, + (Some(()), true) | // Client is asking for something different than a vcalendar (None, false) => return false, _ => {} @@ -106,7 +106,7 @@ impl CompFilterElement { let comp_name_matches = self.name == cal_object.get_component_name(); match (self.is_not_defined, comp_name_matches) { // Client wants VCALENDAR to not exist but we are a VCALENDAR - (Some(()), true) => return false, + (Some(()), true) | // Client is asking for something different than a vcalendar (None, false) => return false, _ => {} @@ -164,7 +164,7 @@ impl From<&FilterElement> for CalendarQuery { }; } } - Default::default() + Self::default() } } @@ -184,10 +184,6 @@ pub struct CalendarQueryRequest { impl From<&CalendarQueryRequest> for CalendarQuery { fn from(value: &CalendarQueryRequest) -> Self { - value - .filter - .as_ref() - .map(Self::from) - .unwrap_or_default() + value.filter.as_ref().map(Self::from).unwrap_or_default() } } diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs b/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs index 34d4835..7490fc7 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs @@ -33,7 +33,7 @@ mod tests { PropFilterElement, TextMatchElement, }, }, - calendar_object::{CalendarObjectPropName, CalendarObjectPropWrapperName}, + calendar_object::{CalendarData, CalendarObjectPropName, CalendarObjectPropWrapperName}, }; #[test] @@ -76,7 +76,7 @@ mod tests { CalendarObjectPropName::Getetag, ), CalendarObjectPropWrapperName::CalendarObject( - CalendarObjectPropName::CalendarData(Default::default()) + CalendarObjectPropName::CalendarData(CalendarData::default()) ), ], vec![] @@ -115,6 +115,6 @@ mod tests { timezone: None, timezone_id: None } - ) + ); } } diff --git a/crates/caldav/src/calendar/methods/report/mod.rs b/crates/caldav/src/calendar/methods/report/mod.rs index 5952aea..2703f01 100644 --- a/crates/caldav/src/calendar/methods/report/mod.rs +++ b/crates/caldav/src/calendar/methods/report/mod.rs @@ -43,9 +43,9 @@ pub(crate) enum ReportRequest { impl ReportRequest { const fn props(&self) -> &PropfindType { match &self { - Self::CalendarMultiget(CalendarMultigetRequest { prop, .. }) => prop, - Self::CalendarQuery(CalendarQueryRequest { prop, .. }) => prop, - Self::SyncCollection(SyncCollectionRequest { prop, .. }) => prop, + Self::CalendarMultiget(CalendarMultigetRequest { prop, .. }) + | Self::CalendarQuery(CalendarQueryRequest { prop, .. }) + | Self::SyncCollection(SyncCollectionRequest { prop, .. }) => prop, } } } @@ -184,7 +184,7 @@ mod tests { "/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned() ] }) - ) + ); } #[test] @@ -241,7 +241,7 @@ mod tests { timezone: None, timezone_id: None, }) - ) + ); } #[test] @@ -269,6 +269,6 @@ mod tests { "/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned() ] }) - ) + ); } } diff --git a/crates/caldav/src/calendar/resource.rs b/crates/caldav/src/calendar/resource.rs index 785ca77..bec5de3 100644 --- a/crates/caldav/src/calendar/resource.rs +++ b/crates/caldav/src/calendar/resource.rs @@ -18,7 +18,7 @@ use rustical_xml::{EnumVariants, PropName}; use rustical_xml::{XmlDeserialize, XmlSerialize}; use serde::Deserialize; -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, EnumVariants, PropName)] #[xml(unit_variants_ident = "CalendarPropName")] pub enum CalendarProp { // CalDAV (RFC 4791) @@ -54,7 +54,7 @@ pub enum CalendarProp { MaxDateTime(String), } -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, EnumVariants, PropName)] #[xml(unit_variants_ident = "CalendarPropWrapperName", untagged)] pub enum CalendarPropWrapper { Calendar(CalendarProp), @@ -135,7 +135,9 @@ impl Resource for CalendarResource { } CalendarPropName::CalendarTimezone => { CalendarProp::CalendarTimezone(self.cal.timezone_id.as_ref().and_then(|tzid| { - vtimezones_rs::VTIMEZONES.get(tzid).map(|tz| (*tz).to_string()) + vtimezones_rs::VTIMEZONES + .get(tzid) + .map(|tz| (*tz).to_string()) })) } // chrono_tz uses the IANA database @@ -154,13 +156,13 @@ impl Resource for CalendarResource { CalendarPropName::SupportedCalendarData => { CalendarProp::SupportedCalendarData(SupportedCalendarData::default()) } - CalendarPropName::MaxResourceSize => CalendarProp::MaxResourceSize(10000000), + CalendarPropName::MaxResourceSize => CalendarProp::MaxResourceSize(10_000_000), CalendarPropName::SupportedReportSet => { CalendarProp::SupportedReportSet(SupportedReportSet::all()) } - CalendarPropName::Source => CalendarProp::Source( - self.cal.subscription_url.clone().map(HrefElement::from), - ), + CalendarPropName::Source => { + CalendarProp::Source(self.cal.subscription_url.clone().map(HrefElement::from)) + } CalendarPropName::MinDateTime => { CalendarProp::MinDateTime(CalDateTime::from(DateTime::::MIN_UTC).format()) } @@ -199,22 +201,20 @@ impl Resource for CalendarResource { // TODO: Proper error (calendar-timezone precondition) let calendar = IcalParser::new(tz.as_bytes()) .next() - .ok_or(rustical_dav::Error::BadRequest( - "No timezone data provided".to_owned(), - ))? + .ok_or_else(|| { + rustical_dav::Error::BadRequest( + "No timezone data provided".to_owned(), + ) + })? .map_err(|_| { rustical_dav::Error::BadRequest( "No timezone data provided".to_owned(), ) })?; - let timezone = - calendar - .timezones - .first() - .ok_or(rustical_dav::Error::BadRequest( - "No timezone data provided".to_owned(), - ))?; + let timezone = calendar.timezones.first().ok_or_else(|| { + rustical_dav::Error::BadRequest("No timezone data provided".to_owned()) + })?; let timezone: chrono_tz::Tz = timezone.try_into().map_err(|_| { rustical_dav::Error::BadRequest("No timezone data provided".to_owned()) })?; @@ -223,7 +223,6 @@ impl Resource for CalendarResource { } Ok(()) } - CalendarProp::TimezoneServiceSet(_) => Err(rustical_dav::Error::PropReadOnly), CalendarProp::CalendarTimezoneId(timezone_id) => { if let Some(tzid) = &timezone_id && !vtimezones_rs::VTIMEZONES.contains_key(tzid) @@ -243,13 +242,13 @@ impl Resource for CalendarResource { self.cal.components = comp_set.into(); Ok(()) } - CalendarProp::SupportedCalendarData(_) => Err(rustical_dav::Error::PropReadOnly), - CalendarProp::MaxResourceSize(_) => Err(rustical_dav::Error::PropReadOnly), - CalendarProp::SupportedReportSet(_) => Err(rustical_dav::Error::PropReadOnly), - // Converting between a calendar subscription calendar and a normal one would be weird - CalendarProp::Source(_) => Err(rustical_dav::Error::PropReadOnly), - CalendarProp::MinDateTime(_) => Err(rustical_dav::Error::PropReadOnly), - CalendarProp::MaxDateTime(_) => Err(rustical_dav::Error::PropReadOnly), + CalendarProp::TimezoneServiceSet(_) + | CalendarProp::SupportedCalendarData(_) + | CalendarProp::MaxResourceSize(_) + | CalendarProp::SupportedReportSet(_) + | CalendarProp::Source(_) + | CalendarProp::MinDateTime(_) + | CalendarProp::MaxDateTime(_) => Err(rustical_dav::Error::PropReadOnly), }, CalendarPropWrapper::SyncToken(prop) => SyncTokenExtension::set_prop(self, prop), CalendarPropWrapper::DavPush(prop) => DavPushExtension::set_prop(self, prop), @@ -275,7 +274,6 @@ impl Resource for CalendarResource { self.cal.timezone_id = None; Ok(()) } - CalendarPropName::TimezoneServiceSet => Err(rustical_dav::Error::PropReadOnly), CalendarPropName::CalendarOrder => { self.cal.meta.order = 0; Ok(()) @@ -283,13 +281,13 @@ impl Resource for CalendarResource { CalendarPropName::SupportedCalendarComponentSet => { Err(rustical_dav::Error::PropReadOnly) } - CalendarPropName::SupportedCalendarData => Err(rustical_dav::Error::PropReadOnly), - CalendarPropName::MaxResourceSize => Err(rustical_dav::Error::PropReadOnly), - CalendarPropName::SupportedReportSet => Err(rustical_dav::Error::PropReadOnly), - // Converting a calendar subscription calendar into a normal one would be weird - CalendarPropName::Source => Err(rustical_dav::Error::PropReadOnly), - CalendarPropName::MinDateTime => Err(rustical_dav::Error::PropReadOnly), - CalendarPropName::MaxDateTime => Err(rustical_dav::Error::PropReadOnly), + CalendarPropName::TimezoneServiceSet + | CalendarPropName::SupportedCalendarData + | CalendarPropName::MaxResourceSize + | CalendarPropName::SupportedReportSet + | CalendarPropName::Source + | CalendarPropName::MinDateTime + | CalendarPropName::MaxDateTime => Err(rustical_dav::Error::PropReadOnly), }, CalendarPropWrapperName::SyncToken(prop) => SyncTokenExtension::remove_prop(self, prop), CalendarPropWrapperName::DavPush(prop) => DavPushExtension::remove_prop(self, prop), diff --git a/crates/caldav/src/calendar_object/methods.rs b/crates/caldav/src/calendar_object/methods.rs index 1053076..510b1ed 100644 --- a/crates/caldav/src/calendar_object/methods.rs +++ b/crates/caldav/src/calendar_object/methods.rs @@ -78,7 +78,7 @@ pub async fn put_event( true }; - let object = if let Ok(obj) = CalendarObject::from_ics(body.clone()) { obj } else { + let Ok(object) = CalendarObject::from_ics(body.clone()) else { debug!("invalid calendar data:\n{body}"); return Err(Error::PreconditionFailed(Precondition::ValidCalendarData)); }; diff --git a/crates/caldav/src/calendar_object/prop.rs b/crates/caldav/src/calendar_object/prop.rs index 0922e6d..bea5c68 100644 --- a/crates/caldav/src/calendar_object/prop.rs +++ b/crates/caldav/src/calendar_object/prop.rs @@ -17,7 +17,7 @@ pub enum CalendarObjectProp { CalendarData(String), } -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, EnumVariants, PropName)] #[xml(unit_variants_ident = "CalendarObjectPropWrapperName", untagged)] pub enum CalendarObjectPropWrapper { CalendarObject(CalendarObjectProp), diff --git a/crates/caldav/src/calendar_object/resource.rs b/crates/caldav/src/calendar_object/resource.rs index cd3690f..15af050 100644 --- a/crates/caldav/src/calendar_object/resource.rs +++ b/crates/caldav/src/calendar_object/resource.rs @@ -1,4 +1,7 @@ -use super::prop::{CalendarObjectPropWrapper, CalendarObjectPropWrapperName, CalendarObjectPropName, CalendarObjectProp, CalendarData}; +use super::prop::{ + CalendarData, CalendarObjectProp, CalendarObjectPropName, CalendarObjectPropWrapper, + CalendarObjectPropWrapperName, +}; use crate::Error; use derive_more::derive::{From, Into}; use rustical_dav::{ diff --git a/crates/caldav/src/calendar_object/service.rs b/crates/caldav/src/calendar_object/service.rs index f6527c8..8a63e12 100644 --- a/crates/caldav/src/calendar_object/service.rs +++ b/crates/caldav/src/calendar_object/service.rs @@ -106,9 +106,8 @@ where D: Deserializer<'de>, { let name: String = Deserialize::deserialize(deserializer)?; - if let Some(object_id) = name.strip_suffix(".ics") { - Ok(object_id.to_owned()) - } else { - Err(serde::de::Error::custom("Missing .ics extension")) - } + name.strip_suffix(".ics").map_or_else( + || Err(serde::de::Error::custom("Missing .ics extension")), + |object_id| Ok(object_id.to_owned()), + ) } diff --git a/crates/caldav/src/error.rs b/crates/caldav/src/error.rs index a3a56c5..790646f 100644 --- a/crates/caldav/src/error.rs +++ b/crates/caldav/src/error.rs @@ -62,7 +62,8 @@ pub enum Error { } impl Error { - #[must_use] pub fn status_code(&self) -> StatusCode { + #[must_use] + pub fn status_code(&self) -> StatusCode { match self { Self::StoreError(err) => match err { rustical_store::Error::NotFound => StatusCode::NOT_FOUND, @@ -70,12 +71,11 @@ impl Error { rustical_store::Error::ReadOnly => StatusCode::FORBIDDEN, _ => StatusCode::INTERNAL_SERVER_ERROR, }, - Self::ChronoParseError(_) => StatusCode::INTERNAL_SERVER_ERROR, Self::DavError(err) => StatusCode::try_from(err.status_code().as_u16()) .expect("Just converting between versions"), Self::Unauthorized => StatusCode::UNAUTHORIZED, Self::XmlDecodeError(_) => StatusCode::BAD_REQUEST, - Self::NotImplemented => StatusCode::INTERNAL_SERVER_ERROR, + Self::ChronoParseError(_) | Self::NotImplemented => StatusCode::INTERNAL_SERVER_ERROR, Self::NotFound => StatusCode::NOT_FOUND, Self::IcalError(err) => err.status_code(), Self::PreconditionFailed(_err) => StatusCode::PRECONDITION_FAILED, diff --git a/crates/caldav/src/lib.rs b/crates/caldav/src/lib.rs index 9db5f27..991dfc8 100644 --- a/crates/caldav/src/lib.rs +++ b/crates/caldav/src/lib.rs @@ -1,4 +1,5 @@ #![warn(clippy::all, clippy::pedantic, clippy::nursery)] +#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] use axum::{Extension, Router}; use derive_more::Constructor; use principal::PrincipalResourceService; diff --git a/crates/caldav/src/principal/prop.rs b/crates/caldav/src/principal/prop.rs index 6285d8b..5052819 100644 --- a/crates/caldav/src/principal/prop.rs +++ b/crates/caldav/src/principal/prop.rs @@ -37,7 +37,7 @@ pub enum PrincipalProp { #[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone)] pub struct CalendarHomeSet(#[xml(ty = "untagged", flatten)] pub Vec); -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, EnumVariants, PropName)] #[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)] pub enum PrincipalPropWrapper { Principal(PrincipalProp), diff --git a/crates/carddav/src/address_object/prop.rs b/crates/carddav/src/address_object/prop.rs index 0a33f5e..6451902 100644 --- a/crates/carddav/src/address_object/prop.rs +++ b/crates/carddav/src/address_object/prop.rs @@ -15,7 +15,7 @@ pub enum AddressObjectProp { AddressData(String), } -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, EnumVariants, PropName)] #[xml(unit_variants_ident = "AddressObjectPropWrapperName", untagged)] pub enum AddressObjectPropWrapper { AddressObject(AddressObjectProp), diff --git a/crates/carddav/src/address_object/service.rs b/crates/carddav/src/address_object/service.rs index e23725b..1c6efdc 100644 --- a/crates/carddav/src/address_object/service.rs +++ b/crates/carddav/src/address_object/service.rs @@ -98,9 +98,8 @@ where D: Deserializer<'de>, { let name: String = Deserialize::deserialize(deserializer)?; - if let Some(object_id) = name.strip_suffix(".vcf") { - Ok(object_id.to_owned()) - } else { - Err(serde::de::Error::custom("Missing .vcf extension")) - } + name.strip_suffix(".vcf").map_or_else( + || Err(serde::de::Error::custom("Missing .vcf extension")), + |object_id| Ok(object_id.to_owned()), + ) } diff --git a/crates/carddav/src/addressbook/methods/mkcol.rs b/crates/carddav/src/addressbook/methods/mkcol.rs index 6fdc9af..8a64173 100644 --- a/crates/carddav/src/addressbook/methods/mkcol.rs +++ b/crates/carddav/src/addressbook/methods/mkcol.rs @@ -127,6 +127,6 @@ mod tests { } } } - ) + ); } } diff --git a/crates/carddav/src/addressbook/methods/post.rs b/crates/carddav/src/addressbook/methods/post.rs index 7b89a5e..96ef1e3 100644 --- a/crates/carddav/src/addressbook/methods/post.rs +++ b/crates/carddav/src/addressbook/methods/post.rs @@ -49,7 +49,8 @@ pub async fn route_post( push_resource: request .subscription .web_push_subscription - .push_resource.clone(), + .push_resource + .clone(), topic: addressbook_resource.0.push_topic, expiration: expires.naive_local(), public_key: request diff --git a/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs b/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs index ae41dcb..16e46ce 100644 --- a/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs +++ b/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs @@ -47,11 +47,9 @@ pub async fn get_objects_addressbook_multiget( } } else { not_found.push(href.to_owned()); - continue; } } else { not_found.push(href.to_owned()); - continue; } } diff --git a/crates/carddav/src/addressbook/methods/report/mod.rs b/crates/carddav/src/addressbook/methods/report/mod.rs index 283f806..feb6aea 100644 --- a/crates/carddav/src/addressbook/methods/report/mod.rs +++ b/crates/carddav/src/addressbook/methods/report/mod.rs @@ -28,8 +28,8 @@ pub(crate) enum ReportRequest { impl ReportRequest { const fn props(&self) -> &PropfindType { match self { - Self::AddressbookMultiget(AddressbookMultigetRequest { prop, .. }) => prop, - Self::SyncCollection(SyncCollectionRequest { prop, .. }) => prop, + Self::AddressbookMultiget(AddressbookMultigetRequest { prop, .. }) + | Self::SyncCollection(SyncCollectionRequest { prop, .. }) => prop, } } } @@ -101,7 +101,7 @@ mod tests { assert_eq!( report_request, ReportRequest::SyncCollection(SyncCollectionRequest { - sync_token: "".to_owned(), + sync_token: String::new(), sync_level: SyncLevel::One, prop: rustical_dav::xml::PropfindType::Prop(PropElement( vec![AddressObjectPropWrapperName::AddressObject( @@ -111,7 +111,7 @@ mod tests { )), limit: None }) - ) + ); } #[test] @@ -142,6 +142,6 @@ mod tests { "/carddav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned() ] }) - ) + ); } } diff --git a/crates/carddav/src/addressbook/prop.rs b/crates/carddav/src/addressbook/prop.rs index def843f..48165a2 100644 --- a/crates/carddav/src/addressbook/prop.rs +++ b/crates/carddav/src/addressbook/prop.rs @@ -6,7 +6,7 @@ use rustical_dav_push::DavPushExtensionProp; use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize}; use strum_macros::VariantArray; -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, EnumVariants, PropName)] #[xml(unit_variants_ident = "AddressbookPropName")] pub enum AddressbookProp { // CardDAV (RFC 6352) @@ -20,7 +20,7 @@ pub enum AddressbookProp { MaxResourceSize(i64), } -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, EnumVariants, PropName)] #[xml(unit_variants_ident = "AddressbookPropWrapperName", untagged)] pub enum AddressbookPropWrapper { Addressbook(AddressbookProp), diff --git a/crates/carddav/src/addressbook/resource.rs b/crates/carddav/src/addressbook/resource.rs index d62904b..fcaea04 100644 --- a/crates/carddav/src/addressbook/resource.rs +++ b/crates/carddav/src/addressbook/resource.rs @@ -59,7 +59,7 @@ impl Resource for AddressbookResource { AddressbookPropWrapperName::Addressbook(prop) => { AddressbookPropWrapper::Addressbook(match prop { AddressbookPropName::MaxResourceSize => { - AddressbookProp::MaxResourceSize(10000000) + AddressbookProp::MaxResourceSize(10_000_000) } AddressbookPropName::SupportedReportSet => { AddressbookProp::SupportedReportSet(SupportedReportSet::all()) @@ -92,9 +92,11 @@ impl Resource for AddressbookResource { self.0.description = description; Ok(()) } - AddressbookProp::MaxResourceSize(_) => Err(rustical_dav::Error::PropReadOnly), - AddressbookProp::SupportedReportSet(_) => Err(rustical_dav::Error::PropReadOnly), - AddressbookProp::SupportedAddressData(_) => Err(rustical_dav::Error::PropReadOnly), + AddressbookProp::MaxResourceSize(_) + | AddressbookProp::SupportedReportSet(_) + | AddressbookProp::SupportedAddressData(_) => { + Err(rustical_dav::Error::PropReadOnly) + } }, AddressbookPropWrapper::SyncToken(prop) => SyncTokenExtension::set_prop(self, prop), AddressbookPropWrapper::DavPush(prop) => DavPushExtension::set_prop(self, prop), @@ -112,9 +114,11 @@ impl Resource for AddressbookResource { self.0.description = None; Ok(()) } - AddressbookPropName::MaxResourceSize => Err(rustical_dav::Error::PropReadOnly), - AddressbookPropName::SupportedReportSet => Err(rustical_dav::Error::PropReadOnly), - AddressbookPropName::SupportedAddressData => Err(rustical_dav::Error::PropReadOnly), + AddressbookPropName::MaxResourceSize + | AddressbookPropName::SupportedReportSet + | AddressbookPropName::SupportedAddressData => { + Err(rustical_dav::Error::PropReadOnly) + } }, AddressbookPropWrapperName::SyncToken(prop) => { SyncTokenExtension::remove_prop(self, prop) diff --git a/crates/carddav/src/error.rs b/crates/carddav/src/error.rs index a79df8f..fff3ffc 100644 --- a/crates/carddav/src/error.rs +++ b/crates/carddav/src/error.rs @@ -30,7 +30,8 @@ pub enum Error { } impl Error { - #[must_use] pub const fn status_code(&self) -> StatusCode { + #[must_use] + pub const fn status_code(&self) -> StatusCode { match self { Self::StoreError(err) => match err { rustical_store::Error::NotFound => StatusCode::NOT_FOUND, @@ -38,11 +39,10 @@ impl Error { rustical_store::Error::ReadOnly => StatusCode::FORBIDDEN, _ => StatusCode::INTERNAL_SERVER_ERROR, }, - Self::ChronoParseError(_) => StatusCode::INTERNAL_SERVER_ERROR, Self::DavError(err) => err.status_code(), Self::Unauthorized => StatusCode::UNAUTHORIZED, Self::XmlDecodeError(_) => StatusCode::BAD_REQUEST, - Self::NotImplemented => StatusCode::INTERNAL_SERVER_ERROR, + Self::ChronoParseError(_) | Self::NotImplemented => StatusCode::INTERNAL_SERVER_ERROR, Self::NotFound => StatusCode::NOT_FOUND, Self::IcalError(err) => err.status_code(), } diff --git a/crates/carddav/src/lib.rs b/crates/carddav/src/lib.rs index f9638c2..9c013be 100644 --- a/crates/carddav/src/lib.rs +++ b/crates/carddav/src/lib.rs @@ -1,4 +1,5 @@ #![warn(clippy::all, clippy::pedantic, clippy::nursery)] +#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] use axum::response::Redirect; use axum::routing::any; use axum::{Extension, Router}; @@ -37,20 +38,15 @@ pub fn carddav_router, subscription_store: Arc, ) -> Router { - let principal_service = PrincipalResourceService::new( - store, - auth_provider.clone(), - subscription_store, - ); + let principal_service = + PrincipalResourceService::new(store, auth_provider.clone(), subscription_store); Router::new() .nest( prefix, - RootResourceService::<_, Principal, CardDavPrincipalUri>::new( - principal_service, - ) - .axum_router() - .layer(AuthenticationLayer::new(auth_provider)) - .layer(Extension(CardDavPrincipalUri(prefix))), + RootResourceService::<_, Principal, CardDavPrincipalUri>::new(principal_service) + .axum_router() + .layer(AuthenticationLayer::new(auth_provider)) + .layer(Extension(CardDavPrincipalUri(prefix))), ) .route( "/.well-known/carddav", diff --git a/crates/carddav/src/principal/prop.rs b/crates/carddav/src/principal/prop.rs index 2689589..2d56992 100644 --- a/crates/carddav/src/principal/prop.rs +++ b/crates/carddav/src/principal/prop.rs @@ -30,7 +30,7 @@ pub enum PrincipalProp { #[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone)] pub struct AddressbookHomeSet(#[xml(ty = "untagged", flatten)] pub Vec); -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, EnumVariants, PropName)] #[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)] pub enum PrincipalPropWrapper { Principal(PrincipalProp), diff --git a/crates/dav/src/error.rs b/crates/dav/src/error.rs index ab614cd..1d0e386 100644 --- a/crates/dav/src/error.rs +++ b/crates/dav/src/error.rs @@ -35,9 +35,9 @@ pub enum Error { } impl Error { - #[must_use] pub const fn status_code(&self) -> StatusCode { + #[must_use] + pub const fn status_code(&self) -> StatusCode { match self { - Self::InternalError => StatusCode::INTERNAL_SERVER_ERROR, Self::NotFound => StatusCode::NOT_FOUND, Self::BadRequest(_) => StatusCode::BAD_REQUEST, Self::Unauthorized => StatusCode::UNAUTHORIZED, @@ -52,7 +52,7 @@ impl Error { }, Self::PropReadOnly => StatusCode::CONFLICT, Self::PreconditionFailed => StatusCode::PRECONDITION_FAILED, - Self::IOError(_) => StatusCode::INTERNAL_SERVER_ERROR, + Self::InternalError | Self::IOError(_) => StatusCode::INTERNAL_SERVER_ERROR, Self::Forbidden => StatusCode::FORBIDDEN, } } diff --git a/crates/dav/src/extensions/common.rs b/crates/dav/src/extensions/common.rs index b9ad129..0592252 100644 --- a/crates/dav/src/extensions/common.rs +++ b/crates/dav/src/extensions/common.rs @@ -6,7 +6,7 @@ use crate::{ }; use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize}; -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, PropName, EnumVariants)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, PropName, EnumVariants)] #[xml(unit_variants_ident = "CommonPropertiesPropName")] pub enum CommonPropertiesProp { // WebDAV (RFC 2518) @@ -39,9 +39,9 @@ pub trait CommonPropertiesExtension: Resource { CommonPropertiesPropName::Resourcetype => { CommonPropertiesProp::Resourcetype(self.get_resourcetype()) } - CommonPropertiesPropName::Displayname => { - CommonPropertiesProp::Displayname(self.get_displayname().map(std::string::ToString::to_string)) - } + CommonPropertiesPropName::Displayname => CommonPropertiesProp::Displayname( + self.get_displayname().map(std::string::ToString::to_string), + ), CommonPropertiesPropName::CurrentUserPrincipal => { CommonPropertiesProp::CurrentUserPrincipal( principal_uri.principal_uri(principal.get_id()).into(), diff --git a/crates/dav/src/header/depth.rs b/crates/dav/src/header/depth.rs index 70baa92..21ee497 100644 --- a/crates/dav/src/header/depth.rs +++ b/crates/dav/src/header/depth.rs @@ -85,10 +85,11 @@ impl FromRequestParts for Depth { parts: &mut axum::http::request::Parts, _state: &S, ) -> Result { - if let Some(depth_header) = parts.headers.get("Depth") { - depth_header.as_bytes().try_into() - } else { - Ok(Self::Zero) - } + parts + .headers + .get("Depth") + .map_or(Ok(Self::Zero), |depth_header| { + depth_header.as_bytes().try_into() + }) } } diff --git a/crates/dav/src/header/overwrite.rs b/crates/dav/src/header/overwrite.rs index 3cd646e..1238c1c 100644 --- a/crates/dav/src/header/overwrite.rs +++ b/crates/dav/src/header/overwrite.rs @@ -30,11 +30,10 @@ impl FromRequestParts for Overwrite { parts: &mut axum::http::request::Parts, _state: &S, ) -> Result { - if let Some(overwrite_header) = parts.headers.get("Overwrite") { - overwrite_header.as_bytes().try_into() - } else { - Ok(Self::default()) - } + parts.headers.get("Overwrite").map_or_else( + || Ok(Self::default()), + |overwrite_header| overwrite_header.as_bytes().try_into(), + ) } } @@ -60,7 +59,7 @@ mod tests { #[tokio::test] async fn test_overwrite_default() { let request = Request::put("asd").body(()).unwrap(); - let (mut parts, _) = request.into_parts(); + let (mut parts, ()) = request.into_parts(); let overwrite = Overwrite::from_request_parts(&mut parts, &()) .await .unwrap(); diff --git a/crates/dav/src/lib.rs b/crates/dav/src/lib.rs index 3d2c179..3d0fe97 100644 --- a/crates/dav/src/lib.rs +++ b/crates/dav/src/lib.rs @@ -1,4 +1,5 @@ #![warn(clippy::all, clippy::pedantic, clippy::nursery)] +#![allow(clippy::missing_errors_doc)] pub mod error; pub mod extensions; pub mod header; diff --git a/crates/dav/src/privileges.rs b/crates/dav/src/privileges.rs index bc7ebb2..aff7bdf 100644 --- a/crates/dav/src/privileges.rs +++ b/crates/dav/src/privileges.rs @@ -47,7 +47,8 @@ pub struct UserPrivilegeSet { } impl UserPrivilegeSet { - #[must_use] pub fn has(&self, privilege: &UserPrivilege) -> bool { + #[must_use] + pub fn has(&self, privilege: &UserPrivilege) -> bool { if (privilege == &UserPrivilege::WriteProperties || privilege == &UserPrivilege::WriteContent) && self.privileges.contains(&UserPrivilege::Write) @@ -57,13 +58,15 @@ impl UserPrivilegeSet { self.privileges.contains(privilege) || self.privileges.contains(&UserPrivilege::All) } - #[must_use] pub fn all() -> Self { + #[must_use] + pub fn all() -> Self { Self { privileges: HashSet::from([UserPrivilege::All]), } } - #[must_use] pub fn owner_only(is_owner: bool) -> Self { + #[must_use] + pub fn owner_only(is_owner: bool) -> Self { if is_owner { Self::all() } else { @@ -71,7 +74,8 @@ impl UserPrivilegeSet { } } - #[must_use] pub fn owner_read(is_owner: bool) -> Self { + #[must_use] + pub fn owner_read(is_owner: bool) -> Self { if is_owner { Self::read_only() } else { @@ -79,7 +83,8 @@ impl UserPrivilegeSet { } } - #[must_use] pub fn owner_write_properties(is_owner: bool) -> Self { + #[must_use] + pub fn owner_write_properties(is_owner: bool) -> Self { // Content is read-only but we can write properties if is_owner { Self::write_properties() @@ -88,7 +93,8 @@ impl UserPrivilegeSet { } } - #[must_use] pub fn read_only() -> Self { + #[must_use] + pub fn read_only() -> Self { Self { privileges: HashSet::from([ UserPrivilege::Read, @@ -98,7 +104,8 @@ impl UserPrivilegeSet { } } - #[must_use] pub fn write_properties() -> Self { + #[must_use] + pub fn write_properties() -> Self { Self { privileges: HashSet::from([ UserPrivilege::Read, diff --git a/crates/dav/src/resource/axum_methods.rs b/crates/dav/src/resource/axum_methods.rs index 98b6fed..6c6a6a1 100644 --- a/crates/dav/src/resource/axum_methods.rs +++ b/crates/dav/src/resource/axum_methods.rs @@ -9,42 +9,50 @@ pub type MethodFunction = pub trait AxumMethods: Sized + Send + Sync + 'static { #[inline] - #[must_use] fn report() -> Option> { + #[must_use] + fn report() -> Option> { None } #[inline] - #[must_use] fn get() -> Option> { + #[must_use] + fn get() -> Option> { None } #[inline] - #[must_use] fn post() -> Option> { + #[must_use] + fn post() -> Option> { None } #[inline] - #[must_use] fn mkcol() -> Option> { + #[must_use] + fn mkcol() -> Option> { None } #[inline] - #[must_use] fn mkcalendar() -> Option> { + #[must_use] + fn mkcalendar() -> Option> { None } #[inline] - #[must_use] fn put() -> Option> { + #[must_use] + fn put() -> Option> { None } #[inline] - #[must_use] fn import() -> Option> { + #[must_use] + fn import() -> Option> { None } #[inline] - #[must_use] fn allow_header() -> Allow { + #[must_use] + fn allow_header() -> Allow { let mut allow = vec![ Method::from_str("PROPFIND").unwrap(), Method::from_str("PROPPATCH").unwrap(), diff --git a/crates/dav/src/resource/mod.rs b/crates/dav/src/resource/mod.rs index 9717ce1..5d4f7ed 100644 --- a/crates/dav/src/resource/mod.rs +++ b/crates/dav/src/resource/mod.rs @@ -42,7 +42,8 @@ pub trait Resource: Clone + Send + 'static { fn get_resourcetype(&self) -> Resourcetype; - #[must_use] fn list_props() -> Vec<(Option>, &'static str)> { + #[must_use] + fn list_props() -> Vec<(Option>, &'static str)> { Self::Prop::variant_names() } @@ -75,27 +76,27 @@ pub trait Resource: Clone + Send + 'static { } fn satisfies_if_match(&self, if_match: &IfMatch) -> bool { - if let Some(etag) = self.get_etag() { - if let Ok(etag) = ETag::from_str(&etag) { - if_match.precondition_passes(&etag) - } else { - if_match.is_any() - } - } else { - if_match.is_any() - } + self.get_etag().map_or_else( + || if_match.is_any(), + |etag| { + ETag::from_str(&etag).map_or_else( + |_| if_match.is_any(), + |etag| if_match.precondition_passes(&etag), + ) + }, + ) } fn satisfies_if_none_match(&self, if_none_match: &IfNoneMatch) -> bool { - if let Some(etag) = self.get_etag() { - if let Ok(etag) = ETag::from_str(&etag) { - if_none_match.precondition_passes(&etag) - } else { - if_none_match != &IfNoneMatch::any() - } - } else { - if_none_match != &IfNoneMatch::any() - } + self.get_etag().map_or_else( + || if_none_match != &IfNoneMatch::any(), + |etag| { + ETag::from_str(&etag).map_or_else( + |_| if_none_match != &IfNoneMatch::any(), + |etag| if_none_match.precondition_passes(&etag), + ) + }, + ) } fn get_user_privileges( diff --git a/crates/dav/src/resource/resource_service.rs b/crates/dav/src/resource/resource_service.rs index 837ad46..49d3365 100644 --- a/crates/dav/src/resource/resource_service.rs +++ b/crates/dav/src/resource/resource_service.rs @@ -76,10 +76,7 @@ pub trait ResourceService: Clone + Sized + Send + Sync + AxumMethods + 'static { Err(crate::Error::Forbidden.into()) } - fn axum_service(self) -> AxumService - where - Self: AxumMethods, - { + fn axum_service(self) -> AxumService { AxumService::new(self) } diff --git a/crates/dav/src/xml/href.rs b/crates/dav/src/xml/href.rs index 8faba2c..80b7a46 100644 --- a/crates/dav/src/xml/href.rs +++ b/crates/dav/src/xml/href.rs @@ -8,7 +8,8 @@ pub struct HrefElement { } impl HrefElement { - #[must_use] pub const fn new(href: String) -> Self { + #[must_use] + pub const fn new(href: String) -> Self { Self { href } } } diff --git a/crates/dav/src/xml/multistatus.rs b/crates/dav/src/xml/multistatus.rs index 689a105..0df60bb 100644 --- a/crates/dav/src/xml/multistatus.rs +++ b/crates/dav/src/xml/multistatus.rs @@ -19,6 +19,7 @@ pub struct PropstatElement { pub status: StatusCode, } +#[allow(clippy::trivially_copy_pass_by_ref)] fn xml_serialize_status( status: &StatusCode, ns: Option, @@ -56,6 +57,7 @@ pub struct ResponseElement { pub propstat: Vec>, } +#[allow(clippy::trivially_copy_pass_by_ref, clippy::ref_option)] fn xml_serialize_optional_status( val: &Option, ns: Option, diff --git a/crates/dav/src/xml/propfind.rs b/crates/dav/src/xml/propfind.rs index 7e85ed7..2fb51e8 100644 --- a/crates/dav/src/xml/propfind.rs +++ b/crates/dav/src/xml/propfind.rs @@ -6,7 +6,7 @@ use rustical_xml::XmlDeserialize; use rustical_xml::XmlError; use rustical_xml::XmlRootTag; -#[derive(Debug, Clone, XmlDeserialize, XmlRootTag, PartialEq)] +#[derive(Debug, Clone, XmlDeserialize, XmlRootTag, PartialEq, Eq)] #[xml(root = "propfind", ns = "crate::namespace::NS_DAV")] pub struct PropfindElement { #[xml(ty = "untagged")] diff --git a/crates/dav/src/xml/report_set.rs b/crates/dav/src/xml/report_set.rs index e5c5323..5640157 100644 --- a/crates/dav/src/xml/report_set.rs +++ b/crates/dav/src/xml/report_set.rs @@ -10,7 +10,8 @@ pub struct SupportedReportSet { } impl SupportedReportSet { - #[must_use] pub fn new(methods: Vec) -> Self { + #[must_use] + pub fn new(methods: Vec) -> Self { Self { supported_report: methods .into_iter() diff --git a/crates/dav/src/xml/resourcetype.rs b/crates/dav/src/xml/resourcetype.rs index 0a00bde..bc28e4e 100644 --- a/crates/dav/src/xml/resourcetype.rs +++ b/crates/dav/src/xml/resourcetype.rs @@ -40,6 +40,6 @@ mod tests { "# - ) + ); } } diff --git a/crates/dav/src/xml/sync_collection.rs b/crates/dav/src/xml/sync_collection.rs index 30fa10b..3ea7db9 100644 --- a/crates/dav/src/xml/sync_collection.rs +++ b/crates/dav/src/xml/sync_collection.rs @@ -56,7 +56,7 @@ impl From for u64 { #[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq)] pub struct NresultsElement(#[xml(ty = "text")] u64); -#[derive(XmlDeserialize, Clone, Debug, PartialEq, XmlRootTag)] +#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq, XmlRootTag)] // // // @@ -106,11 +106,11 @@ mod tests { assert_eq!( request, SyncCollectionRequest { - sync_token: "".to_owned(), + sync_token: String::new(), sync_level: SyncLevel::One, prop: PropfindType::Prop(PropElement(vec![TestPropName::Getetag], vec![])), limit: Some(100.into()) } - ) + ); } } diff --git a/crates/dav/src/xml/tag_list.rs b/crates/dav/src/xml/tag_list.rs index 41779e3..0b23dac 100644 --- a/crates/dav/src/xml/tag_list.rs +++ b/crates/dav/src/xml/tag_list.rs @@ -17,15 +17,13 @@ impl XmlSerialize for TagList { namespaces: &HashMap, writer: &mut quick_xml::Writer<&mut Vec>, ) -> std::io::Result<()> { - let prefix = ns - .and_then(|ns| namespaces.get(&ns)) - .map(|prefix| { - if prefix.is_empty() { - String::new() - } else { - format!("{prefix}:") - } - }); + let prefix = ns.and_then(|ns| namespaces.get(&ns)).map(|prefix| { + if prefix.is_empty() { + String::new() + } else { + format!("{prefix}:") + } + }); let has_prefix = prefix.is_some(); let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat()); diff --git a/crates/dav_push/src/extension.rs b/crates/dav_push/src/extension.rs index 266675e..781b05e 100644 --- a/crates/dav_push/src/extension.rs +++ b/crates/dav_push/src/extension.rs @@ -2,7 +2,7 @@ use crate::{ContentUpdate, PropertyUpdate, SupportedTriggers, Transports, Trigge use rustical_dav::header::Depth; use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize}; -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, PropName, EnumVariants)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, PropName, EnumVariants)] #[xml(unit_variants_ident = "DavPushExtensionPropName")] pub enum DavPushExtensionProp { // WebDav Push @@ -32,7 +32,7 @@ pub trait DavPushExtension { ) -> Result { Ok(match &prop { DavPushExtensionPropName::Transports => { - DavPushExtensionProp::Transports(Default::default()) + DavPushExtensionProp::Transports(Transports::default()) } DavPushExtensionPropName::Topic => DavPushExtensionProp::Topic(self.get_topic()), DavPushExtensionPropName::SupportedTriggers => { diff --git a/crates/dav_push/src/lib.rs b/crates/dav_push/src/lib.rs index 431061e..068dd9f 100644 --- a/crates/dav_push/src/lib.rs +++ b/crates/dav_push/src/lib.rs @@ -1,4 +1,5 @@ #![warn(clippy::all, clippy::pedantic, clippy::nursery)] +#![allow(clippy::missing_errors_doc)] mod extension; mod prop; pub mod register; @@ -69,6 +70,7 @@ impl DavPushController { } } + #[allow(clippy::cognitive_complexity)] async fn send_message(&self, message: CollectionOperation) { let subscriptions = match self.sub_store.get_subscriptions(&message.topic).await { Ok(subs) => subs, diff --git a/crates/dav_push/src/prop.rs b/crates/dav_push/src/prop.rs index 01237a1..fd681a0 100644 --- a/crates/dav_push/src/prop.rs +++ b/crates/dav_push/src/prop.rs @@ -7,7 +7,7 @@ pub enum Transport { WebPush, } -#[derive(Debug, Clone, XmlSerialize, PartialEq)] +#[derive(Debug, Clone, XmlSerialize, PartialEq, Eq)] pub struct Transports { #[xml(flatten, ty = "untagged")] #[xml(ns = "crate::namespace::NS_DAVPUSH")] @@ -22,10 +22,10 @@ impl Default for Transports { } } -#[derive(XmlSerialize, XmlDeserialize, PartialEq, Clone)] +#[derive(XmlSerialize, XmlDeserialize, PartialEq, Eq, Clone)] pub struct SupportedTriggers(#[xml(flatten, ty = "untagged")] pub Vec); -#[derive(XmlSerialize, XmlDeserialize, PartialEq, Debug, Clone)] +#[derive(XmlSerialize, XmlDeserialize, PartialEq, Eq, Debug, Clone)] pub enum Trigger { #[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")] ContentUpdate(ContentUpdate), diff --git a/crates/dav_push/src/register.rs b/crates/dav_push/src/register.rs index d721050..6cae633 100644 --- a/crates/dav_push/src/register.rs +++ b/crates/dav_push/src/register.rs @@ -1,7 +1,7 @@ use crate::Trigger; use rustical_xml::{XmlDeserialize, XmlRootTag, XmlSerialize}; -#[derive(XmlDeserialize, Clone, Debug, PartialEq)] +#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq)] #[xml(ns = "crate::namespace::NS_DAVPUSH")] pub struct WebPushSubscription { #[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")] @@ -23,16 +23,16 @@ pub struct SubscriptionPublicKey { pub key: String, } -#[derive(XmlDeserialize, Clone, Debug, PartialEq)] +#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq)] pub struct SubscriptionElement { #[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")] pub web_push_subscription: WebPushSubscription, } -#[derive(XmlDeserialize, XmlSerialize, Clone, Debug, PartialEq)] +#[derive(XmlDeserialize, XmlSerialize, Clone, Debug, PartialEq, Eq)] pub struct TriggerElement(#[xml(ty = "untagged", flatten)] Vec); -#[derive(XmlDeserialize, XmlRootTag, Clone, Debug, PartialEq)] +#[derive(XmlDeserialize, XmlRootTag, Clone, Debug, PartialEq, Eq)] #[xml(root = "push-register")] #[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")] pub struct PushRegister { @@ -100,6 +100,6 @@ mod tests { Trigger::PropertyUpdate(PropertyUpdate(Depth::Zero)), ])) } - ) + ); } } diff --git a/crates/frontend/src/assets.rs b/crates/frontend/src/assets.rs index bb65521..b5d9bc5 100644 --- a/crates/frontend/src/assets.rs +++ b/crates/frontend/src/assets.rs @@ -8,7 +8,7 @@ use futures_core::future::BoxFuture; use headers::{ContentType, ETag, HeaderMapExt}; use http::{Method, StatusCode}; use rust_embed::RustEmbed; -use std::{convert::Infallible, marker::PhantomData, str::FromStr}; +use std::{borrow::Cow, convert::Infallible, marker::PhantomData, str::FromStr}; use tower::Service; #[derive(Clone, RustEmbed, Default)] @@ -16,6 +16,7 @@ use tower::Service; #[allow(dead_code)] // Since this is not used with the frontend-dev feature pub struct Assets; +#[allow(dead_code)] #[derive(Clone, Default)] pub struct EmbedService where @@ -41,6 +42,7 @@ where } #[inline] + #[allow(clippy::similar_names)] fn call(&mut self, mut req: Request) -> Self::Future { Box::pin(async move { if req.method() != Method::GET && req.method() != Method::HEAD { @@ -60,7 +62,7 @@ where let mime = mime_guess::from_path(path).first_or_octet_stream(); let body = if req.method() == Method::HEAD { - Default::default() + Cow::default() } else { data }; diff --git a/crates/frontend/src/lib.rs b/crates/frontend/src/lib.rs index 0aee30b..91bebd2 100644 --- a/crates/frontend/src/lib.rs +++ b/crates/frontend/src/lib.rs @@ -48,7 +48,7 @@ pub fn frontend_router Router { let user_router = Router::new() .route("/", get(route_get_home)) - .route("/{user}", get(route_user_named::)) + .route("/{user}", get(route_user_named::)) // App token management .route("/{user}/app_token", post(route_post_app_token::)) .route( diff --git a/crates/frontend/src/nextcloud_login/routes.rs b/crates/frontend/src/nextcloud_login/routes.rs index ce25b5e..773d328 100644 --- a/crates/frontend/src/nextcloud_login/routes.rs +++ b/crates/frontend/src/nextcloud_login/routes.rs @@ -27,20 +27,22 @@ pub async fn post_nextcloud_login( let token = uuid::Uuid::new_v4().to_string(); let app_name = user_agent.to_string(); - let mut flows = state.flows.write().await; - // Flows must not last longer than 10 minutes - // We also enforce that condition here to prevent a memory leak where unpolled flows would - // never be cleaned up - flows.retain(|_, flow| Utc::now() - flow.created_at < Duration::minutes(10)); - flows.insert( - flow_id.clone(), - NextcloudFlow { - app_name: app_name.clone(), - created_at: Utc::now(), - token: token.clone(), - response: None, - }, - ); + { + let mut flows = state.flows.write().await; + // Flows must not last longer than 10 minutes + // We also enforce that condition here to prevent a memory leak where unpolled flows would + // never be cleaned up + flows.retain(|_, flow| Utc::now() - flow.created_at < Duration::minutes(10)); + flows.insert( + flow_id.clone(), + NextcloudFlow { + app_name: app_name.clone(), + created_at: Utc::now(), + token: token.clone(), + response: None, + }, + ); + } Json(NextcloudLoginResponse { login: format!("https://{host}/index.php/login/v2/flow/{flow_id}"), poll: NextcloudLoginPoll { @@ -56,6 +58,7 @@ pub struct NextcloudPollForm { token: String, } +#[allow(clippy::significant_drop_tightening)] pub async fn post_nextcloud_poll( Extension(state): Extension>, Path(flow_id): Path, diff --git a/crates/frontend/src/oidc_user_store.rs b/crates/frontend/src/oidc_user_store.rs index e40c7d8..8399733 100644 --- a/crates/frontend/src/oidc_user_store.rs +++ b/crates/frontend/src/oidc_user_store.rs @@ -1,8 +1,7 @@ -use std::sync::Arc; - use async_trait::async_trait; use rustical_oidc::UserStore; -use rustical_store::auth::{AuthenticationProvider, Principal}; +use rustical_store::auth::{AuthenticationProvider, Principal, PrincipalType}; +use std::sync::Arc; pub struct OidcUserStore(pub Arc); @@ -26,7 +25,7 @@ impl UserStore for OidcUserStore { Principal { id: id.to_owned(), displayname: None, - principal_type: Default::default(), + principal_type: PrincipalType::default(), password: None, memberships: vec![], }, diff --git a/crates/frontend/src/routes/addressbook.rs b/crates/frontend/src/routes/addressbook.rs index 4652896..66e80d0 100644 --- a/crates/frontend/src/routes/addressbook.rs +++ b/crates/frontend/src/routes/addressbook.rs @@ -42,8 +42,8 @@ pub async fn route_addressbook_restore( return Ok(StatusCode::UNAUTHORIZED.into_response()); } store.restore_addressbook(&owner, &addressbook_id).await?; - Ok(match referer { - Some(referer) => Redirect::to(&referer.to_string()).into_response(), - None => (StatusCode::CREATED, "Restored").into_response(), - }) + Ok(referer.map_or_else( + || (StatusCode::CREATED, "Restored").into_response(), + |referer| Redirect::to(&referer.to_string()).into_response(), + )) } diff --git a/crates/frontend/src/routes/calendar.rs b/crates/frontend/src/routes/calendar.rs index 617dcb1..37c5428 100644 --- a/crates/frontend/src/routes/calendar.rs +++ b/crates/frontend/src/routes/calendar.rs @@ -42,8 +42,8 @@ pub async fn route_calendar_restore( return Ok(StatusCode::UNAUTHORIZED.into_response()); } store.restore_calendar(&owner, &cal_id).await?; - Ok(match referer { - Some(referer) => Redirect::to(&referer.to_string()).into_response(), - None => (StatusCode::CREATED, "Restored").into_response(), - }) + Ok(referer.map_or_else( + || (StatusCode::CREATED, "Restored").into_response(), + |referer| Redirect::to(&referer.to_string()).into_response(), + )) } diff --git a/crates/frontend/src/routes/user.rs b/crates/frontend/src/routes/user.rs index c2bc7cb..237b824 100644 --- a/crates/frontend/src/routes/user.rs +++ b/crates/frontend/src/routes/user.rs @@ -10,10 +10,7 @@ use axum::{ use axum_extra::{TypedHeader, extract::Host}; use headers::UserAgent; use http::StatusCode; -use rustical_store::{ - AddressbookStore, CalendarStore, - auth::{AppToken, AuthenticationProvider, Principal}, -}; +use rustical_store::auth::{AppToken, AuthenticationProvider, Principal}; use crate::pages::user::{Section, UserPage}; @@ -32,14 +29,8 @@ pub struct ProfileSection { pub davx5_hostname: Option, } -pub async fn route_user_named< - CS: CalendarStore, - AS: AddressbookStore, - AP: AuthenticationProvider, ->( +pub async fn route_user_named( Path(user_id): Path, - Extension(cal_store): Extension>, - Extension(addr_store): Extension>, Extension(auth_provider): Extension>, TypedHeader(user_agent): TypedHeader, Host(host): Host, @@ -49,26 +40,6 @@ pub async fn route_user_named< return StatusCode::UNAUTHORIZED.into_response(); } - let mut calendars = vec![]; - for group in user.memberships() { - calendars.extend(cal_store.get_calendars(group).await.unwrap()); - } - - let mut deleted_calendars = vec![]; - for group in user.memberships() { - deleted_calendars.extend(cal_store.get_deleted_calendars(group).await.unwrap()); - } - - let mut addressbooks = vec![]; - for group in user.memberships() { - addressbooks.extend(addr_store.get_addressbooks(group).await.unwrap()); - } - - let mut deleted_addressbooks = vec![]; - for group in user.memberships() { - deleted_addressbooks.extend(addr_store.get_deleted_addressbooks(group).await.unwrap()); - } - let is_apple = user_agent.as_str().contains("Apple") || user_agent.as_str().contains("Mac OS"); let davx5_hostname = user_agent.as_str().contains("Android").then_some(host); diff --git a/crates/ical/Cargo.toml b/crates/ical/Cargo.toml index c51d9ab..a02cb26 100644 --- a/crates/ical/Cargo.toml +++ b/crates/ical/Cargo.toml @@ -15,7 +15,6 @@ thiserror.workspace = true derive_more.workspace = true rustical_xml.workspace = true ical.workspace = true -lazy_static.workspace = true regex.workspace = true rrule.workspace = true serde.workspace = true diff --git a/crates/ical/src/address_object.rs b/crates/ical/src/address_object.rs index 5726036..40f5082 100644 --- a/crates/ical/src/address_object.rs +++ b/crates/ical/src/address_object.rs @@ -22,7 +22,7 @@ impl TryFrom for AddressObject { fn try_from(vcard: VcardContact) -> Result { let uid = vcard .get_uid() - .ok_or(Error::InvalidData("missing UID".to_owned()))? + .ok_or_else(|| Error::InvalidData("missing UID".to_owned()))? .to_owned(); let vcf = vcard.generate(); Ok(Self { @@ -45,32 +45,38 @@ impl AddressObject { Ok(Self { id, vcf, vcard }) } - #[must_use] pub fn get_id(&self) -> &str { + #[must_use] + pub fn get_id(&self) -> &str { &self.id } - #[must_use] pub fn get_etag(&self) -> String { + #[must_use] + pub fn get_etag(&self) -> String { let mut hasher = Sha256::new(); hasher.update(self.get_id()); hasher.update(self.get_vcf()); format!("\"{:x}\"", hasher.finalize()) } - #[must_use] pub fn get_vcf(&self) -> &str { + #[must_use] + pub fn get_vcf(&self) -> &str { &self.vcf } - #[must_use] pub fn get_anniversary(&self) -> Option<(CalDateTime, bool)> { + #[must_use] + pub fn get_anniversary(&self) -> Option<(CalDateTime, bool)> { let prop = self.vcard.get_property("ANNIVERSARY")?.value.as_deref()?; CalDateTime::parse_vcard(prop).ok() } - #[must_use] pub fn get_birthday(&self) -> Option<(CalDateTime, bool)> { + #[must_use] + pub fn get_birthday(&self) -> Option<(CalDateTime, bool)> { let prop = self.vcard.get_property("BDAY")?.value.as_deref()?; CalDateTime::parse_vcard(prop).ok() } - #[must_use] pub fn get_full_name(&self) -> Option<&str> { + #[must_use] + pub fn get_full_name(&self) -> Option<&str> { let prop = self.vcard.get_property("FN")?; prop.value.as_deref() } @@ -78,9 +84,7 @@ impl AddressObject { pub fn get_anniversary_object(&self) -> Result, Error> { Ok( if let Some((anniversary, contains_year)) = self.get_anniversary() { - let fullname = if let Some(name) = self.get_full_name() { - name - } else { + let Some(fullname) = self.get_full_name() else { return Ok(None); }; let anniversary = anniversary.date(); @@ -122,9 +126,7 @@ END:VCALENDAR", pub fn get_birthday_object(&self) -> Result, Error> { Ok( if let Some((birthday, contains_year)) = self.get_birthday() { - let fullname = if let Some(name) = self.get_full_name() { - name - } else { + let Some(fullname) = self.get_full_name() else { return Ok(None); }; let birthday = birthday.date(); diff --git a/crates/ical/src/error.rs b/crates/ical/src/error.rs index 5805901..52ff7e7 100644 --- a/crates/ical/src/error.rs +++ b/crates/ical/src/error.rs @@ -24,10 +24,12 @@ pub enum Error { } impl Error { - #[must_use] pub const fn status_code(&self) -> StatusCode { + #[must_use] + pub const fn status_code(&self) -> StatusCode { match self { - Self::InvalidData(_) => StatusCode::BAD_REQUEST, - Self::MissingCalendar | Self::MissingContact => StatusCode::BAD_REQUEST, + Self::InvalidData(_) | Self::MissingCalendar | Self::MissingContact => { + StatusCode::BAD_REQUEST + } _ => StatusCode::INTERNAL_SERVER_ERROR, } } diff --git a/crates/ical/src/icalendar/event.rs b/crates/ical/src/icalendar/event.rs index 25ad480..4ed18cb 100644 --- a/crates/ical/src/icalendar/event.rs +++ b/crates/ical/src/icalendar/event.rs @@ -15,7 +15,8 @@ pub struct EventObject { } impl EventObject { - #[must_use] pub fn get_uid(&self) -> &str { + #[must_use] + pub fn get_uid(&self) -> &str { self.event.get_uid() } @@ -70,9 +71,9 @@ impl EventObject { for prop in &self.event.properties { rrule_set = match prop.name.as_str() { "RRULE" => { - let rrule = RRule::from_str(prop.value.as_ref().ok_or(Error::RRuleError( - rrule::ParseError::MissingDateGenerationRules.into(), - ))?)? + let rrule = RRule::from_str(prop.value.as_ref().ok_or_else(|| { + Error::RRuleError(rrule::ParseError::MissingDateGenerationRules.into()) + })?)? .validate(dtstart) .unwrap(); rrule_set.rrule(rrule) @@ -120,8 +121,8 @@ impl EventObject { date.format() }; - for _override in overrides { - if let Some(override_id) = &_override + for ev_override in overrides { + if let Some(override_id) = &ev_override .event .get_recurrence_id() .as_ref() @@ -131,7 +132,7 @@ impl EventObject { { // We have an override for this occurence // - events.push(_override.event.clone()); + events.push(ev_override.event.clone()); continue 'recurrence; } } @@ -185,7 +186,7 @@ mod tests { use crate::CalendarObject; use ical::generator::Emitter; - const ICS: &str = r#"BEGIN:VCALENDAR + const ICS: &str = r"BEGIN:VCALENDAR CALSCALE:GREGORIAN VERSION:2.0 BEGIN:VTIMEZONE @@ -203,7 +204,7 @@ SUMMARY:weekly stuff TRANSP:OPAQUE RRULE:FREQ=WEEKLY;COUNT=4;INTERVAL=2;BYDAY=TU,TH,SU END:VEVENT -END:VCALENDAR"#; +END:VCALENDAR"; const EXPANDED: [&str; 4] = [ "BEGIN:VEVENT\r @@ -251,13 +252,7 @@ END:VEVENT\r\n", #[test] fn test_expand_recurrence() { let event = CalendarObject::from_ics(ICS.to_string()).unwrap(); - let (event, overrides) = if let crate::CalendarObjectComponent::Event( - main_event, - overrides, - ) = event.get_data() - { - (main_event, overrides) - } else { + let crate::CalendarObjectComponent::Event(event, overrides) = event.get_data() else { panic!() }; diff --git a/crates/ical/src/icalendar/object.rs b/crates/ical/src/icalendar/object.rs index 9dbe7c5..c02b52f 100644 --- a/crates/ical/src/icalendar/object.rs +++ b/crates/ical/src/icalendar/object.rs @@ -26,7 +26,8 @@ pub enum CalendarObjectType { } impl CalendarObjectType { - #[must_use] pub const fn as_str(&self) -> &'static str { + #[must_use] + pub const fn as_str(&self) -> &'static str { match self { Self::Event => "VEVENT", Self::Todo => "VTODO", @@ -208,15 +209,18 @@ impl CalendarObject { }) } - #[must_use] pub const fn get_vtimezones(&self) -> &HashMap { + #[must_use] + pub const fn get_vtimezones(&self) -> &HashMap { &self.vtimezones } - #[must_use] pub const fn get_data(&self) -> &CalendarObjectComponent { + #[must_use] + pub const fn get_data(&self) -> &CalendarObjectComponent { &self.data } - #[must_use] pub fn get_id(&self) -> &str { + #[must_use] + pub fn get_id(&self) -> &str { match &self.data { // We've made sure before that the first component exists and all components share the // same UID @@ -226,22 +230,26 @@ impl CalendarObject { } } - #[must_use] pub fn get_etag(&self) -> String { + #[must_use] + pub fn get_etag(&self) -> String { let mut hasher = Sha256::new(); hasher.update(self.get_id()); hasher.update(self.get_ics()); format!("\"{:x}\"", hasher.finalize()) } - #[must_use] pub fn get_ics(&self) -> &str { + #[must_use] + pub fn get_ics(&self) -> &str { &self.ics } - #[must_use] pub fn get_component_name(&self) -> &str { + #[must_use] + pub fn get_component_name(&self) -> &str { self.get_object_type().as_str() } - #[must_use] pub fn get_object_type(&self) -> CalendarObjectType { + #[must_use] + pub fn get_object_type(&self) -> CalendarObjectType { (&self.data).into() } @@ -249,7 +257,7 @@ impl CalendarObject { match &self.data { CalendarObjectComponent::Event(main_event, overrides) => Ok(overrides .iter() - .chain([main_event].into_iter()) + .chain(std::iter::once(main_event)) .map(super::event::EventObject::get_dtstart) .collect::, _>>()? .into_iter() @@ -263,7 +271,7 @@ impl CalendarObject { match &self.data { CalendarObjectComponent::Event(main_event, overrides) => Ok(overrides .iter() - .chain([main_event].into_iter()) + .chain(std::iter::once(main_event)) .map(super::event::EventObject::get_last_occurence) .collect::, _>>()? .into_iter() diff --git a/crates/ical/src/lib.rs b/crates/ical/src/lib.rs index a02d1c8..9c00d52 100644 --- a/crates/ical/src/lib.rs +++ b/crates/ical/src/lib.rs @@ -1,4 +1,5 @@ #![warn(clippy::all, clippy::pedantic, clippy::nursery)] +#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] mod timestamp; mod timezone; pub use timestamp::*; diff --git a/crates/ical/src/timestamp.rs b/crates/ical/src/timestamp.rs index 74a9720..f96ad9d 100644 --- a/crates/ical/src/timestamp.rs +++ b/crates/ical/src/timestamp.rs @@ -3,14 +3,11 @@ use chrono::{DateTime, Datelike, Duration, Local, NaiveDate, NaiveDateTime, Naiv use chrono_tz::Tz; use derive_more::derive::Deref; use ical::property::Property; -use lazy_static::lazy_static; use rustical_xml::{ValueDeserialize, ValueSerialize}; -use std::{borrow::Cow, collections::HashMap, ops::Add}; +use std::{borrow::Cow, collections::HashMap, ops::Add, sync::LazyLock}; -lazy_static! { - static ref RE_VCARD_DATE_MM_DD: regex::Regex = - regex::Regex::new(r"^--(?\d{2})(?\d{2})$").unwrap(); -} +static RE_VCARD_DATE_MM_DD: LazyLock = + LazyLock::new(|| regex::Regex::new(r"^--(?\d{2})(?\d{2})$").unwrap()); const LOCAL_DATE_TIME: &str = "%Y%m%dT%H%M%S"; const UTC_DATE_TIME: &str = "%Y%m%dT%H%M%SZ"; @@ -137,9 +134,7 @@ impl CalDateTime { let prop_value = prop .value .as_ref() - .ok_or(CalDateTimeError::InvalidDatetimeFormat( - "empty property".to_owned(), - ))?; + .ok_or_else(|| CalDateTimeError::InvalidDatetimeFormat("empty property".into()))?; let timezone = if let Some(tzid) = prop.get_param("TZID") { if let Some(timezone) = timezones.get(tzid) { @@ -158,7 +153,8 @@ impl CalDateTime { Self::parse(prop_value, timezone) } - #[must_use] pub fn format(&self) -> String { + #[must_use] + pub fn format(&self) -> String { match self { Self::DateTime(datetime) => match datetime.timezone() { ICalTimezone::Olson(chrono_tz::UTC) => datetime.format(UTC_DATE_TIME).to_string(), @@ -168,25 +164,29 @@ impl CalDateTime { } } - #[must_use] pub fn format_date(&self) -> String { + #[must_use] + pub fn format_date(&self) -> String { match self { Self::DateTime(datetime) => datetime.format(LOCAL_DATE).to_string(), Self::Date(date, _) => date.format(LOCAL_DATE).to_string(), } } - #[must_use] pub fn date(&self) -> NaiveDate { + #[must_use] + pub fn date(&self) -> NaiveDate { match self { Self::DateTime(datetime) => datetime.date_naive(), Self::Date(date, _) => date.to_owned(), } } - #[must_use] pub const fn is_date(&self) -> bool { + #[must_use] + pub const fn is_date(&self) -> bool { matches!(&self, Self::Date(_, _)) } - #[must_use] pub fn as_datetime(&self) -> Cow<'_, DateTime> { + #[must_use] + pub fn as_datetime(&self) -> Cow<'_, DateTime> { match self { Self::DateTime(datetime) => Cow::Borrowed(datetime), Self::Date(date, tz) => Cow::Owned( @@ -219,8 +219,7 @@ impl CalDateTime { if let Ok(datetime) = NaiveDateTime::parse_from_str(value, UTC_DATE_TIME) { return Ok(datetime.and_utc().into()); } - let timezone = timezone - .map_or(ICalTimezone::Local, ICalTimezone::Olson); + let timezone = timezone.map_or(ICalTimezone::Local, ICalTimezone::Olson); if let Ok(date) = NaiveDate::parse_from_str(value, LOCAL_DATE) { return Ok(Self::Date(date, timezone)); } @@ -251,7 +250,7 @@ impl CalDateTime { return Ok(( Self::Date( NaiveDate::from_ymd_opt(year, month, day) - .ok_or(CalDateTimeError::ParseError(value.to_string()))?, + .ok_or_else(|| CalDateTimeError::ParseError(value.to_string()))?, ICalTimezone::Local, ), false, @@ -260,11 +259,13 @@ impl CalDateTime { Err(CalDateTimeError::InvalidDatetimeFormat(value.to_string())) } - #[must_use] pub fn utc(&self) -> DateTime { + #[must_use] + pub fn utc(&self) -> DateTime { self.as_datetime().to_utc() } - #[must_use] pub fn timezone(&self) -> ICalTimezone { + #[must_use] + pub fn timezone(&self) -> ICalTimezone { match &self { Self::DateTime(datetime) => datetime.timezone(), Self::Date(_, tz) => tz.to_owned(), @@ -349,9 +350,7 @@ impl Datelike for CalDateTime { fn with_month0(&self, month0: u32) -> Option { match &self { Self::DateTime(datetime) => Some(Self::DateTime(datetime.with_month0(month0)?)), - Self::Date(date, tz) => { - Some(Self::Date(date.with_month0(month0)?, tz.to_owned())) - } + Self::Date(date, tz) => Some(Self::Date(date.with_month0(month0)?, tz.to_owned())), } } fn with_day(&self, day: u32) -> Option { @@ -368,22 +367,14 @@ impl Datelike for CalDateTime { } fn with_ordinal(&self, ordinal: u32) -> Option { match &self { - Self::DateTime(datetime) => { - Some(Self::DateTime(datetime.with_ordinal(ordinal)?)) - } - Self::Date(date, tz) => { - Some(Self::Date(date.with_ordinal(ordinal)?, tz.to_owned())) - } + Self::DateTime(datetime) => Some(Self::DateTime(datetime.with_ordinal(ordinal)?)), + Self::Date(date, tz) => Some(Self::Date(date.with_ordinal(ordinal)?, tz.to_owned())), } } fn with_ordinal0(&self, ordinal0: u32) -> Option { match &self { - Self::DateTime(datetime) => { - Some(Self::DateTime(datetime.with_ordinal0(ordinal0)?)) - } - Self::Date(date, tz) => { - Some(Self::Date(date.with_ordinal0(ordinal0)?, tz.to_owned())) - } + Self::DateTime(datetime) => Some(Self::DateTime(datetime.with_ordinal0(ordinal0)?)), + Self::Date(date, tz) => Some(Self::Date(date.with_ordinal0(ordinal0)?, tz.to_owned())), } } } diff --git a/crates/oidc/src/lib.rs b/crates/oidc/src/lib.rs index eb63b0c..5e74250 100644 --- a/crates/oidc/src/lib.rs +++ b/crates/oidc/src/lib.rs @@ -1,4 +1,5 @@ #![warn(clippy::all, clippy::pedantic, clippy::nursery)] +#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] use axum::{ Extension, Form, extract::Query, diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml index 20049b3..98823af 100644 --- a/crates/store/Cargo.toml +++ b/crates/store/Cargo.toml @@ -15,7 +15,6 @@ sha2 = { workspace = true } ical = { workspace = true } chrono = { workspace = true } regex = { workspace = true } -lazy_static = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true } chrono-tz = { workspace = true } diff --git a/crates/store/src/addressbook.rs b/crates/store/src/addressbook.rs index 120781c..05016b8 100644 --- a/crates/store/src/addressbook.rs +++ b/crates/store/src/addressbook.rs @@ -14,7 +14,8 @@ pub struct Addressbook { } impl Addressbook { - #[must_use] pub fn format_synctoken(&self) -> String { + #[must_use] + pub fn format_synctoken(&self) -> String { format_synctoken(self.synctoken) } } diff --git a/crates/store/src/auth/middleware.rs b/crates/store/src/auth/middleware.rs index f8a75b7..5c94ff8 100644 --- a/crates/store/src/auth/middleware.rs +++ b/crates/store/src/auth/middleware.rs @@ -53,9 +53,9 @@ impl Clone for AuthenticationMiddleware Service for AuthenticationMiddleware +impl Service for AuthenticationMiddleware where - S: Service + Send + 'static, + S: Service + Send + Clone + 'static, S::Future: Send + 'static, { type Response = S::Response; diff --git a/crates/store/src/auth/principal.rs b/crates/store/src/auth/principal.rs index 4ca71f7..6d69224 100644 --- a/crates/store/src/auth/principal.rs +++ b/crates/store/src/auth/principal.rs @@ -35,7 +35,8 @@ impl Principal { /// Returns true if the user is either /// - the principal itself /// - has full access to the prinicpal (is member) - #[must_use] pub fn is_principal(&self, principal: &str) -> bool { + #[must_use] + pub fn is_principal(&self, principal: &str) -> bool { if self.id == principal { return true; } diff --git a/crates/store/src/auth/principal_type.rs b/crates/store/src/auth/principal_type.rs index 69a5ca3..c9102fb 100644 --- a/crates/store/src/auth/principal_type.rs +++ b/crates/store/src/auth/principal_type.rs @@ -36,7 +36,8 @@ impl TryFrom<&str> for PrincipalType { } impl PrincipalType { - #[must_use] pub const fn as_str(&self) -> &'static str { + #[must_use] + pub const fn as_str(&self) -> &'static str { match self { Self::Individual => "INDIVIDUAL", Self::Group => "GROUP", diff --git a/crates/store/src/calendar.rs b/crates/store/src/calendar.rs index a8a7567..31e6a1c 100644 --- a/crates/store/src/calendar.rs +++ b/crates/store/src/calendar.rs @@ -32,17 +32,20 @@ pub struct Calendar { } impl Calendar { - #[must_use] pub fn format_synctoken(&self) -> String { + #[must_use] + pub fn format_synctoken(&self) -> String { format_synctoken(self.synctoken) } - #[must_use] pub fn get_timezone(&self) -> Option { + #[must_use] + pub fn get_timezone(&self) -> Option { self.timezone_id .as_ref() .and_then(|tzid| chrono_tz::Tz::from_str(tzid).ok()) } - #[must_use] pub fn get_vtimezone(&self) -> Option<&'static str> { + #[must_use] + pub fn get_vtimezone(&self) -> Option<&'static str> { self.timezone_id .as_ref() .and_then(|tzid| vtimezones_rs::VTIMEZONES.get(tzid).copied()) diff --git a/crates/store/src/combined_calendar_store.rs b/crates/store/src/combined_calendar_store.rs index 2a7110c..d90130a 100644 --- a/crates/store/src/combined_calendar_store.rs +++ b/crates/store/src/combined_calendar_store.rs @@ -20,6 +20,7 @@ impl CombinedCalendarStore { } } + #[must_use] pub fn with_store(mut self, store: Arc) -> Self { let store: Arc = store; self.stores.insert(CS::PREFIX, store); @@ -30,8 +31,7 @@ impl CombinedCalendarStore { self.stores .iter() .find(|&(prefix, _store)| id.starts_with(prefix)) - .map(|(_prefix, store)| store.clone()) - .unwrap_or(self.default.clone()) + .map_or_else(|| self.default.clone(), |(_prefix, store)| store.clone()) } } diff --git a/crates/store/src/error.rs b/crates/store/src/error.rs index 5739b1d..649105c 100644 --- a/crates/store/src/error.rs +++ b/crates/store/src/error.rs @@ -30,7 +30,8 @@ pub enum Error { } impl Error { - #[must_use] pub const fn status_code(&self) -> StatusCode { + #[must_use] + pub const fn status_code(&self) -> StatusCode { match self { Self::NotFound => StatusCode::NOT_FOUND, Self::AlreadyExists => StatusCode::CONFLICT, diff --git a/crates/store/src/synctoken.rs b/crates/store/src/synctoken.rs index b7dbcf9..e21aac2 100644 --- a/crates/store/src/synctoken.rs +++ b/crates/store/src/synctoken.rs @@ -1,10 +1,12 @@ const SYNC_NAMESPACE: &str = "github.com/lennart-k/rustical/ns/"; -#[must_use] pub fn format_synctoken(synctoken: i64) -> String { +#[must_use] +pub fn format_synctoken(synctoken: i64) -> String { format!("{SYNC_NAMESPACE}{synctoken}") } -#[must_use] pub fn parse_synctoken(synctoken: &str) -> Option { +#[must_use] +pub fn parse_synctoken(synctoken: &str) -> Option { if !synctoken.starts_with(SYNC_NAMESPACE) { return None; } diff --git a/crates/store_sqlite/src/addressbook_store.rs b/crates/store_sqlite/src/addressbook_store.rs index b6897a2..1e79857 100644 --- a/crates/store_sqlite/src/addressbook_store.rs +++ b/crates/store_sqlite/src/addressbook_store.rs @@ -141,10 +141,12 @@ impl SqliteAddressbookStore { if use_trashbin { sqlx::query!( r#"UPDATE addressbooks SET deleted_at = datetime() WHERE (principal, id) = (?, ?)"#, - principal, addressbook_id + principal, + addressbook_id ) .execute(executor) - .await.map_err(crate::Error::from)?; + .await + .map_err(crate::Error::from)?; } else { sqlx::query!( r#"DELETE FROM addressbooks WHERE (principal, id) = (?, ?)"#, @@ -203,9 +205,7 @@ impl SqliteAddressbookStore { let mut objects = vec![]; let mut deleted_objects = vec![]; - let new_synctoken = changes - .last() - .map_or(0, |&Row { synctoken, .. }| synctoken); + let new_synctoken = changes.last().map_or(0, |&Row { synctoken, .. }| synctoken); for Row { object_id, .. } in changes { match Self::_get_object(&mut *conn, principal, addressbook_id, &object_id, false).await diff --git a/crates/store_sqlite/src/calendar_store.rs b/crates/store_sqlite/src/calendar_store.rs index 7e0d12b..054ae2e 100644 --- a/crates/store_sqlite/src/calendar_store.rs +++ b/crates/store_sqlite/src/calendar_store.rs @@ -213,21 +213,25 @@ impl SqliteCalendarStore { id: &str, use_trashbin: bool, ) -> Result<(), Error> { - if use_trashbin { sqlx::query!( - r#"UPDATE calendars SET deleted_at = datetime() WHERE (principal, id) = (?, ?)"#, - principal, - id - ) - .execute(executor) - .await - .map_err(crate::Error::from)? } else { sqlx::query!( - r#"DELETE FROM calendars WHERE (principal, id) = (?, ?)"#, - principal, - id - ) - .execute(executor) - .await - .map_err(crate::Error::from)? }; + if use_trashbin { + sqlx::query!( + r#"UPDATE calendars SET deleted_at = datetime() WHERE (principal, id) = (?, ?)"#, + principal, + id + ) + .execute(executor) + .await + .map_err(crate::Error::from)? + } else { + sqlx::query!( + r#"DELETE FROM calendars WHERE (principal, id) = (?, ?)"#, + principal, + id + ) + .execute(executor) + .await + .map_err(crate::Error::from)? + }; Ok(()) } @@ -476,9 +480,7 @@ impl SqliteCalendarStore { let mut objects = vec![]; let mut deleted_objects = vec![]; - let new_synctoken = changes - .last() - .map_or(0, |&Row { synctoken, .. }| synctoken); + let new_synctoken = changes.last().map_or(0, |&Row { synctoken, .. }| synctoken); for Row { object_id, .. } in changes { match Self::_get_object(&mut *conn, principal, cal_id, &object_id, false).await { diff --git a/crates/store_sqlite/src/lib.rs b/crates/store_sqlite/src/lib.rs index fe94488..52353d7 100644 --- a/crates/store_sqlite/src/lib.rs +++ b/crates/store_sqlite/src/lib.rs @@ -1,4 +1,5 @@ #![warn(clippy::all, clippy::pedantic, clippy::nursery)] +#![allow(clippy::missing_errors_doc)] pub use error::Error; use serde::Serialize; use sqlx::{Pool, Sqlite, SqlitePool, sqlite::SqliteConnectOptions}; @@ -26,7 +27,8 @@ pub struct SqliteStore { } impl SqliteStore { - #[must_use] pub const fn new(db: SqlitePool) -> Self { + #[must_use] + pub const fn new(db: SqlitePool) -> Self { Self { db } } } diff --git a/crates/store_sqlite/src/principal_store.rs b/crates/store_sqlite/src/principal_store.rs index f774fe4..bdcbac7 100644 --- a/crates/store_sqlite/src/principal_store.rs +++ b/crates/store_sqlite/src/principal_store.rs @@ -195,9 +195,8 @@ impl AuthenticationProvider for SqlitePrincipalStore { Some(user) => user, None => return Ok(None), }; - let password = match &user.password { - Some(password) => password, - None => return Ok(None), + let Some(password) = &user.password else { + return Ok(None); }; if password_auth::verify_password(password_input, password.as_ref()).is_ok() { diff --git a/crates/xml/src/de.rs b/crates/xml/src/de.rs index 25c12a8..4ae00e9 100644 --- a/crates/xml/src/de.rs +++ b/crates/xml/src/de.rs @@ -18,10 +18,7 @@ pub trait XmlDocument: XmlDeserialize { fn parse(reader: quick_xml::NsReader) -> Result; #[inline] - fn parse_reader(input: R) -> Result - where - Self: XmlDeserialize, - { + fn parse_reader(input: R) -> Result { let mut reader = quick_xml::NsReader::from_reader(input); reader.config_mut().trim_text(true); Self::parse(reader) @@ -43,8 +40,7 @@ impl XmlDocument for T { let event = reader.read_event_into(&mut buf)?; let empty = matches!(event, Event::Empty(_)); match event { - Event::Decl(_) => { /* ignore this */ } - Event::Comment(_) => { /* ignore this */ } + Event::Decl(_) | Event::Comment(_) => { /* ignore this */ } Event::Start(start) | Event::Empty(start) => { let (ns, name) = reader.resolve_element(start.name()); let matches = match (Self::root_ns(), &ns, name) { diff --git a/crates/xml/src/lib.rs b/crates/xml/src/lib.rs index 271efc8..1968d5e 100644 --- a/crates/xml/src/lib.rs +++ b/crates/xml/src/lib.rs @@ -1,4 +1,5 @@ #![warn(clippy::all, clippy::pedantic, clippy::nursery)] +#![allow(clippy::missing_errors_doc)] use quick_xml::name::Namespace; use std::collections::HashMap; use std::hash::Hash; diff --git a/crates/xml/src/namespace.rs b/crates/xml/src/namespace.rs index 40b6a67..749827c 100644 --- a/crates/xml/src/namespace.rs +++ b/crates/xml/src/namespace.rs @@ -28,7 +28,8 @@ impl<'a> From<&'a Namespace<'a>> for NamespaceOwned { } impl NamespaceOwned { - #[must_use] pub fn as_ref(&self) -> Namespace<'_> { + #[must_use] + pub fn as_ref(&self) -> Namespace<'_> { Namespace(&self.0) } } diff --git a/crates/xml/src/se.rs b/crates/xml/src/se.rs index ab13622..8c7a6d2 100644 --- a/crates/xml/src/se.rs +++ b/crates/xml/src/se.rs @@ -26,11 +26,8 @@ impl XmlSerialize for Option { namespaces: &HashMap, writer: &mut quick_xml::Writer<&mut Vec>, ) -> std::io::Result<()> { - if let Some(some) = self { - some.serialize(ns, tag, namespaces, writer) - } else { - Ok(()) - } + self.as_ref() + .map_or(Ok(()), |some| some.serialize(ns, tag, namespaces, writer)) } fn attributes<'a>(&self) -> Option>> { @@ -64,15 +61,13 @@ impl XmlSerialize for () { namespaces: &HashMap, writer: &mut quick_xml::Writer<&mut Vec>, ) -> std::io::Result<()> { - let prefix = ns - .and_then(|ns| namespaces.get(&ns)) - .map(|prefix| { - if prefix.is_empty() { - String::new() - } else { - [*prefix, ":"].concat() - } - }); + let prefix = ns.and_then(|ns| namespaces.get(&ns)).map(|prefix| { + if prefix.is_empty() { + String::new() + } else { + [*prefix, ":"].concat() + } + }); let has_prefix = prefix.is_some(); let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat()); if let Some(tagname) = tagname.as_ref() { diff --git a/crates/xml/src/unparsed.rs b/crates/xml/src/unparsed.rs index 37d0708..1a2cdcd 100644 --- a/crates/xml/src/unparsed.rs +++ b/crates/xml/src/unparsed.rs @@ -9,7 +9,8 @@ use crate::{XmlDeserialize, XmlError}; pub struct Unparsed(BytesStart<'static>); impl Unparsed { - #[must_use] pub fn tag_name(&self) -> String { + #[must_use] + pub fn tag_name(&self) -> String { // TODO: respect namespace? String::from_utf8_lossy(self.0.local_name().as_ref()).to_string() } diff --git a/crates/xml/src/value.rs b/crates/xml/src/value.rs index 23d938d..14f2028 100644 --- a/crates/xml/src/value.rs +++ b/crates/xml/src/value.rs @@ -114,15 +114,13 @@ impl XmlSerialize for T { namespaces: &HashMap, writer: &mut quick_xml::Writer<&mut Vec>, ) -> std::io::Result<()> { - let prefix = ns - .and_then(|ns| namespaces.get(&ns)) - .map(|prefix| { - if prefix.is_empty() { - String::new() - } else { - [*prefix, ":"].concat() - } - }); + let prefix = ns.and_then(|ns| namespaces.get(&ns)).map(|prefix| { + if prefix.is_empty() { + String::new() + } else { + [*prefix, ":"].concat() + } + }); let has_prefix = prefix.is_some(); let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat()); if let Some(tagname) = tagname.as_ref() { diff --git a/src/app.rs b/src/app.rs index 08e5bfd..ff523fa 100644 --- a/src/app.rs +++ b/src/app.rs @@ -28,7 +28,11 @@ use tower_sessions::{Expiry, MemoryStore, SessionManagerLayer}; use tracing::Span; use tracing::field::display; -#[allow(clippy::too_many_arguments)] +#[allow( + clippy::too_many_arguments, + clippy::too_many_lines, + clippy::cognitive_complexity +)] pub fn make_app( addr_store: Arc, cal_store: Arc, @@ -36,7 +40,7 @@ pub fn make_app( auth_provider: Arc, frontend_config: FrontendConfig, oidc_config: Option, - nextcloud_login_config: NextcloudLoginConfig, + nextcloud_login_config: &NextcloudLoginConfig, dav_push_enabled: bool, session_cookie_samesite_strict: bool, payload_limit_mb: usize, diff --git a/src/main.rs b/src/main.rs index 5459874..52ff9ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -115,7 +115,7 @@ async fn main() -> Result<()> { principal_store.clone(), config.frontend.clone(), config.oidc.clone(), - config.nextcloud_login.clone(), + &config.nextcloud_login, config.dav_push.enabled, config.http.session_cookie_samesite_strict, config.http.payload_limit_mb,