termusicplayback/backends/
mod.rs

1use std::{error::Error, fmt::Display};
2
3use termusiclib::config::{v2::server::Backend as ConfigBackend, SharedServerSettings};
4
5use crate::{PlayerCmdSender, PlayerTrait};
6
7#[cfg(feature = "gst")]
8mod gstreamer;
9#[cfg(feature = "mpv")]
10mod mpv;
11// public for benching lower modules
12pub(crate) mod rusty;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
15pub enum BackendSelect {
16    #[cfg(feature = "mpv")]
17    Mpv,
18    #[cfg(feature = "gst")]
19    GStreamer,
20    #[default]
21    Rusty,
22}
23
24/// Error for when [`ThemeColor`] parsing fails
25#[derive(Debug, Clone, PartialEq)]
26pub enum BackendSelectConvertError {
27    UnavailableBackend(String),
28}
29
30impl Display for BackendSelectConvertError {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        match self {
33            BackendSelectConvertError::UnavailableBackend(backend) => {
34                write!(f, "Backend {backend} is unavailable")
35            }
36        }
37    }
38}
39
40impl Error for BackendSelectConvertError {}
41
42impl TryFrom<ConfigBackend> for BackendSelect {
43    type Error = BackendSelectConvertError;
44
45    fn try_from(value: ConfigBackend) -> Result<Self, Self::Error> {
46        Ok(match value {
47            #[cfg(feature = "gst")]
48            ConfigBackend::Gstreamer => Self::GStreamer,
49            #[cfg(feature = "mpv")]
50            ConfigBackend::Mpv => Self::Mpv,
51            ConfigBackend::Rusty => Self::Rusty,
52            #[allow(unreachable_patterns)] // allow as a catch-all because of feature gates
53            _ => {
54                return Err(BackendSelectConvertError::UnavailableBackend(
55                    value.to_string(),
56                ))
57            }
58        })
59    }
60}
61
62/// Enum to choose backend at runtime
63#[non_exhaustive]
64pub enum Backend {
65    #[cfg(feature = "mpv")]
66    Mpv(mpv::MpvBackend),
67    Rusty(rusty::RustyBackend),
68    #[cfg(feature = "gst")]
69    GStreamer(gstreamer::GStreamerBackend),
70}
71
72impl Backend {
73    /// Create a new Backend based on `backend`([`BackendSelect`])
74    pub(crate) fn new_select(
75        backend: BackendSelect,
76        config: SharedServerSettings,
77        cmd_tx: PlayerCmdSender,
78    ) -> Self {
79        match backend {
80            #[cfg(feature = "mpv")]
81            BackendSelect::Mpv => Self::new_mpv(&config, cmd_tx),
82            #[cfg(feature = "gst")]
83            BackendSelect::GStreamer => Self::new_gstreamer(&config, cmd_tx),
84            BackendSelect::Rusty => Self::new_rusty(config, cmd_tx),
85        }
86    }
87
88    // /// Create a new Backend with default backend ordering
89    // ///
90    // /// For the order see [`BackendSelect::Default`]
91    // #[allow(unreachable_code)]
92    // fn new_default(config: SharedServerSettings, cmd_tx: PlayerCmdSender) -> Self {
93    //     #[cfg(feature = "gst")]
94    //     return Self::new_gstreamer(config, cmd_tx);
95    //     #[cfg(feature = "mpv")]
96    //     return Self::new_mpv(config, cmd_tx);
97    //     return Self::new_rusty(config, cmd_tx);
98    // }
99
100    /// Explicitly choose Backend [`RustyBackend`](rusty::RustyBackend)
101    fn new_rusty(config: SharedServerSettings, cmd_tx: PlayerCmdSender) -> Self {
102        info!("Using Backend \"rusty\"");
103        Self::Rusty(rusty::RustyBackend::new(config, cmd_tx))
104    }
105
106    /// Explicitly choose Backend [`GstreamerBackend`](gstreamer::GStreamerBackend)
107    #[cfg(feature = "gst")]
108    fn new_gstreamer(config: &SharedServerSettings, cmd_tx: PlayerCmdSender) -> Self {
109        info!("Using Backend \"GStreamer\"");
110        let config_read = config.read();
111        Self::GStreamer(gstreamer::GStreamerBackend::new(&config_read, cmd_tx))
112    }
113
114    /// Explicitly choose Backend [`MpvBackend`](mpv::MpvBackend)
115    #[cfg(feature = "mpv")]
116    fn new_mpv(config: &SharedServerSettings, cmd_tx: PlayerCmdSender) -> Self {
117        info!("Using Backend \"mpv\"");
118        let config_read = config.read();
119        Self::Mpv(mpv::MpvBackend::new(&config_read, cmd_tx))
120    }
121
122    #[must_use]
123    pub fn as_player(&self) -> &dyn PlayerTrait {
124        match self {
125            #[cfg(feature = "mpv")]
126            Backend::Mpv(v) => v,
127            #[cfg(feature = "gst")]
128            Backend::GStreamer(v) => v,
129            Backend::Rusty(v) => v,
130        }
131    }
132
133    #[must_use]
134    pub fn as_player_mut(&mut self) -> &mut (dyn PlayerTrait + Send) {
135        match self {
136            #[cfg(feature = "mpv")]
137            Backend::Mpv(v) => v,
138            #[cfg(feature = "gst")]
139            Backend::GStreamer(v) => v,
140            Backend::Rusty(v) => v,
141        }
142    }
143}