mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 16:32:29 +00:00
Error typing for rustical_store as well as some refactoring
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1818,6 +1818,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use actix_web::{
|
|||||||
HttpResponse,
|
HttpResponse,
|
||||||
};
|
};
|
||||||
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
|
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
|
||||||
use rustical_store::store::CalendarStore;
|
use rustical_store::CalendarStore;
|
||||||
|
|
||||||
pub async fn route_delete_calendar<A: CheckAuthentication, C: CalendarStore + ?Sized>(
|
pub async fn route_delete_calendar<A: CheckAuthentication, C: CalendarStore + ?Sized>(
|
||||||
context: Data<CalDavContext<C>>,
|
context: Data<CalDavContext<C>>,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use actix_web::HttpResponse;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
|
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
|
||||||
use rustical_store::calendar::Calendar;
|
use rustical_store::calendar::Calendar;
|
||||||
use rustical_store::store::CalendarStore;
|
use rustical_store::CalendarStore;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||||
@@ -69,10 +69,7 @@ pub async fn route_mkcol_calendar<A: CheckAuthentication, C: CalendarStore + ?Si
|
|||||||
return Err(Error::Unauthorized);
|
return Err(Error::Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
let request: MkcalendarRequest = quick_xml::de::from_str(&body).map_err(|e| {
|
let request: MkcalendarRequest = quick_xml::de::from_str(&body)?;
|
||||||
dbg!(e.to_string());
|
|
||||||
Error::BadRequest
|
|
||||||
})?;
|
|
||||||
let request = request.set.prop;
|
let request = request.set.prop;
|
||||||
|
|
||||||
let calendar = Calendar {
|
let calendar = Calendar {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use rustical_dav::{
|
|||||||
resource::HandlePropfind,
|
resource::HandlePropfind,
|
||||||
};
|
};
|
||||||
use rustical_store::event::Event;
|
use rustical_store::event::Event;
|
||||||
use rustical_store::store::CalendarStore;
|
use rustical_store::CalendarStore;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
@@ -154,10 +154,7 @@ pub async fn route_report_calendar<A: CheckAuthentication, C: CalendarStore + ?S
|
|||||||
return Err(Error::Unauthorized);
|
return Err(Error::Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
let request: ReportRequest = quick_xml::de::from_str(&body).map_err(|err| {
|
let request: ReportRequest = quick_xml::de::from_str(&body)?;
|
||||||
dbg!(err.to_string());
|
|
||||||
Error::InternalError
|
|
||||||
})?;
|
|
||||||
let events = match request.clone() {
|
let events = match request.clone() {
|
||||||
ReportRequest::CalendarQuery(cal_query) => {
|
ReportRequest::CalendarQuery(cal_query) => {
|
||||||
get_events_calendar_query(cal_query, &cid, &cal_store).await?
|
get_events_calendar_query(cal_query, &cid, &cal_store).await?
|
||||||
@@ -178,7 +175,7 @@ pub async fn route_report_calendar<A: CheckAuthentication, C: CalendarStore + ?S
|
|||||||
}
|
}
|
||||||
PropfindType::Propname => {
|
PropfindType::Propname => {
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
return Err(Error::InternalError);
|
return Err(Error::NotImplemented);
|
||||||
}
|
}
|
||||||
PropfindType::Prop(PropElement { prop: prop_tags }) => prop_tags.into(),
|
PropfindType::Prop(PropElement { prop: prop_tags }) => prop_tags.into(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use rustical_dav::error::Error;
|
|||||||
use rustical_dav::resource::{Resource, ResourceService};
|
use rustical_dav::resource::{Resource, ResourceService};
|
||||||
use rustical_dav::xml_snippets::{HrefElement, TextNode};
|
use rustical_dav::xml_snippets::{HrefElement, TextNode};
|
||||||
use rustical_store::calendar::Calendar;
|
use rustical_store::calendar::Calendar;
|
||||||
use rustical_store::store::CalendarStore;
|
use rustical_store::CalendarStore;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use strum::{EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumString, IntoStaticStr, VariantNames};
|
||||||
|
|||||||
44
crates/caldav/src/error.rs
Normal file
44
crates/caldav/src/error.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
use actix_web::{http::StatusCode, HttpResponse};
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Unauthorized")]
|
||||||
|
Unauthorized,
|
||||||
|
|
||||||
|
#[error("Not implemented")]
|
||||||
|
NotImplemented,
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
StoreError(#[from] rustical_store::Error),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
DavError(#[from] rustical_dav::Error),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
XmlDecodeError(#[from] quick_xml::DeError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Other(#[from] anyhow::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl actix_web::ResponseError for Error {
|
||||||
|
fn status_code(&self) -> actix_web::http::StatusCode {
|
||||||
|
match self {
|
||||||
|
Error::StoreError(err) => match err {
|
||||||
|
rustical_store::Error::NotFound => StatusCode::NOT_FOUND,
|
||||||
|
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
},
|
||||||
|
Error::DavError(err) => err.status_code(),
|
||||||
|
Error::Unauthorized => StatusCode::UNAUTHORIZED,
|
||||||
|
Error::XmlDecodeError(_) => StatusCode::BAD_REQUEST,
|
||||||
|
Error::NotImplemented => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
Error::Other(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn error_response(&self) -> actix_web::HttpResponse<actix_web::body::BoxBody> {
|
||||||
|
match self {
|
||||||
|
Error::DavError(err) => err.error_response(),
|
||||||
|
_ => HttpResponse::build(self.status_code()).body(self.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +1,9 @@
|
|||||||
use crate::CalDavContext;
|
use crate::CalDavContext;
|
||||||
use actix_web::http::StatusCode;
|
use crate::Error;
|
||||||
use actix_web::web::{Data, Path};
|
use actix_web::web::{Data, Path};
|
||||||
use actix_web::{HttpResponse, ResponseError};
|
use actix_web::HttpResponse;
|
||||||
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
|
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
|
||||||
use rustical_store::store::CalendarStore;
|
use rustical_store::CalendarStore;
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum Error {
|
|
||||||
#[error(transparent)]
|
|
||||||
Other(#[from] anyhow::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResponseError for Error {
|
|
||||||
fn status_code(&self) -> actix_web::http::StatusCode {
|
|
||||||
match self {
|
|
||||||
Self::Other(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn error_response(&self) -> HttpResponse<actix_web::body::BoxBody> {
|
|
||||||
HttpResponse::build(self.status_code()).body(self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete_event<A: CheckAuthentication, C: CalendarStore + ?Sized>(
|
pub async fn delete_event<A: CheckAuthentication, C: CalendarStore + ?Sized>(
|
||||||
context: Data<CalDavContext<C>>,
|
context: Data<CalDavContext<C>>,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use rustical_dav::error::Error;
|
|||||||
use rustical_dav::resource::{Resource, ResourceService};
|
use rustical_dav::resource::{Resource, ResourceService};
|
||||||
use rustical_dav::xml_snippets::TextNode;
|
use rustical_dav::xml_snippets::TextNode;
|
||||||
use rustical_store::event::Event;
|
use rustical_store::event::Event;
|
||||||
use rustical_store::store::CalendarStore;
|
use rustical_store::CalendarStore;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use strum::{EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumString, IntoStaticStr, VariantNames};
|
||||||
|
|||||||
@@ -6,18 +6,20 @@ use event::resource::EventResource;
|
|||||||
use principal::PrincipalResource;
|
use principal::PrincipalResource;
|
||||||
use root::RootResource;
|
use root::RootResource;
|
||||||
use rustical_auth::CheckAuthentication;
|
use rustical_auth::CheckAuthentication;
|
||||||
use rustical_dav::error::Error;
|
|
||||||
use rustical_dav::propfind::{route_propfind, ServicePrefix};
|
use rustical_dav::propfind::{route_propfind, ServicePrefix};
|
||||||
use rustical_store::store::CalendarStore;
|
use rustical_store::CalendarStore;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
pub mod calendar;
|
pub mod calendar;
|
||||||
|
pub mod error;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod principal;
|
pub mod principal;
|
||||||
pub mod root;
|
pub mod root;
|
||||||
|
|
||||||
|
pub use error::Error;
|
||||||
|
|
||||||
pub struct CalDavContext<C: CalendarStore + ?Sized> {
|
pub struct CalDavContext<C: CalendarStore + ?Sized> {
|
||||||
pub store: Arc<RwLock<C>>,
|
pub store: Arc<RwLock<C>>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use async_trait::async_trait;
|
|||||||
use rustical_auth::AuthInfo;
|
use rustical_auth::AuthInfo;
|
||||||
use rustical_dav::resource::{Resource, ResourceService};
|
use rustical_dav::resource::{Resource, ResourceService};
|
||||||
use rustical_dav::xml_snippets::HrefElement;
|
use rustical_dav::xml_snippets::HrefElement;
|
||||||
use rustical_store::store::CalendarStore;
|
use rustical_store::CalendarStore;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use strum::{EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumString, IntoStaticStr, VariantNames};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|||||||
@@ -5,3 +5,5 @@ pub mod propfind;
|
|||||||
pub mod resource;
|
pub mod resource;
|
||||||
pub mod xml;
|
pub mod xml;
|
||||||
pub mod xml_snippets;
|
pub mod xml_snippets;
|
||||||
|
|
||||||
|
pub use error::Error;
|
||||||
|
|||||||
@@ -29,3 +29,4 @@ regex = "1.10"
|
|||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
rstest = "0.18.2"
|
rstest = "0.18.2"
|
||||||
rstest_reuse = "0.6.0"
|
rstest_reuse = "0.6.0"
|
||||||
|
thiserror = "1.0.61"
|
||||||
|
|||||||
23
crates/store/src/error.rs
Normal file
23
crates/store/src/error.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Not found")]
|
||||||
|
NotFound,
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
SqlxError(sqlx::Error),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Other(#[from] anyhow::Error),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
ParserError(#[from] ical::parser::ParserError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<sqlx::Error> for Error {
|
||||||
|
fn from(value: sqlx::Error) -> Self {
|
||||||
|
match value {
|
||||||
|
sqlx::Error::RowNotFound => Error::NotFound,
|
||||||
|
err => Error::SqlxError(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
use crate::timestamps::{parse_datetime, parse_duration};
|
use crate::{
|
||||||
|
timestamps::{parse_datetime, parse_duration},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use chrono::{Duration, NaiveDateTime, Timelike};
|
use chrono::{Duration, NaiveDateTime, Timelike};
|
||||||
use ical::parser::{ical::component::IcalCalendar, Component};
|
use ical::parser::{ical::component::IcalCalendar, Component};
|
||||||
@@ -51,14 +54,14 @@ impl Serialize for Event {
|
|||||||
impl Event {
|
impl Event {
|
||||||
// https://datatracker.ietf.org/doc/html/rfc4791#section-4.1
|
// https://datatracker.ietf.org/doc/html/rfc4791#section-4.1
|
||||||
// MUST NOT contain more than one calendar objects (VEVENT, VTODO, VJOURNAL)
|
// MUST NOT contain more than one calendar objects (VEVENT, VTODO, VJOURNAL)
|
||||||
pub fn from_ics(uid: String, ics: String) -> Result<Self> {
|
pub fn from_ics(uid: String, ics: String) -> Result<Self, Error> {
|
||||||
let mut parser = ical::IcalParser::new(BufReader::new(ics.as_bytes()));
|
let mut parser = ical::IcalParser::new(BufReader::new(ics.as_bytes()));
|
||||||
let cal = parser.next().ok_or(anyhow!("no calendar :("))??;
|
let cal = parser.next().ok_or(Error::NotFound)??;
|
||||||
if parser.next().is_some() {
|
if parser.next().is_some() {
|
||||||
return Err(anyhow!("multiple calendars!"));
|
return Err(anyhow!("multiple calendars!").into());
|
||||||
}
|
}
|
||||||
if cal.events.len() != 1 {
|
if cal.events.len() != 1 {
|
||||||
return Err(anyhow!("multiple or no events"));
|
return Err(anyhow!("multiple or no events").into());
|
||||||
}
|
}
|
||||||
let event = Self { uid, cal, ics };
|
let event = Self { uid, cal, ics };
|
||||||
// Run getters now to validate the input and ensure that they'll work later on
|
// Run getters now to validate the input and ensure that they'll work later on
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
pub mod calendar;
|
pub mod calendar;
|
||||||
|
pub mod error;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod sqlite_store;
|
pub mod sqlite_store;
|
||||||
pub mod store;
|
pub mod store;
|
||||||
pub mod timestamps;
|
pub mod timestamps;
|
||||||
pub mod toml_store;
|
pub mod toml_store;
|
||||||
|
pub use error::Error;
|
||||||
|
pub use store::CalendarStore;
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use anyhow::Result;
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use sqlx::{sqlite::SqliteConnectOptions, Pool, Sqlite, SqlitePool};
|
use sqlx::{sqlite::SqliteConnectOptions, Pool, Sqlite, SqlitePool};
|
||||||
|
|
||||||
use crate::{calendar::Calendar, event::Event, store::CalendarStore};
|
use crate::event::Event;
|
||||||
|
use crate::{calendar::Calendar, CalendarStore, Error};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SqliteCalendarStore {
|
pub struct SqliteCalendarStore {
|
||||||
@@ -22,16 +23,16 @@ struct EventRow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<EventRow> for Event {
|
impl TryFrom<EventRow> for Event {
|
||||||
type Error = anyhow::Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(value: EventRow) -> Result<Self> {
|
fn try_from(value: EventRow) -> Result<Self, Error> {
|
||||||
Event::from_ics(value.uid, value.ics)
|
Event::from_ics(value.uid, value.ics)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl CalendarStore for SqliteCalendarStore {
|
impl CalendarStore for SqliteCalendarStore {
|
||||||
async fn get_calendar(&self, id: &str) -> Result<Calendar> {
|
async fn get_calendar(&self, id: &str) -> Result<Calendar, Error> {
|
||||||
let cal = sqlx::query_as!(
|
let cal = sqlx::query_as!(
|
||||||
Calendar,
|
Calendar,
|
||||||
"SELECT id, name, owner, description, color, timezone FROM calendars WHERE id = ?",
|
"SELECT id, name, owner, description, color, timezone FROM calendars WHERE id = ?",
|
||||||
@@ -42,7 +43,7 @@ impl CalendarStore for SqliteCalendarStore {
|
|||||||
Ok(cal)
|
Ok(cal)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_calendars(&self, _owner: &str) -> Result<Vec<Calendar>> {
|
async fn get_calendars(&self, _owner: &str) -> Result<Vec<Calendar>, Error> {
|
||||||
let cals = sqlx::query_as!(
|
let cals = sqlx::query_as!(
|
||||||
Calendar,
|
Calendar,
|
||||||
"SELECT id, name, owner, description, color, timezone FROM calendars"
|
"SELECT id, name, owner, description, color, timezone FROM calendars"
|
||||||
@@ -52,7 +53,7 @@ impl CalendarStore for SqliteCalendarStore {
|
|||||||
Ok(cals)
|
Ok(cals)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn insert_calendar(&mut self, cid: String, calendar: Calendar) -> Result<()> {
|
async fn insert_calendar(&mut self, cid: String, calendar: Calendar) -> Result<(), Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO calendars (id, name, description, owner, color, timezone) VALUES (?, ?, ?, ?, ?, ?)",
|
"INSERT INTO calendars (id, name, description, owner, color, timezone) VALUES (?, ?, ?, ?, ?, ?)",
|
||||||
cid,
|
cid,
|
||||||
@@ -65,14 +66,14 @@ impl CalendarStore for SqliteCalendarStore {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete_calendar(&mut self, cid: &str) -> Result<()> {
|
async fn delete_calendar(&mut self, cid: &str) -> Result<(), Error> {
|
||||||
sqlx::query!("DELETE FROM calendars WHERE id = ?", cid)
|
sqlx::query!("DELETE FROM calendars WHERE id = ?", cid)
|
||||||
.execute(&self.db)
|
.execute(&self.db)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_events(&self, cid: &str) -> Result<Vec<Event>> {
|
async fn get_events(&self, cid: &str) -> Result<Vec<Event>, Error> {
|
||||||
sqlx::query_as!(EventRow, "SELECT uid, ics FROM events WHERE cid = ?", cid)
|
sqlx::query_as!(EventRow, "SELECT uid, ics FROM events WHERE cid = ?", cid)
|
||||||
.fetch_all(&self.db)
|
.fetch_all(&self.db)
|
||||||
.await?
|
.await?
|
||||||
@@ -81,7 +82,7 @@ impl CalendarStore for SqliteCalendarStore {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_event(&self, cid: &str, uid: &str) -> Result<Event> {
|
async fn get_event(&self, cid: &str, uid: &str) -> Result<Event, Error> {
|
||||||
let event = sqlx::query_as!(
|
let event = sqlx::query_as!(
|
||||||
EventRow,
|
EventRow,
|
||||||
"SELECT uid, ics FROM events where cid = ? AND uid = ?",
|
"SELECT uid, ics FROM events where cid = ? AND uid = ?",
|
||||||
@@ -94,7 +95,7 @@ impl CalendarStore for SqliteCalendarStore {
|
|||||||
Ok(event)
|
Ok(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn upsert_event(&mut self, cid: String, uid: String, ics: String) -> Result<()> {
|
async fn upsert_event(&mut self, cid: String, uid: String, ics: String) -> Result<(), Error> {
|
||||||
// Do this extra step to ensure that the input is actually valid
|
// Do this extra step to ensure that the input is actually valid
|
||||||
let _ = Event::from_ics(uid.to_owned(), ics.to_owned())?;
|
let _ = Event::from_ics(uid.to_owned(), ics.to_owned())?;
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
@@ -108,7 +109,7 @@ impl CalendarStore for SqliteCalendarStore {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete_event(&mut self, cid: &str, uid: &str) -> Result<()> {
|
async fn delete_event(&mut self, cid: &str, uid: &str) -> Result<(), Error> {
|
||||||
sqlx::query!("DELETE FROM events WHERE cid = ? AND uid = ?", cid, uid)
|
sqlx::query!("DELETE FROM events WHERE cid = ? AND uid = ?", cid, uid)
|
||||||
.execute(&self.db)
|
.execute(&self.db)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
use crate::{calendar::Calendar, event::Event};
|
use crate::{calendar::Calendar, event::Event};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait CalendarStore: Send + Sync + 'static {
|
pub trait CalendarStore: Send + Sync + 'static {
|
||||||
async fn get_calendar(&self, id: &str) -> Result<Calendar>;
|
async fn get_calendar(&self, id: &str) -> Result<Calendar, Error>;
|
||||||
async fn get_calendars(&self, owner: &str) -> Result<Vec<Calendar>>;
|
async fn get_calendars(&self, owner: &str) -> Result<Vec<Calendar>, Error>;
|
||||||
async fn insert_calendar(&mut self, cid: String, calendar: Calendar) -> Result<()>;
|
async fn insert_calendar(&mut self, cid: String, calendar: Calendar) -> Result<(), Error>;
|
||||||
async fn delete_calendar(&mut self, cid: &str) -> Result<()>;
|
async fn delete_calendar(&mut self, cid: &str) -> Result<(), Error>;
|
||||||
|
|
||||||
async fn get_events(&self, cid: &str) -> Result<Vec<Event>>;
|
async fn get_events(&self, cid: &str) -> Result<Vec<Event>, Error>;
|
||||||
async fn get_event(&self, cid: &str, uid: &str) -> Result<Event>;
|
async fn get_event(&self, cid: &str, uid: &str) -> Result<Event, Error>;
|
||||||
async fn upsert_event(&mut self, cid: String, uid: String, ics: String) -> Result<()>;
|
async fn upsert_event(&mut self, cid: String, uid: String, ics: String) -> Result<(), Error>;
|
||||||
async fn delete_event(&mut self, cid: &str, uid: &str) -> Result<()>;
|
async fn delete_event(&mut self, cid: &str, uid: &str) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use crate::calendar::Calendar;
|
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
use crate::store::CalendarStore;
|
use crate::{calendar::Calendar, CalendarStore, Error};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::anyhow;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::{hash_map::Entry, HashMap};
|
use std::collections::{hash_map::Entry, HashMap};
|
||||||
@@ -31,7 +30,7 @@ impl TomlCalendarStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn save(&self) -> Result<()> {
|
pub async fn save(&self) -> anyhow::Result<()> {
|
||||||
let output = toml::to_string_pretty(&self)?;
|
let output = toml::to_string_pretty(&self)?;
|
||||||
if let Some(path) = &self.path {
|
if let Some(path) = &self.path {
|
||||||
let mut file = File::create(path).await?;
|
let mut file = File::create(path).await?;
|
||||||
@@ -43,11 +42,11 @@ impl TomlCalendarStore {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl CalendarStore for TomlCalendarStore {
|
impl CalendarStore for TomlCalendarStore {
|
||||||
async fn get_calendar(&self, id: &str) -> Result<Calendar> {
|
async fn get_calendar(&self, id: &str) -> Result<Calendar, Error> {
|
||||||
Ok(self.calendars.get(id).ok_or(anyhow!("not found"))?.clone())
|
Ok(self.calendars.get(id).ok_or(Error::NotFound)?.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_calendars(&self, user: &str) -> Result<Vec<Calendar>> {
|
async fn get_calendars(&self, user: &str) -> Result<Vec<Calendar>, Error> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.calendars
|
.calendars
|
||||||
.values()
|
.values()
|
||||||
@@ -56,9 +55,9 @@ impl CalendarStore for TomlCalendarStore {
|
|||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn insert_calendar(&mut self, cid: String, calendar: Calendar) -> Result<()> {
|
async fn insert_calendar(&mut self, cid: String, calendar: Calendar) -> Result<(), Error> {
|
||||||
match self.calendars.entry(cid) {
|
match self.calendars.entry(cid) {
|
||||||
Entry::Occupied(_) => Err(anyhow!("calendar already exists")),
|
Entry::Occupied(_) => Err(anyhow!("calendar already exists").into()),
|
||||||
Entry::Vacant(v) => {
|
Entry::Vacant(v) => {
|
||||||
v.insert(calendar);
|
v.insert(calendar);
|
||||||
self.save().await.unwrap();
|
self.save().await.unwrap();
|
||||||
@@ -67,13 +66,13 @@ impl CalendarStore for TomlCalendarStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete_calendar(&mut self, cid: &str) -> Result<()> {
|
async fn delete_calendar(&mut self, cid: &str) -> Result<(), Error> {
|
||||||
self.events.remove(cid);
|
self.events.remove(cid);
|
||||||
self.save().await.unwrap();
|
self.save().await.unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_events(&self, cid: &str) -> Result<Vec<Event>> {
|
async fn get_events(&self, cid: &str) -> Result<Vec<Event>, Error> {
|
||||||
if let Some(events) = self.events.get(cid) {
|
if let Some(events) = self.events.get(cid) {
|
||||||
Ok(events.values().cloned().collect())
|
Ok(events.values().cloned().collect())
|
||||||
} else {
|
} else {
|
||||||
@@ -81,19 +80,19 @@ impl CalendarStore for TomlCalendarStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_event(&self, cid: &str, uid: &str) -> Result<Event> {
|
async fn get_event(&self, cid: &str, uid: &str) -> Result<Event, Error> {
|
||||||
let events = self.events.get(cid).ok_or(anyhow!("not found"))?;
|
let events = self.events.get(cid).ok_or(anyhow!("not found"))?;
|
||||||
Ok(events.get(uid).ok_or(anyhow!("not found"))?.clone())
|
Ok(events.get(uid).ok_or(Error::NotFound)?.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn upsert_event(&mut self, cid: String, uid: String, ics: String) -> Result<()> {
|
async fn upsert_event(&mut self, cid: String, uid: String, ics: String) -> Result<(), Error> {
|
||||||
let events = self.events.entry(cid).or_default();
|
let events = self.events.entry(cid).or_default();
|
||||||
events.insert(uid.clone(), Event::from_ics(uid, ics)?);
|
events.insert(uid.clone(), Event::from_ics(uid, ics)?);
|
||||||
self.save().await.unwrap();
|
self.save().await.unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete_event(&mut self, cid: &str, uid: &str) -> Result<()> {
|
async fn delete_event(&mut self, cid: &str, uid: &str) -> Result<(), Error> {
|
||||||
if let Some(events) = self.events.get_mut(cid) {
|
if let Some(events) = self.events.get_mut(cid) {
|
||||||
events.remove(uid);
|
events.remove(uid);
|
||||||
self.save().await?;
|
self.save().await?;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use actix_web::dev::{ServiceFactory, ServiceRequest, ServiceResponse};
|
|||||||
use actix_web::middleware::{Logger, NormalizePath};
|
use actix_web::middleware::{Logger, NormalizePath};
|
||||||
use actix_web::{web, App};
|
use actix_web::{web, App};
|
||||||
use rustical_auth::CheckAuthentication;
|
use rustical_auth::CheckAuthentication;
|
||||||
use rustical_store::store::CalendarStore;
|
use rustical_store::CalendarStore;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
pub fn make_app<CS: CalendarStore + ?Sized, A: CheckAuthentication>(
|
pub fn make_app<CS: CalendarStore + ?Sized, A: CheckAuthentication>(
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use clap::Parser;
|
|||||||
use config::{CalendarStoreConfig, SqliteCalendarStoreConfig, TomlCalendarStoreConfig};
|
use config::{CalendarStoreConfig, SqliteCalendarStoreConfig, TomlCalendarStoreConfig};
|
||||||
use rustical_auth::AuthProvider;
|
use rustical_auth::AuthProvider;
|
||||||
use rustical_store::sqlite_store::{create_db_pool, SqliteCalendarStore};
|
use rustical_store::sqlite_store::{create_db_pool, SqliteCalendarStore};
|
||||||
use rustical_store::store::CalendarStore;
|
|
||||||
use rustical_store::toml_store::TomlCalendarStore;
|
use rustical_store::toml_store::TomlCalendarStore;
|
||||||
|
use rustical_store::CalendarStore;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|||||||
Reference in New Issue
Block a user