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}
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub enum Access {
32    /// Each endpoint states its own limits.
33    Public,
34    /// Another owned record is not found, and a write to one is refused.
35    Owned,
36    #[cfg(feature = "permissions")]
37    Permitted,
38}
39
40/// An aggregate is its own principal, owning its records; a marker such as sofie-identity's
41/// `Permitted` names the aggregate that signs in under another access.
42pub trait AreaPrincipal: Send + 'static {
43    const ACCESS: Access;
44    type Record: 'static;
45}
46
47impl<T: Aggregate> AreaPrincipal for T {
48    const ACCESS: Access = Access::Owned;
49    type Record = T;
50}
51
52pub struct Web;
53
54impl Area for Web {
55    const NAME: &'static str = "web";
56    const PREFIX: &'static str = "";
57    const SIGN_IN: &'static str = "/";
58    type Principal = Visitor;
59}
60
61/// Another area cannot resolve it:
62///
63/// ```compile_fail
64/// # use sofie_http::{Area, Routes, Visitor};
65/// struct Console;
66/// impl Area for Console {
67///     const NAME: &'static str = "console";
68///     const PREFIX: &'static str = "/console";
69///     const SIGN_IN: &'static str = "/";
70///     type Principal = Visitor;
71/// }
72/// async fn greet(_visitor: Visitor) -> sofie_core::Result<()> { Ok(()) }
73/// fn routes(routes: &mut Routes<Console>) { routes.get("/", greet); }
74/// ```
75#[derive(Debug)]
76pub struct Visitor(());
77
78/// No one signs in as one, so its `Principal` is never found.
79impl AreaPrincipal for Visitor {
80    const ACCESS: Access = Access::Public;
81    type Record = Self;
82}
83
84impl<M> Resolve<RequestContext<Web, M>> for Visitor {
85    fn resolve(_context: &mut RequestContext<Web, M>) -> impl Future<Output = Result<Self>> + Send {
86        std::future::ready(Ok(Self(())))
87    }
88}