1use std::future::Future;
6use std::sync::Arc;
7use std::time::Duration;
8
9#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
12pub struct Space {
13 pub tenant: String,
14 pub user: String,
15 pub project: String,
16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
20pub enum Feature {
21 Core,
23 Pro(&'static str),
25}
26
27pub trait Identity: Send + Sync + 'static {
29 fn identify(&self, bearer: Option<&str>) -> impl Future<Output = Option<Arc<Space>>> + Send;
32}
33
34pub trait Entitlements: Send + Sync + 'static {
36 fn allows(&self, space: &Space, feature: Feature) -> bool;
37}
38
39pub trait Metering: Send + Sync + 'static {
41 fn record(&self, space: &Space, usage: Usage);
42}
43
44#[derive(Clone, Copy, Debug)]
46pub struct Usage {
47 pub operation: &'static str,
48 pub feature: Feature,
49 pub elapsed: Duration,
50 pub ok: bool,
52}
53
54#[derive(Clone, Debug)]
57pub struct Local {
58 space: Arc<Space>,
59}
60
61impl Local {
62 pub fn from_working_directory() -> std::io::Result<Self> {
65 let directory = std::env::current_dir()?.canonicalize()?;
66 Ok(Self::new(directory.to_string_lossy()))
67 }
68
69 pub fn new(project: impl Into<String>) -> Self {
70 Self {
71 space: Arc::new(Space {
72 tenant: "local".into(),
73 user: "local".into(),
74 project: project.into(),
75 }),
76 }
77 }
78}
79
80impl Identity for Local {
81 async fn identify(&self, _bearer: Option<&str>) -> Option<Arc<Space>> {
82 Some(self.space.clone())
83 }
84}
85
86#[derive(Clone, Copy, Debug, Default)]
88pub struct Open;
89
90impl Entitlements for Open {
91 fn allows(&self, _space: &Space, feature: Feature) -> bool {
92 feature == Feature::Core
93 }
94}
95
96#[derive(Clone, Copy, Debug, Default)]
98pub struct Unmetered;
99
100impl Metering for Unmetered {
101 fn record(&self, _space: &Space, _usage: Usage) {}
102}
103
104#[derive(Clone, Debug, PartialEq, Eq)]
106pub struct Change {
107 pub space: Arc<Space>,
108 pub uri: String,
109}
110
111pub trait ChangeFeed: Send + Sync + 'static {
115 fn publish(&self, change: Change);
117
118 fn subscribe(&self) -> tokio::sync::broadcast::Receiver<Change>;
121}
122
123#[derive(Debug)]
128pub struct InProcessChanges(tokio::sync::broadcast::Sender<Change>);
129
130const BACKLOG: usize = 1024;
132
133impl Default for InProcessChanges {
134 fn default() -> Self {
135 Self(tokio::sync::broadcast::channel(BACKLOG).0)
136 }
137}
138
139impl ChangeFeed for InProcessChanges {
140 fn publish(&self, change: Change) {
141 let _ = self.0.send(change);
143 }
144
145 fn subscribe(&self) -> tokio::sync::broadcast::Receiver<Change> {
146 self.0.subscribe()
147 }
148}