1use axum::{extract::State, http::StatusCode, Json};
2use caldav_utils::{
3 availability::{get_availability, AvailabilityRequest, AvailabilityResponse},
4 caldav::calendar::Calendar,
5};
6use tracing::info;
7
8pub mod error;
9pub mod state;
10
11pub use crate::{
12 error::{SchedulerError, SchedulerResult},
13 state::CaldavAvailability,
14};
15
16pub async fn get_calendars(
17 client: &reqwest::Client,
18 caldav_state: CaldavAvailability,
19) -> SchedulerResult<(Calendar, Calendar)> {
20 let mut principal = caldav_state.davclient.get_principal(client).await?;
21
22 Ok((
23 principal
24 .get_calendar(client, &caldav_state.availability_calendar)
25 .await?,
26 principal
27 .get_calendar(client, &caldav_state.booked_calendar)
28 .await?,
29 ))
30}
31
32#[axum::debug_handler]
33pub async fn request_availability(
34 State(caldav_state): State<CaldavAvailability>,
35 body: Json<AvailabilityRequest>,
36) -> SchedulerResult<Json<AvailabilityResponse>> {
37 let client = reqwest::Client::new();
41 let (availability_calendar, booked_calendar) = get_calendars(&client, caldav_state).await?;
42 info!(
43 "Found calendars: {}, {}",
44 availability_calendar.path, booked_calendar.path
45 );
46
47 let granularity = chrono::Duration::minutes(30);
48
49 let avail = get_availability(
50 &client,
51 &availability_calendar,
52 &booked_calendar,
53 body.start,
54 body.end,
55 granularity,
56 )
57 .await?;
58
59 Ok(Json(avail))
61}
62
63#[derive(Debug, serde::Deserialize)]
64pub struct BookingRequest {
65 pub start: chrono::DateTime<chrono::Utc>,
66 pub end: chrono::DateTime<chrono::Utc>,
67 pub name: String,
68 pub email: String,
69 pub description: String,
70}
71pub async fn request_booking(
74 State(caldav_state): State<CaldavAvailability>,
75 body: Json<BookingRequest>,
76) -> SchedulerResult<StatusCode> {
77 let client = reqwest::Client::new();
78 let (availability_calendar, booked_calendar) = get_calendars(&client, caldav_state).await?;
79
80 let granularity = chrono::Duration::minutes(30);
81
82 let avail = get_availability(
84 &client,
85 &availability_calendar,
86 &booked_calendar,
87 body.start,
88 body.end,
89 granularity,
90 )
91 .await?;
92
93 let is_available = avail.matrix.iter().all(|it| *it);
97 if !is_available {
98 return Err(SchedulerError::TimeNotAvailable(body.start));
99 }
100
101 let description = format!("email: {}\n{}", body.email, body.description);
102
103 booked_calendar
105 .create_event(&client, body.start, body.end, &body.name, &description)
106 .await?;
107
108 Ok(StatusCode::OK)
109}
110
111pub async fn get_now() -> Result<Json<chrono::DateTime<chrono::Utc>>, StatusCode> {
113 Ok(Json(chrono::Utc::now()))
114}