use std::fmt::Display; use crate::models::*; use chrono::{DateTime, Utc}; #[derive(Debug)] pub enum AuthErr { UnknownSession, UnknownUser, InvalidPassword, NotAnAdmin, Other(Box), } impl Display for AuthErr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::UnknownUser => write!(f, "unknown user"), Self::UnknownSession => write!(f, "unknown session"), Self::InvalidPassword => write!(f, "invalid password"), Self::NotAnAdmin => write!(f, "not an admin"), Self::Other(err) => err.fmt(f), } } } impl std::error::Error for AuthErr {} /// API abstraction providing methods for serving the Gpodder API pub trait GpodderStore: GpodderAuthStore + GpodderDeviceStore + GpodderSubscriptionStore + GpodderEpisodeActionStore { } impl GpodderStore for T where T: GpodderAuthStore + GpodderDeviceStore + GpodderSubscriptionStore + GpodderEpisodeActionStore { } pub trait GpodderAuthStore { /// Retrieve the session with the given session ID fn get_session(&self, session_id: i64) -> Result, AuthErr>; /// Retrieve a paginated list of the given user's sessions, ordered descending by the last seen /// value. fn paginated_sessions(&self, user: &User, page: Page) -> Result, AuthErr>; /// Retrieve the user with the given username fn get_user(&self, username: &str) -> Result, AuthErr>; /// Insert a new user into the data store fn insert_user(&self, username: &str, password_hash: &str) -> Result; /// Create a new session for a user with the given session ID /// /// The `last_seen` timestamp's precision should be at least accurate to the second fn insert_session(&self, session: &Session) -> Result<(), AuthErr>; /// Remove the session with the given session ID fn remove_session(&self, session_id: i64) -> Result<(), AuthErr>; /// Update the session's timestamp fn refresh_session(&self, session: &Session, timestamp: DateTime) -> Result<(), AuthErr>; /// Remove any sessions whose last_seen timestamp is before the given minimum value fn remove_old_sessions(&self, min_last_seen: DateTime) -> Result; /// Return the given page of users, ordered by username fn paginated_users(&self, page: Page) -> Result, AuthErr>; } pub trait GpodderDeviceStore { /// Return all devices associated with the user fn devices_for_user(&self, user: &User) -> Result, AuthErr>; /// Update the information for the given device. If the device doesn't exist yet, it should be /// created without a sync group. fn update_device_info( &self, user: &User, device_id: &str, patch: DevicePatch, ) -> Result<(), AuthErr>; /// Add the devices to the same sync group by: /// /// * Merging the sync groups of all devices already in a sync group /// * Adding all devices not yet in a sync group to the newly merged sync group /// /// # Returns /// /// ID of the final sync group fn merge_sync_groups(&self, user: &User, device_ids: Vec<&str>) -> Result; /// Synchronize the sync group by adding or removing subscriptions to each device so that each /// device's subscription state is the same fn synchronize_sync_group( &self, group_id: i64, time_changed: DateTime, ) -> Result<(), AuthErr>; /// Remove the devices from their respective sync groups fn remove_from_sync_group(&self, user: &User, device_ids: Vec<&str>) -> Result<(), AuthErr>; /// Return all device IDs for the user, grouped per sync group /// /// # Returns /// /// A tuple (unsynced devices, sync groups) fn devices_by_sync_group( &self, user: &User, ) -> Result<(Vec, Vec>), AuthErr>; } pub trait GpodderSubscriptionStore { /// Return the subscriptions for the given device fn subscriptions_for_device( &self, user: &User, device_id: &str, ) -> Result, AuthErr>; /// Return all subscriptions for a given user fn subscriptions_for_user(&self, user: &User) -> Result, AuthErr>; /// Replace the list of subscriptions for a device and all devices in its sync group with the /// given list fn set_subscriptions_for_device( &self, user: &User, device_id: &str, urls: Vec, time_changed: DateTime, ) -> Result<(), AuthErr>; /// Update the list of subscriptions for a device and all devices in its sync group by adding /// and removing the given URLs fn update_subscriptions_for_device( &self, user: &User, device_id: &str, add: Vec, remove: Vec, time_changed: DateTime, ) -> Result<(), AuthErr>; /// Returns the changes in subscriptions since the given timestamp. fn subscription_updates_for_device( &self, user: &User, device_id: &str, since: DateTime, ) -> Result<(Vec, Vec), AuthErr>; } pub trait GpodderEpisodeActionStore { /// Insert the given episode actions into the datastore. fn add_episode_actions( &self, user: &User, actions: Vec, time_changed: DateTime, ) -> Result<(), AuthErr>; /// Retrieve the list of episode actions for the given user. fn episode_actions_for_user( &self, user: &User, since: Option>, podcast: Option, device: Option, aggregated: bool, ) -> Result, AuthErr>; }