Skip to main content

player_plugin/
invocation.rs

1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4use crate::PluginTransport;
5
6/// Workload class used to select a plugin invocation transport.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase")]
9pub enum PluginInvocationWorkload {
10    /// Latency-sensitive media decode, processing, or normalization.
11    RealtimeMedia,
12    /// Bounded observation of runtime events and diagnostics.
13    Observer,
14    /// Bounded work outside the realtime media path.
15    Offline,
16}
17
18/// Typed rejection from the host-owned transport workload policy.
19#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
20#[error("plugin transport {transport:?} cannot serve {workload:?} workload")]
21pub struct PluginInvocationPolicyError {
22    workload: PluginInvocationWorkload,
23    transport: PluginTransport,
24}
25
26impl PluginInvocationPolicyError {
27    pub const fn workload(self) -> PluginInvocationWorkload {
28        self.workload
29    }
30
31    pub const fn transport(self) -> PluginTransport {
32        self.transport
33    }
34}
35
36/// Host-owned policy that keeps transport labels separate from workload safety.
37#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
38pub struct PluginInvocationPolicy;
39
40impl PluginInvocationPolicy {
41    pub const fn standard() -> Self {
42        Self
43    }
44
45    pub fn validate(
46        self,
47        workload: PluginInvocationWorkload,
48        transport: PluginTransport,
49    ) -> Result<(), PluginInvocationPolicyError> {
50        if transport == PluginTransport::Wasm && workload == PluginInvocationWorkload::RealtimeMedia
51        {
52            return Err(PluginInvocationPolicyError {
53                workload,
54                transport,
55            });
56        }
57        Ok(())
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn standard_policy_rejects_only_wasm_realtime_media() {
67        let policy = PluginInvocationPolicy::standard();
68        for workload in [
69            PluginInvocationWorkload::RealtimeMedia,
70            PluginInvocationWorkload::Observer,
71            PluginInvocationWorkload::Offline,
72        ] {
73            assert_eq!(policy.validate(workload, PluginTransport::Native), Ok(()));
74        }
75        assert_eq!(
76            policy.validate(
77                PluginInvocationWorkload::RealtimeMedia,
78                PluginTransport::Wasm,
79            ),
80            Err(PluginInvocationPolicyError {
81                workload: PluginInvocationWorkload::RealtimeMedia,
82                transport: PluginTransport::Wasm,
83            })
84        );
85        for workload in [
86            PluginInvocationWorkload::Observer,
87            PluginInvocationWorkload::Offline,
88        ] {
89            assert_eq!(policy.validate(workload, PluginTransport::Wasm), Ok(()));
90        }
91    }
92}