use std::future::Future;
use std::sync::Arc;
use std::time::Duration;
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct Space {
pub tenant: String,
pub user: String,
pub project: String,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Feature {
Core,
Pro(&'static str),
}
pub trait Identity: Send + Sync + 'static {
fn identify(&self, bearer: Option<&str>) -> impl Future<Output = Option<Arc<Space>>> + Send;
}
pub trait Entitlements: Send + Sync + 'static {
fn allows(&self, space: &Space, feature: Feature) -> bool;
}
pub trait Metering: Send + Sync + 'static {
fn record(&self, space: &Space, usage: Usage);
}
#[derive(Clone, Copy, Debug)]
pub struct Usage {
pub operation: &'static str,
pub feature: Feature,
pub elapsed: Duration,
pub ok: bool,
}
#[derive(Clone, Debug)]
pub struct Local {
space: Arc<Space>,
}
impl Local {
pub fn from_working_directory() -> std::io::Result<Self> {
let directory = std::env::current_dir()?.canonicalize()?;
Ok(Self::new(directory.to_string_lossy()))
}
pub fn new(project: impl Into<String>) -> Self {
Self {
space: Arc::new(Space {
tenant: "local".into(),
user: "local".into(),
project: project.into(),
}),
}
}
}
impl Identity for Local {
async fn identify(&self, _bearer: Option<&str>) -> Option<Arc<Space>> {
Some(self.space.clone())
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Open;
impl Entitlements for Open {
fn allows(&self, _space: &Space, feature: Feature) -> bool {
feature == Feature::Core
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Unmetered;
impl Metering for Unmetered {
fn record(&self, _space: &Space, _usage: Usage) {}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Change {
pub space: Arc<Space>,
pub uri: String,
}
pub trait ChangeFeed: Send + Sync + 'static {
fn publish(&self, change: Change);
fn subscribe(&self) -> tokio::sync::broadcast::Receiver<Change>;
}
#[derive(Debug)]
pub struct InProcessChanges(tokio::sync::broadcast::Sender<Change>);
const BACKLOG: usize = 1024;
impl Default for InProcessChanges {
fn default() -> Self {
Self(tokio::sync::broadcast::channel(BACKLOG).0)
}
}
impl ChangeFeed for InProcessChanges {
fn publish(&self, change: Change) {
let _ = self.0.send(change);
}
fn subscribe(&self) -> tokio::sync::broadcast::Receiver<Change> {
self.0.subscribe()
}
}