#![deny(clippy::pedantic)]
#![deny(clippy::unwrap_used)]
#![cfg_attr(not(test), deny(clippy::indexing_slicing))]
#![deny(rustdoc::broken_intra_doc_links)]
#![warn(missing_docs)]
use dav::RequestError;
use http::HeaderValue;
use http::StatusCode;
pub mod caldav;
pub mod carddav;
mod common;
pub mod dav;
pub mod encoding;
pub mod names;
pub mod requests;
pub mod sd;
pub mod xmlutils;
pub use caldav::CalDavClient;
pub use caldav::service_for_url as caldav_service_for_url;
pub use carddav::CardDavClient;
pub use carddav::service_for_url as carddav_service_for_url;
use roxmltree::ExpandedName;
#[derive(Debug, PartialEq)]
pub struct PropertyName<'ns, 'name> {
namespace: &'ns str,
name: &'name str,
}
impl<'ns, 'name> PropertyName<'ns, 'name> {
#[must_use]
pub const fn new(namespace: &'ns str, name: &'name str) -> PropertyName<'ns, 'name> {
PropertyName { namespace, name }
}
}
impl<'ns, 'name> PropertyName<'ns, 'name> {
#[must_use]
pub fn name(&self) -> &'name str {
self.name
}
#[must_use]
pub fn namespace(&self) -> &'ns str {
self.namespace
}
}
impl PartialEq<ExpandedName<'_, '_>> for PropertyName<'_, '_> {
fn eq(&self, other: &ExpandedName<'_, '_>) -> bool {
other.name() == self.name && other.namespace() == Some(self.namespace)
}
}
impl PartialEq<PropertyName<'_, '_>> for ExpandedName<'_, '_> {
fn eq(&self, other: &PropertyName<'_, '_>) -> bool {
self.name() == other.name && self.namespace() == Some(other.namespace)
}
}
#[derive(thiserror::Error, Debug)]
#[error("ns={}, name={}", .0.namespace, .0.name)]
pub struct Precondition<'p>(PropertyName<'p, 'p>);
impl<'p> From<PropertyName<'p, 'p>> for Precondition<'p> {
fn from(value: PropertyName<'p, 'p>) -> Precondition<'p> {
Precondition(value)
}
}
impl<'p> From<Precondition<'p>> for PropertyName<'p, 'p> {
fn from(value: Precondition<'p>) -> PropertyName<'p, 'p> {
value.0
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct FetchedResourceContent {
pub data: String,
pub etag: String,
}
#[derive(Debug, PartialEq, Eq)]
pub struct FetchedResource {
pub href: String,
pub content: Result<FetchedResourceContent, StatusCode>,
}
#[derive(thiserror::Error, Debug)]
pub enum CheckSupportError<E> {
#[error("DAV header missing from the response")]
MissingHeader,
#[error("requested support not advertised by the server")]
NotAdvertised,
#[error("DAV header is not a valid string: {0}")]
HeaderNotAscii(#[from] http::header::ToStrError),
#[error(transparent)]
Request(#[from] RequestError<E>),
#[error("invalid input URL: {0}")]
InvalidInput(#[from] http::Error),
#[error("http request returned {0}")]
BadStatusCode(http::StatusCode),
}
impl<E> From<StatusCode> for CheckSupportError<E> {
fn from(status: StatusCode) -> Self {
CheckSupportError::BadStatusCode(status)
}
}
impl<E> From<dav::CheckSupportParseError> for CheckSupportError<E> {
fn from(error: dav::CheckSupportParseError) -> Self {
match error {
dav::CheckSupportParseError::MissingHeader => CheckSupportError::MissingHeader,
dav::CheckSupportParseError::NotAdvertised => CheckSupportError::NotAdvertised,
dav::CheckSupportParseError::HeaderNotAscii(e) => CheckSupportError::HeaderNotAscii(e),
dav::CheckSupportParseError::BadStatusCode(s) => CheckSupportError::BadStatusCode(s),
}
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct ResourceType {
pub is_collection: bool,
pub is_calendar: bool,
pub is_address_book: bool,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Depth {
Zero,
One,
Infinity,
}
const DEPTH_ZERO: HeaderValue = HeaderValue::from_static("0");
const DEPTH_ONE: HeaderValue = HeaderValue::from_static("1");
const DEPTH_INFINITY: HeaderValue = HeaderValue::from_static("infinity");
impl From<Depth> for HeaderValue {
fn from(value: Depth) -> Self {
match value {
Depth::Zero => DEPTH_ZERO,
Depth::One => DEPTH_ONE,
Depth::Infinity => DEPTH_INFINITY,
}
}
}
impl std::fmt::Display for Depth {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Depth::Zero => "0",
Depth::One => "1",
Depth::Infinity => "infinity",
})
}
}