Skip to main content

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}