synd-runtime 0.4.0-rc.1

Runtime session and singleton daemon lifecycle for syndicationd
use std::time::Duration;

use crate::{CapabilitySet, Result};
#[cfg(test)]
pub(crate) use renewal::SessionRenewalObserver;
use renewal::{SessionLeaseRenewal, SessionLeaseRenewalHandle, SessionRenewalSchedule};
use synd_protocol::{
    capability,
    session::{CloseSessionRequest, SessionId, SessionLease},
};

mod renewal;

pub struct Session {
    client: synd_client::Client,
    available_capabilities: CapabilitySet,
    handle: Handle,
}

impl Session {
    pub fn new(
        client: synd_client::Client,
        available_capabilities: CapabilitySet,
        handle: Handle,
    ) -> Self {
        Self {
            client,
            available_capabilities,
            handle,
        }
    }

    pub fn client(&self) -> &synd_client::Client {
        &self.client
    }

    pub fn available_capabilities(&self) -> &CapabilitySet {
        &self.available_capabilities
    }

    pub async fn close(self) -> Result<()> {
        self.handle.close().await
    }
}

pub struct Handle {
    kind: HandleKind,
}

enum HandleKind {
    Inert,
    Daemon(Box<DaemonSessionHandle>),
}

impl Handle {
    pub fn inert() -> Self {
        Self {
            kind: HandleKind::Inert,
        }
    }

    #[cfg(not(test))]
    pub(crate) fn daemon(
        client: synd_client::Client,
        session_id: SessionId,
        lease: SessionLease,
    ) -> Self {
        Self {
            kind: HandleKind::Daemon(Box::new(DaemonSessionHandle::new(
                client, session_id, lease,
            ))),
        }
    }

    #[cfg(test)]
    pub(crate) fn daemon(
        client: synd_client::Client,
        session_id: SessionId,
        lease: SessionLease,
        renewal_observer: Option<SessionRenewalObserver>,
    ) -> Self {
        Self {
            kind: HandleKind::Daemon(Box::new(DaemonSessionHandle::new(
                client,
                session_id,
                lease,
                renewal_observer,
            ))),
        }
    }

    pub async fn close(self) -> Result<()> {
        match self.kind {
            HandleKind::Inert => Ok(()),
            HandleKind::Daemon(handle) => handle.close().await,
        }
    }
}

/// Client-side handle used to close an accepted daemon session.
struct DaemonSessionHandle {
    client: synd_client::Client,
    session_id: SessionId,
    renewal: SessionLeaseRenewalHandle,
}

impl DaemonSessionHandle {
    #[cfg(not(test))]
    fn new(client: synd_client::Client, session_id: SessionId, lease: SessionLease) -> Self {
        let renewal = {
            let schedule = SessionRenewalSchedule::from_lease(lease);
            SessionLeaseRenewal::new(client.clone(), session_id.clone(), schedule).spawn()
        };

        Self {
            client,
            session_id,
            renewal,
        }
    }

    #[cfg(test)]
    fn new(
        client: synd_client::Client,
        session_id: SessionId,
        lease: SessionLease,
        renewal_observer: Option<SessionRenewalObserver>,
    ) -> Self {
        let renewal = {
            let schedule = SessionRenewalSchedule::from_lease(lease);
            SessionLeaseRenewal::new(
                client.clone(),
                session_id.clone(),
                schedule,
                renewal_observer,
            )
            .spawn()
        };

        Self {
            client,
            session_id,
            renewal,
        }
    }

    async fn close(self) -> Result<()> {
        self.renewal.stop().await;
        self.client
            .close_session(CloseSessionRequest::new(self.session_id))
            .await?;

        Ok(())
    }
}

#[derive(Debug, Clone)]
pub struct Config {
    acquire_timeout: Duration,
    #[cfg(test)]
    renewal_observer: Option<SessionRenewalObserver>,
}

impl Config {
    pub fn new(acquire_timeout: Duration) -> Self {
        Self {
            acquire_timeout,
            #[cfg(test)]
            renewal_observer: None,
        }
    }

    pub fn acquire_timeout(&self) -> Duration {
        self.acquire_timeout
    }

    #[cfg(test)]
    pub(crate) fn with_renewal_observer(mut self, observer: SessionRenewalObserver) -> Self {
        self.renewal_observer = Some(observer);
        self
    }

    #[cfg(test)]
    pub(crate) fn renewal_observer(&self) -> Option<SessionRenewalObserver> {
        self.renewal_observer.clone()
    }
}

impl PartialEq for Config {
    fn eq(&self, other: &Self) -> bool {
        self.acquire_timeout == other.acquire_timeout
    }
}

impl Eq for Config {}

impl Default for Config {
    fn default() -> Self {
        Self::new(Duration::from_secs(5))
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Requirements {
    required_capabilities: CapabilitySet,
}

impl Requirements {
    pub fn new(required_capabilities: CapabilitySet) -> Self {
        Self {
            required_capabilities,
        }
    }

    pub fn required_capabilities(&self) -> &CapabilitySet {
        &self.required_capabilities
    }
}

impl Default for Requirements {
    fn default() -> Self {
        Self::new(capability::local_api_capabilities())
    }
}