mpris_server/
track_id.rs

1use std::{fmt, ops};
2
3use serde::{de, Deserialize, Deserializer, Serialize};
4use zbus::zvariant::{serialized::Format, Basic, Error, ObjectPath, Result, Type, Value};
5
6/// Unique track identifier.
7///
8/// If the media player implements the TrackList interface and allows
9/// the same track to appear multiple times in the tracklist, this
10/// must be unique within the scope of the tracklist.
11///
12/// Note that this should be a valid D-Bus object id, although clients
13/// should not assume that any object is actually exported with any
14/// interfaces at that path.
15///
16/// Media players may not use any paths starting with /org/mpris unless
17/// explicitly allowed by this specification. Such paths are intended to
18/// have special meaning, such as /org/mpris/MediaPlayer2/TrackList/NoTrack
19/// to indicate "no track".
20///
21/// <details><summary>Rationale</summary>
22///
23/// This is a D-Bus object id as that is the definitive way to have unique
24/// identifiers on D-Bus. It also allows for future optional expansions
25/// to the specification where tracks are exported to D-Bus with an
26/// interface similar to `org.gnome.UPnP.MediaItem2`.
27///
28/// </details>
29#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Type)]
30#[serde(transparent)]
31#[doc(alias = "Track_Id")]
32pub struct TrackId(ObjectPath<'static>);
33
34impl TrackId {
35    /// A special track ID to indicate "no track".
36    pub const NO_TRACK: TrackId = TrackId(ObjectPath::from_static_str_unchecked(
37        "/org/mpris/MediaPlayer2/TrackList/NoTrack",
38    ));
39
40    /// Returns the track ID as an [`ObjectPath`].
41    pub fn into_inner(self) -> ObjectPath<'static> {
42        self.0
43    }
44}
45
46impl Basic for TrackId {
47    const SIGNATURE_CHAR: char = ObjectPath::SIGNATURE_CHAR;
48    const SIGNATURE_STR: &'static str = ObjectPath::SIGNATURE_STR;
49
50    fn alignment(format: Format) -> usize {
51        ObjectPath::alignment(format)
52    }
53}
54
55impl ops::Deref for TrackId {
56    type Target = ObjectPath<'static>;
57
58    fn deref(&self) -> &Self::Target {
59        &self.0
60    }
61}
62
63impl From<TrackId> for ObjectPath<'static> {
64    fn from(o: TrackId) -> Self {
65        o.into_inner()
66    }
67}
68
69impl From<TrackId> for Value<'_> {
70    fn from(o: TrackId) -> Self {
71        o.into_inner().into()
72    }
73}
74
75impl<'unowned, 'owned: 'unowned> From<&'owned TrackId> for ObjectPath<'unowned> {
76    fn from(o: &'owned TrackId) -> Self {
77        ObjectPath::from_str_unchecked(o.as_str())
78    }
79}
80
81impl<'a> From<ObjectPath<'a>> for TrackId {
82    fn from(o: ObjectPath<'a>) -> Self {
83        TrackId(o.into_owned())
84    }
85}
86
87impl TryFrom<&'_ str> for TrackId {
88    type Error = Error;
89
90    fn try_from(value: &str) -> Result<Self> {
91        Ok(Self::from(ObjectPath::try_from(value)?))
92    }
93}
94
95impl TryFrom<String> for TrackId {
96    type Error = Error;
97
98    fn try_from(value: String) -> Result<Self> {
99        Ok(Self::from(ObjectPath::try_from(value)?))
100    }
101}
102
103impl TryFrom<Value<'_>> for TrackId {
104    type Error = Error;
105
106    fn try_from(value: Value<'_>) -> Result<Self> {
107        ObjectPath::try_from(value).map(|o| Self(o.to_owned()))
108    }
109}
110
111impl TryFrom<&Value<'_>> for TrackId {
112    type Error = Error;
113
114    fn try_from(value: &Value<'_>) -> Result<Self> {
115        ObjectPath::try_from(value).map(|o| Self(o.to_owned()))
116    }
117}
118
119impl<'de> Deserialize<'de> for TrackId {
120    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
121    where
122        D: Deserializer<'de>,
123    {
124        String::deserialize(deserializer)
125            .and_then(|s| ObjectPath::try_from(s).map_err(|e| de::Error::custom(e.to_string())))
126            .map(Self)
127    }
128}
129
130impl fmt::Display for TrackId {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        fmt::Display::fmt(&self.as_str(), f)
133    }
134}