diff --git a/.idea/runConfigurations/Run_recurring_event_api.xml b/.idea/runConfigurations/Run_recurring_event_api.xml
new file mode 100644
index 0000000..27957c8
--- /dev/null
+++ b/.idea/runConfigurations/Run_recurring_event_api.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/handler/ics.rs b/src/handler/ics.rs
new file mode 100644
index 0000000..48c08dd
--- /dev/null
+++ b/src/handler/ics.rs
@@ -0,0 +1,46 @@
+use crate::handler::EventQuery;
+use crate::service::date_service::get_dates;
+use axum::extract::Query;
+use axum::http::header::CONTENT_TYPE;
+use axum::response::{IntoResponse, Response};
+use axum::routing::get;
+use axum::Router;
+use chrono::TimeDelta;
+use icalendar::{Calendar, Class, Component, Event, EventLike};
+
+const TEXT_CALENDAR: &'static str = "text/calendar";
+
+pub(super) fn router() -> Router {
+ Router::new().route("/ics", get(get_ics))
+}
+
+async fn get_ics(Query(query): Query) -> impl IntoResponse {
+ let start = query.start_date_time;
+ let end = query
+ .end_date
+ .unwrap_or_else(|| (start + TimeDelta::days(365)).date());
+ let mut calendar = Calendar::new();
+ calendar.name(&query.title);
+ get_dates(
+ start,
+ end,
+ query.recurring,
+ query.avoid_weekends.unwrap_or(false),
+ )
+ .into_iter()
+ .for_each(|date_time| {
+ let mut event = Event::new();
+ if query.all_day {
+ event.all_day(date_time.date());
+ } else {
+ event.starts(date_time);
+ };
+ event.summary(&query.title).class(Class::Private);
+ calendar.push(event);
+ });
+
+ Response::builder()
+ .header(CONTENT_TYPE, TEXT_CALENDAR)
+ .body(calendar.done().to_string())
+ .unwrap()
+}
diff --git a/src/handler/index.rs b/src/handler/index.rs
new file mode 100644
index 0000000..9c883e9
--- /dev/null
+++ b/src/handler/index.rs
@@ -0,0 +1,24 @@
+use crate::handler::EventQuery;
+use crate::service::date_service::get_dates;
+use axum::extract::Query;
+use axum::response::IntoResponse;
+use axum::routing::get;
+use axum::{Json, Router};
+use chrono::TimeDelta;
+
+pub(super) fn router() -> Router {
+ Router::new().route("/", get(get_calendar))
+}
+
+async fn get_calendar(Query(query): Query) -> impl IntoResponse {
+ let start = query.start_date_time;
+ let end = query
+ .end_date
+ .unwrap_or_else(|| (start + TimeDelta::days(365)).date());
+ Json(get_dates(
+ start,
+ end,
+ query.recurring,
+ query.avoid_weekends.unwrap_or(false),
+ ))
+}
diff --git a/src/handler/mod.rs b/src/handler/mod.rs
new file mode 100644
index 0000000..89b7555
--- /dev/null
+++ b/src/handler/mod.rs
@@ -0,0 +1,27 @@
+use crate::model::models::{Move, Recurring};
+use axum::Router;
+use chrono::{NaiveDate, NaiveDateTime};
+use serde::Deserialize;
+
+mod ics;
+mod index;
+
+pub(crate) fn router() -> Router {
+ Router::new().merge(index::router()).merge(ics::router())
+}
+
+#[derive(Deserialize)]
+struct EventQuery {
+ /// Start date
+ start_date_time: NaiveDateTime,
+ /// Recurring date
+ recurring: Recurring,
+ // If set, will ignore time on start_date
+ all_day: bool,
+ /// Title of the event
+ title: String,
+ // Optionals
+ end_date: Option,
+ avoid_weekends: Option,
+ on_collition: Option,
+}
diff --git a/src/main.rs b/src/main.rs
index f403220..99fc0d4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,163 +1,18 @@
-use axum::extract::Query;
-use axum::http::header::CONTENT_TYPE;
-use axum::response::{IntoResponse, Response};
-use axum::routing::get;
-use axum::{Json, Router};
-use chrono::{Datelike, Months, NaiveDate, NaiveDateTime, TimeDelta, Weekday};
-use icalendar::{Calendar, Class, Component, Event, EventLike};
-use serde::Deserialize;
-use std::ops::Sub;
+mod handler;
+mod model;
+mod service;
-const TEXT_CALENDAR: &'static str = "text/calendar";
-
-#[derive(Deserialize)]
-enum Recurring {
- Daily,
- Weeky,
- BiWeekly,
- Monthly,
- Quarterly,
- Yearly,
-}
-
-#[derive(Deserialize)]
-enum Move {
- Before,
- After,
-}
-
-#[derive(Deserialize)]
-struct EventQuery {
- /// Start date
- start_date_time: NaiveDateTime,
- /// Recurring date
- recurring: Recurring,
- // If set, will ignore time on start_date
- all_day: bool,
- /// Title of the event
- title: String,
- // Optionals
- end_date: Option,
- avoid_weekends: Option,
- on_collition: Option,
-}
-
-#[derive(Deserialize)]
-enum Time {
- /// Specific time at day
- DateTime {
- start: NaiveDateTime,
- end: NaiveDateTime,
- },
- /// All day
- Date(NaiveDate),
-}
-
-#[derive(Deserialize)]
-struct EventBody {
- /// Start date
- time: Time,
- /// Recurring date
- recurring: Recurring,
- /// Title of the event
- title: String,
- // Optionals
- end_date: Option,
- ignore_weekdays: Vec,
- on_collition: Option,
-}
-
-/// TODO create ical object
-/// TODO implement Before / After
-/// TODO what if start_date is weekend
-async fn get_calendar(Query(query): Query) -> impl IntoResponse {
- let start = query.start_date_time;
- let end = query
- .end_date
- .unwrap_or_else(|| (start + TimeDelta::days(365)).date());
- Json(get_dates(
- start,
- end,
- query.recurring,
- query.avoid_weekends.unwrap_or(false),
- ))
-}
-
-async fn get_ics(Query(query): Query) -> impl IntoResponse {
- let start = query.start_date_time;
- let end = query
- .end_date
- .unwrap_or_else(|| (start + TimeDelta::days(365)).date());
- let mut calendar = Calendar::new();
- calendar.name(&query.title);
- get_dates(
- start,
- end,
- query.recurring,
- query.avoid_weekends.unwrap_or(false),
- )
- .into_iter()
- .for_each(|date_time| {
- let mut event = Event::new();
- if query.all_day {
- event.all_day(date_time.date());
- } else {
- event.starts(date_time);
- };
- event.summary(&query.title).class(Class::Private);
- calendar.push(event);
- });
-
- Response::builder()
- .header(CONTENT_TYPE, TEXT_CALENDAR)
- .body(calendar.done().to_string())
- .unwrap()
-}
-
-fn get_dates(
- start: NaiveDateTime,
- end: NaiveDate,
- recurring: Recurring,
- avoid_weekends: bool,
-) -> Vec {
- let mut events = vec![start];
- let mut previous_date_time = start;
- let mut baseline = start;
- while previous_date_time.date() < end {
- let next = match recurring {
- Recurring::Daily => todo!(),
- Recurring::Weeky => todo!(),
- Recurring::BiWeekly => todo!(),
- Recurring::Monthly => {
- baseline = baseline.checked_add_months(Months::new(1)).unwrap();
- if let true = avoid_weekends {
- match baseline.weekday() {
- Weekday::Sat => baseline.sub(TimeDelta::days(1)),
- Weekday::Sun => baseline.sub(TimeDelta::days(2)),
- _ => baseline,
- }
- } else {
- baseline
- }
- }
- Recurring::Quarterly => todo!(),
- Recurring::Yearly => todo!(),
- };
- events.push(next);
- previous_date_time = next;
- }
- events
-}
+use axum::Router;
#[tokio::main]
async fn main() {
const PORT: &'static str = "8000";
- let app = Router::new()
- .route("/", get(get_calendar))
- .route("/ics", get(get_ics));
+ let app = Router::new().merge(handler::router());
println!("Starting Application on port {PORT}");
- let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{PORT}")).await.unwrap();
+ let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{PORT}"))
+ .await
+ .unwrap();
axum::serve(listener, app).await.unwrap();
}
diff --git a/src/model/mod.rs b/src/model/mod.rs
new file mode 100644
index 0000000..3605c98
--- /dev/null
+++ b/src/model/mod.rs
@@ -0,0 +1 @@
+pub(crate) mod models;
diff --git a/src/model/models.rs b/src/model/models.rs
new file mode 100644
index 0000000..b4be348
--- /dev/null
+++ b/src/model/models.rs
@@ -0,0 +1,17 @@
+use serde::Deserialize;
+
+#[derive(Deserialize)]
+pub enum Recurring {
+ Daily,
+ Weeky,
+ BiWeekly,
+ Monthly,
+ Quarterly,
+ Yearly,
+}
+
+#[derive(Deserialize)]
+pub enum Move {
+ Before,
+ After,
+}
diff --git a/src/service/date_service.rs b/src/service/date_service.rs
new file mode 100644
index 0000000..b7be209
--- /dev/null
+++ b/src/service/date_service.rs
@@ -0,0 +1,39 @@
+use crate::model::models::Recurring;
+use chrono::{Datelike, Months, NaiveDate, NaiveDateTime, TimeDelta, Weekday};
+
+/// TODO implement Before / After
+/// TODO what if start_date is weekend
+pub fn get_dates(
+ start: NaiveDateTime,
+ end: NaiveDate,
+ recurring: Recurring,
+ avoid_weekends: bool,
+) -> Vec {
+ let mut events = vec![start];
+ let mut previous_date_time = start;
+ let mut baseline = start;
+ while previous_date_time.date() < end {
+ let next = match recurring {
+ Recurring::Daily => todo!(),
+ Recurring::Weeky => todo!(),
+ Recurring::BiWeekly => todo!(),
+ Recurring::Monthly => {
+ baseline = baseline.checked_add_months(Months::new(1)).unwrap();
+ if let true = avoid_weekends {
+ match baseline.weekday() {
+ Weekday::Sat => baseline - TimeDelta::days(1),
+ Weekday::Sun => baseline - TimeDelta::days(2),
+ _ => baseline,
+ }
+ } else {
+ baseline
+ }
+ }
+ Recurring::Quarterly => todo!(),
+ Recurring::Yearly => todo!(),
+ };
+ events.push(next);
+ previous_date_time = next;
+ }
+ events
+}
diff --git a/src/service/mod.rs b/src/service/mod.rs
new file mode 100644
index 0000000..6097c33
--- /dev/null
+++ b/src/service/mod.rs
@@ -0,0 +1 @@
+pub(crate) mod date_service;