Skip to main content

fission_core/
platform_volume.rs

1//! Volume-control host capabilities.
2
3use crate::capability::{CapabilityType, OperationCapability};
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
7pub enum VolumeStream {
8    #[default]
9    Media,
10    Ring,
11    Alarm,
12    Notification,
13    Call,
14    System,
15}
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
18pub enum VolumeAdjustDirection {
19    Up,
20    Down,
21}
22
23#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
24pub struct VolumeLevel {
25    pub stream: VolumeStream,
26    pub level: u8,
27    pub muted: bool,
28}
29
30#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
31pub struct VolumeSetRequest {
32    pub stream: VolumeStream,
33    pub level: u8,
34    pub muted: Option<bool>,
35}
36
37#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
38pub struct VolumeAdjustRequest {
39    pub stream: VolumeStream,
40    pub direction: VolumeAdjustDirection,
41    pub step: u8,
42}
43
44impl Default for VolumeAdjustDirection {
45    fn default() -> Self {
46        Self::Up
47    }
48}
49
50#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
51pub struct VolumeError {
52    pub code: String,
53    pub message: String,
54}
55
56impl VolumeError {
57    /// Creates a portable volume error payload.
58    ///
59    /// `code` should be a stable, machine-readable reason such as
60    /// `unsupported`, `permission_denied`, or `timeout`. `message` should be a
61    /// concise human-readable explanation suitable for logs or developer-facing
62    /// diagnostics.
63    pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
64        Self {
65            code: code.into(),
66            message: message.into(),
67        }
68    }
69
70    /// Creates the standard unsupported-operation error for this capability.
71    ///
72    /// `operation` should name the attempted volume operation. Use this
73    /// from hosts that implement the capability contract but cannot provide this
74    /// operation on the current platform or hardware.
75    pub fn unsupported(operation: impl Into<String>) -> Self {
76        Self::new(
77            "unsupported",
78            format!(
79                "volume operation `{}` is not supported by this host",
80                operation.into()
81            ),
82        )
83    }
84}
85
86pub struct GetVolumeLevelCapability;
87impl OperationCapability for GetVolumeLevelCapability {
88    type Request = VolumeStream;
89    type Ok = VolumeLevel;
90    type Err = VolumeError;
91}
92
93pub struct SetVolumeLevelCapability;
94impl OperationCapability for SetVolumeLevelCapability {
95    type Request = VolumeSetRequest;
96    type Ok = VolumeLevel;
97    type Err = VolumeError;
98}
99
100pub struct AdjustVolumeLevelCapability;
101impl OperationCapability for AdjustVolumeLevelCapability {
102    type Request = VolumeAdjustRequest;
103    type Ok = VolumeLevel;
104    type Err = VolumeError;
105}
106
107pub const GET_VOLUME_LEVEL: CapabilityType<GetVolumeLevelCapability> =
108    CapabilityType::new("fission.volume.get_level");
109pub const SET_VOLUME_LEVEL: CapabilityType<SetVolumeLevelCapability> =
110    CapabilityType::new("fission.volume.set_level");
111pub const ADJUST_VOLUME_LEVEL: CapabilityType<AdjustVolumeLevelCapability> =
112    CapabilityType::new("fission.volume.adjust_level");
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn volume_set_request_round_trips() {
120        let request = VolumeSetRequest {
121            stream: VolumeStream::Media,
122            level: 42,
123            muted: Some(false),
124        };
125
126        let bytes = serde_json::to_vec(&request).unwrap();
127        let decoded: VolumeSetRequest = serde_json::from_slice(&bytes).unwrap();
128
129        assert_eq!(decoded, request);
130    }
131}