add todo now adds alarms to start and end of todo
more sensible name for submodule `caldav` of `caldav`
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
use http::{StatusCode, Uri};
|
||||
use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder};
|
||||
use hyper_util::client::legacy::Client;
|
||||
use hyper_util::client::legacy::connect::HttpConnector;
|
||||
use hyper_util::rt::TokioExecutor;
|
||||
use icalendar::{Calendar, CalendarComponent};
|
||||
use libdav::caldav::{FindCalendarHomeSet, FindCalendars, GetCalendarResources};
|
||||
use libdav::dav::{FoundCollection, PutResource, PutResourceResponse, WebDavClient, WebDavError};
|
||||
use libdav::{CalDavClient, FetchedResource};
|
||||
use rmcp::ErrorData;
|
||||
use rmcp::model::ErrorCode;
|
||||
use thiserror::Error;
|
||||
use tower_http::auth::AddAuthorization;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub(crate) type AuthorizedCaldavClient =
|
||||
CalDavClient<AddAuthorization<Client<HttpsConnector<HttpConnector>, String>>>;
|
||||
|
||||
pub(crate) fn get_caldav_client(
|
||||
webdav_uri: Uri,
|
||||
username: &str,
|
||||
password: &str,
|
||||
) -> AuthorizedCaldavClient {
|
||||
let https_connector: HttpsConnector<HttpConnector> = HttpsConnectorBuilder::new()
|
||||
.with_native_roots()
|
||||
.unwrap()
|
||||
.https_only()
|
||||
.enable_http1()
|
||||
.build();
|
||||
|
||||
let https_client: Client<HttpsConnector<HttpConnector>, _> =
|
||||
Client::builder(TokioExecutor::new()).build(https_connector);
|
||||
|
||||
let https_client_with_auth = AddAuthorization::basic(https_client, username, password);
|
||||
|
||||
CalDavClient::new(WebDavClient::new(webdav_uri, https_client_with_auth))
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub(crate) enum CalendarError {
|
||||
#[error("failed to get user principal")]
|
||||
UserPrincipalError(
|
||||
#[from] libdav::dav::FindCurrentUserPrincipalError<hyper_util::client::legacy::Error>,
|
||||
),
|
||||
#[error("User Principal does not exist")]
|
||||
NoUserPrincipal,
|
||||
#[error("webdav error")]
|
||||
WebDavError(#[from] WebDavError<hyper_util::client::legacy::Error>),
|
||||
#[error("unavailable resource")]
|
||||
UnavailableResourceError(StatusCode),
|
||||
#[error("failed Parsing a caldav resource")]
|
||||
ParsingError(String),
|
||||
}
|
||||
|
||||
impl From<CalendarError> for ErrorData {
|
||||
fn from(value: CalendarError) -> Self {
|
||||
use crate::caldav::client::CalendarError::*;
|
||||
|
||||
match value {
|
||||
UserPrincipalError(e) => ErrorData::new(ErrorCode::INTERNAL_ERROR, e.to_string(), None),
|
||||
NoUserPrincipal => ErrorData::new(
|
||||
ErrorCode::RESOURCE_NOT_FOUND,
|
||||
"no user principal".to_string(),
|
||||
None,
|
||||
),
|
||||
WebDavError(e) => ErrorData::new(ErrorCode::INTERNAL_ERROR, e.to_string(), None),
|
||||
UnavailableResourceError(e) => {
|
||||
ErrorData::new(ErrorCode::RESOURCE_NOT_FOUND, e.to_string(), None)
|
||||
}
|
||||
ParsingError(e) => ErrorData::new(ErrorCode::INTERNAL_ERROR, e.to_string(), None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn find_calendars(
|
||||
caldav_client: &AuthorizedCaldavClient,
|
||||
) -> Result<Vec<FoundCollection>, CalendarError> {
|
||||
use CalendarError::*;
|
||||
|
||||
let user_principal = caldav_client
|
||||
.find_current_user_principal()
|
||||
.await?
|
||||
.ok_or(NoUserPrincipal)?;
|
||||
|
||||
let calendar_home_sets = caldav_client
|
||||
.request(FindCalendarHomeSet::new(&user_principal))
|
||||
.await?
|
||||
.home_sets;
|
||||
|
||||
Ok(caldav_client
|
||||
.request(FindCalendars::new(&calendar_home_sets[0]))
|
||||
.await?
|
||||
.calendars)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_calendar_resources(
|
||||
caldav_client: &AuthorizedCaldavClient,
|
||||
calendar: &FoundCollection,
|
||||
) -> Result<Vec<FetchedResource>, CalendarError> {
|
||||
let calendar_resources = caldav_client
|
||||
.request(GetCalendarResources::new(calendar.href.as_str()))
|
||||
.await
|
||||
.map_err(CalendarError::WebDavError)?
|
||||
.resources;
|
||||
|
||||
Ok(calendar_resources)
|
||||
}
|
||||
|
||||
pub(crate) async fn upload_components(
|
||||
authorized_client: &AuthorizedCaldavClient,
|
||||
calendar: &FoundCollection,
|
||||
components: Vec<impl Into<CalendarComponent>>,
|
||||
) -> Result<PutResourceResponse, CalendarError> {
|
||||
println!("uploading components to {}", calendar.href);
|
||||
let mut upload_calendar = Calendar::new();
|
||||
|
||||
for todo in components {
|
||||
upload_calendar.push(todo);
|
||||
}
|
||||
|
||||
Ok(authorized_client
|
||||
.request(
|
||||
PutResource::new(format!("{}{}.ics", calendar.href, Uuid::new_v4()).as_str())
|
||||
.create(upload_calendar.to_string().leak(), "text/caldav"),
|
||||
)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_components(
|
||||
authorized_client: &AuthorizedCaldavClient,
|
||||
calendar: &FoundCollection,
|
||||
) -> Result<Vec<CalendarComponent>, CalendarError> {
|
||||
use CalendarError::*;
|
||||
|
||||
let mut components: Vec<CalendarComponent> = Vec::new();
|
||||
|
||||
let resources = get_calendar_resources(authorized_client, calendar).await?;
|
||||
for i in resources {
|
||||
let calendar: Calendar = i
|
||||
.content
|
||||
.map_err(UnavailableResourceError)?
|
||||
.data
|
||||
.parse()
|
||||
.map_err(ParsingError)?;
|
||||
components.extend(calendar.components);
|
||||
}
|
||||
|
||||
Ok(components)
|
||||
}
|
||||
Reference in New Issue
Block a user