mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-19 06:12:24 +02:00
119 lines
4.8 KiB
Rust
119 lines
4.8 KiB
Rust
//! Views onto [`AppState`]: the subsets each subsystem actually reads.
|
|
//!
|
|
//! Every extracted subsystem takes a small context struct - the handles it
|
|
//! needs - rather than the server's root state. Those structs live in their own
|
|
//! crates and cannot name `AppState`, so something has to build them, and this
|
|
//! is it.
|
|
//!
|
|
//! # Why the trait is defined here
|
|
//!
|
|
//! The obvious shape - a `FromAppState` trait in `tuliprox-core`, implemented by
|
|
//! each context beside its own definition - does not compile. The orphan rule
|
|
//! allows `impl ForeignTrait for LocalType` or `impl LocalTrait for ForeignType`,
|
|
//! and that arrangement is neither: the trait would be core's, the context would
|
|
//! be (say) `tuliprox-hls`'s, and the impl would have to live in this binary,
|
|
//! which owns neither. It also could not be written in `tuliprox-hls`, because
|
|
//! nothing there can name `AppState`.
|
|
//!
|
|
//! So the trait is local to the binary and the contexts are the foreign types.
|
|
//! That is the one arrangement the orphan rule permits, and it puts every view
|
|
//! in one file - which is the property worth having anyway: "what does this
|
|
//! subsystem read from the server?" is answerable without opening its crate.
|
|
//!
|
|
//! # What is not here
|
|
//!
|
|
//! `WeakHlsCtx` is derived from `HlsCtx`, not from `AppState`. `SharedStreamCtx`
|
|
//! borrows rather than owning its handles, so it cannot be built by value.
|
|
//! Neither is a view in this sense.
|
|
|
|
use super::AppState;
|
|
|
|
/// A subset of [`AppState`] that one subsystem reads.
|
|
pub trait AppStateView {
|
|
/// Build the view by cloning the handles it names.
|
|
fn from_app_state(state: &AppState) -> Self;
|
|
}
|
|
|
|
impl AppState {
|
|
/// The subset that `V` reads.
|
|
///
|
|
/// Prefer the named accessors below at call sites; this is what they call,
|
|
/// and what generic code can use.
|
|
pub fn view<V: AppStateView>(&self) -> V { V::from_app_state(self) }
|
|
}
|
|
|
|
/// Declare a view: the accessor name, the context type, and the `AppState`
|
|
/// fields it clones. Field names are the same on both sides by construction -
|
|
/// a view renaming a handle would be a view lying about what it reads.
|
|
///
|
|
/// The one permitted exception is `ctx_field <- state_path`, for a context
|
|
/// that is generic over a handle `AppState` holds concretely - `events <-
|
|
/// event_manager` - or for a handle `AppState` keeps inside a container -
|
|
/// `hls_proxy <- hls.proxy`. The context names the handle by the role it
|
|
/// plays; the view still reads exactly the handle it names.
|
|
///
|
|
/// A context that holds a handle as a trait object says so with `as` -
|
|
/// `maintenance as Arc<dyn MaintenanceGate>`.
|
|
macro_rules! view_field {
|
|
($state:ident, $field:ident) => {
|
|
::core::clone::Clone::clone(&$state.$field)
|
|
};
|
|
($state:ident, $field:ident, $($source:ident).+) => {
|
|
::core::clone::Clone::clone(&$state.$($source).+)
|
|
};
|
|
}
|
|
|
|
macro_rules! app_state_views {
|
|
($(
|
|
$(#[$meta:meta])*
|
|
$accessor:ident => $ctx:ty { $($field:ident $(<- $($source:ident).+)? $(as $cast:ty)?),+ $(,)? }
|
|
)+) => {
|
|
$(
|
|
impl AppStateView for $ctx {
|
|
fn from_app_state(state: &AppState) -> Self {
|
|
Self { $($field: view_field!(state, $field $(, $($source).+)?) $(as $cast)?),+ }
|
|
}
|
|
}
|
|
|
|
impl AppState {
|
|
$(#[$meta])*
|
|
#[must_use]
|
|
pub fn $accessor(&self) -> $ctx {
|
|
self.view()
|
|
}
|
|
}
|
|
)+
|
|
};
|
|
}
|
|
|
|
app_state_views! {
|
|
/// The handles the DVR needs: the recording queue and what feeds it.
|
|
recording_ctx => crate::api::model::recording::recording_ctx::RecordingCtx<std::sync::Arc<tuliprox_session::EventManager>> {
|
|
app_config, recordings, events <- event_manager, http_clients, recording_capacity,
|
|
}
|
|
|
|
/// The handles the HLS proxy needs: itself, plus provider allocation and
|
|
/// session accounting.
|
|
hls_ctx => crate::api::model::hls_cache::HlsCtx {
|
|
app_config, hls_proxy <- hls.proxy, active_provider, connection_manager, active_users,
|
|
}
|
|
|
|
/// The handles admission reads: it decides over connections and users.
|
|
admission_ctx => tuliprox_session::admission::AdmissionCtx {
|
|
app_config, active_users, connection_manager,
|
|
}
|
|
|
|
/// The handles the provider side of a stream needs: the redirect-aware
|
|
/// HTTP clients.
|
|
provider_stream_ctx => tuliprox_session::stream_ctx::ProviderStreamCtx {
|
|
app_config, active_provider, connection_manager, http_clients,
|
|
}
|
|
|
|
/// The handles the background metadata worker reads.
|
|
metadata_update_ctx => tuliprox_metadata::ctx::BoundMetadataUpdateCtx {
|
|
app_config, active_provider, connection_manager, events <- event_manager, playlists,
|
|
update_guard <- playlist_updates.update_guard, http_clients,
|
|
maintenance as std::sync::Arc<dyn tuliprox_core::model::MaintenanceGate>,
|
|
}
|
|
}
|