Skip to main content

snapcast_client/
config.rs

1//! Client configuration types mirroring the C++ `ClientSettings`.
2
3use std::path::PathBuf;
4
5use snapcast_proto::SampleFormat;
6
7/// Server connection settings.
8#[derive(Debug, Clone)]
9pub struct ServerSettings {
10    /// Connection scheme: "tcp", "ws", or "wss".
11    pub scheme: String,
12    /// Server hostname or IP.
13    pub host: String,
14    /// Server port.
15    pub port: u16,
16    /// Optional authentication.
17    pub auth: Option<Auth>,
18    /// Server CA certificate for TLS verification.
19    pub server_certificate: Option<PathBuf>,
20    /// Client certificate (PEM).
21    pub certificate: Option<PathBuf>,
22    /// Client private key (PEM).
23    pub certificate_key: Option<PathBuf>,
24    /// Password for encrypted private key.
25    pub key_password: Option<String>,
26}
27
28/// Authentication credentials.
29#[derive(Debug, Clone)]
30pub struct Auth {
31    /// Auth scheme (e.g. "Basic").
32    pub scheme: String,
33    /// Auth parameter (e.g. base64-encoded credentials).
34    pub param: String,
35}
36
37/// Mixer mode.
38#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
39pub enum MixerMode {
40    /// Software volume control (default).
41    #[default]
42    Software,
43    /// Hardware mixer (e.g. ALSA).
44    Hardware,
45    /// External script mixer.
46    Script,
47    /// No volume control.
48    None,
49}
50
51/// Mixer settings.
52#[derive(Debug, Clone, Default)]
53pub struct MixerSettings {
54    /// Mixer mode.
55    pub mode: MixerMode,
56    /// Mixer-specific parameter string.
57    pub parameter: String,
58}
59
60/// PCM device identifier.
61#[derive(Debug, Clone)]
62pub struct PcmDevice {
63    /// Device index (-1 for default).
64    pub idx: i32,
65    /// Device name.
66    pub name: String,
67    /// Human-readable description.
68    pub description: String,
69}
70
71impl Default for PcmDevice {
72    fn default() -> Self {
73        Self {
74            idx: -1,
75            name: "default".into(),
76            description: String::new(),
77        }
78    }
79}
80
81/// Audio player settings.
82#[derive(Debug, Clone, Default)]
83pub struct PlayerSettings {
84    /// Backend name (e.g. "alsa", "coreaudio").
85    pub player_name: String,
86    /// Backend-specific parameters.
87    pub parameter: String,
88    /// Additional latency in milliseconds.
89    pub latency: i32,
90    /// PCM output device.
91    pub pcm_device: PcmDevice,
92    /// Requested sample format (default: server format).
93    pub sample_format: SampleFormat,
94    /// Volume mixer settings.
95    pub mixer: MixerSettings,
96}
97
98/// Logging settings.
99#[derive(Debug, Clone)]
100pub struct LoggingSettings {
101    /// Log sink: "stdout", "stderr", "null", "file:\<path\>".
102    pub sink: String,
103    /// Log filter: "\<tag\>:\<level\>[,...]".
104    pub filter: String,
105}
106
107impl Default for LoggingSettings {
108    fn default() -> Self {
109        Self {
110            sink: "stdout".into(),
111            filter: "*:info".into(),
112        }
113    }
114}
115
116/// Daemon settings (Unix only).
117#[cfg(unix)]
118#[derive(Debug, Clone, Default)]
119pub struct DaemonSettings {
120    /// Process priority [-20..19], None if not daemonizing.
121    pub priority: Option<i32>,
122    /// `User[:group]` to run as.
123    pub user: Option<String>,
124}
125
126/// Top-level client settings.
127#[derive(Debug, Clone)]
128pub struct ClientSettings {
129    /// Instance id when running multiple clients on one host.
130    pub instance: u32,
131    /// Unique host identifier (default: MAC address).
132    pub host_id: String,
133    /// Server connection settings.
134    pub server: ServerSettings,
135    /// Audio player settings.
136    pub player: PlayerSettings,
137    /// Logging configuration.
138    pub logging: LoggingSettings,
139    /// Daemon settings (Unix only).
140    #[cfg(unix)]
141    pub daemon: Option<DaemonSettings>,
142}
143
144impl Default for ServerSettings {
145    fn default() -> Self {
146        Self {
147            scheme: "tcp".into(),
148            host: String::new(),
149            port: snapcast_proto::DEFAULT_STREAM_PORT,
150            auth: None,
151            server_certificate: None,
152            certificate: None,
153            certificate_key: None,
154            key_password: None,
155        }
156    }
157}
158
159impl Default for ClientSettings {
160    fn default() -> Self {
161        Self {
162            instance: 1,
163            host_id: String::new(),
164            server: ServerSettings::default(),
165            player: PlayerSettings::default(),
166            logging: LoggingSettings::default(),
167            #[cfg(unix)]
168            daemon: None,
169        }
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176
177    #[test]
178    fn defaults_match_cpp() {
179        let s = ClientSettings::default();
180        assert_eq!(s.instance, 1);
181        assert_eq!(s.server.scheme, "tcp");
182        assert_eq!(s.server.port, 1704);
183        assert_eq!(s.player.latency, 0);
184        assert_eq!(s.player.pcm_device.name, "default");
185        assert_eq!(s.player.mixer.mode, MixerMode::Software);
186        assert_eq!(s.logging.filter, "*:info");
187    }
188}