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