sofie_http/area.rs
1use sofie_core::Result;
2use sofie_db::{Aggregate, Resolve};
3
4use crate::RequestContext;
5
6/// Its principal resolves only in its own area.
7pub trait Area: Send + Sync + 'static {
8 /// The folder under a module's `src/areas/`, such as `web`.
9 const NAME: &'static str;
10 const PREFIX: &'static str;
11 const SIGN_IN: &'static str;
12 /// Accepts `Authorization: Bearer` beside the session cookie.
13 #[cfg(feature = "tokens")]
14 const TOKENS: bool = false;
15 /// Read by `sofie gen` and the build script, which compile the area's screens for it.
16 const CLIENT: Client = Client::Html;
17 /// Fixes the area's access, so the two cannot disagree.
18 type Principal: AreaPrincipal;
19}
20
21/// What an area's pages are: HTML over the wire, or the same templates compiled into a client
22/// framework's components when the app is built.
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
24pub enum Client {
25 #[default]
26 Html,
27 Svelte,
28 /// The engine compiles it, but the build refuses it until the web toolchain carries a React
29 /// adapter.
30 React,
31}
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub enum Access {
35 /// Each endpoint states its own limits.
36 Public,
37 /// Another owned record is not found, and a write to one is refused.
38 Owned,
39 #[cfg(feature = "permissions")]
40 Permitted,
41}
42
43/// An aggregate is its own principal, owning its records; a marker such as sofie-identity's
44/// `Permitted` names the aggregate that signs in under another access.
45pub trait AreaPrincipal: Send + 'static {
46 const ACCESS: Access;
47 type Record: 'static;
48}
49
50impl<T: Aggregate> AreaPrincipal for T {
51 const ACCESS: Access = Access::Owned;
52 type Record = T;
53}
54
55pub struct Web;
56
57impl Area for Web {
58 const NAME: &'static str = "web";
59 const PREFIX: &'static str = "";
60 const SIGN_IN: &'static str = "/";
61 type Principal = Visitor;
62}
63
64/// Another area cannot resolve it:
65///
66/// ```compile_fail
67/// # use sofie_http::{Area, Routes, Visitor};
68/// struct Console;
69/// impl Area for Console {
70/// const NAME: &'static str = "console";
71/// const PREFIX: &'static str = "/console";
72/// const SIGN_IN: &'static str = "/";
73/// type Principal = Visitor;
74/// }
75/// async fn greet(_visitor: Visitor) -> sofie_core::Result<()> { Ok(()) }
76/// fn routes(routes: &mut Routes<Console>) { routes.get("/", greet); }
77/// ```
78#[derive(Debug)]
79pub struct Visitor(());
80
81/// No one signs in as one, so its `Principal` is never found.
82impl AreaPrincipal for Visitor {
83 const ACCESS: Access = Access::Public;
84 type Record = Self;
85}
86
87impl<M> Resolve<RequestContext<Web, M>> for Visitor {
88 fn resolve(_context: &mut RequestContext<Web, M>) -> impl Future<Output = Result<Self>> + Send {
89 std::future::ready(Ok(Self(())))
90 }
91}