1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::{fmt, ops};

use serde::{de, Deserialize, Deserializer, Serialize};
use zbus::zvariant::{serialized::Format, Basic, Error, ObjectPath, Result, Type, Value};

/// Unique track identifier.
///
/// If the media player implements the TrackList interface and allows
/// the same track to appear multiple times in the tracklist, this
/// must be unique within the scope of the tracklist.
///
/// Note that this should be a valid D-Bus object id, although clients
/// should not assume that any object is actually exported with any
/// interfaces at that path.
///
/// Media players may not use any paths starting with /org/mpris unless
/// explicitly allowed by this specification. Such paths are intended to
/// have special meaning, such as /org/mpris/MediaPlayer2/TrackList/NoTrack
/// to indicate "no track".
///
/// <details><summary>Rationale</summary>
///
/// This is a D-Bus object id as that is the definitive way to have unique
/// identifiers on D-Bus. It also allows for future optional expansions
/// to the specification where tracks are exported to D-Bus with an
/// interface similar to `org.gnome.UPnP.MediaItem2`.
///
/// </details>
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Type)]
#[serde(transparent)]
#[doc(alias = "Track_Id")]
pub struct TrackId(ObjectPath<'static>);

impl TrackId {
    /// A special track ID to indicate "no track".
    pub const NO_TRACK: TrackId = TrackId(ObjectPath::from_static_str_unchecked(
        "/org/mpris/MediaPlayer2/TrackList/NoTrack",
    ));

    /// Returns the track ID as an [`ObjectPath`].
    pub fn into_inner(self) -> ObjectPath<'static> {
        self.0
    }
}

impl Basic for TrackId {
    const SIGNATURE_CHAR: char = ObjectPath::SIGNATURE_CHAR;
    const SIGNATURE_STR: &'static str = ObjectPath::SIGNATURE_STR;

    fn alignment(format: Format) -> usize {
        ObjectPath::alignment(format)
    }
}

impl ops::Deref for TrackId {
    type Target = ObjectPath<'static>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl From<TrackId> for ObjectPath<'static> {
    fn from(o: TrackId) -> Self {
        o.into_inner()
    }
}

impl From<TrackId> for Value<'_> {
    fn from(o: TrackId) -> Self {
        o.into_inner().into()
    }
}

impl<'unowned, 'owned: 'unowned> From<&'owned TrackId> for ObjectPath<'unowned> {
    fn from(o: &'owned TrackId) -> Self {
        ObjectPath::from_str_unchecked(o.as_str())
    }
}

impl<'a> From<ObjectPath<'a>> for TrackId {
    fn from(o: ObjectPath<'a>) -> Self {
        TrackId(o.into_owned())
    }
}

impl TryFrom<&'_ str> for TrackId {
    type Error = Error;

    fn try_from(value: &str) -> Result<Self> {
        Ok(Self::from(ObjectPath::try_from(value)?))
    }
}

impl TryFrom<String> for TrackId {
    type Error = Error;

    fn try_from(value: String) -> Result<Self> {
        Ok(Self::from(ObjectPath::try_from(value)?))
    }
}

impl TryFrom<Value<'_>> for TrackId {
    type Error = Error;

    fn try_from(value: Value<'_>) -> Result<Self> {
        ObjectPath::try_from(value).map(|o| Self(o.to_owned()))
    }
}

impl TryFrom<&Value<'_>> for TrackId {
    type Error = Error;

    fn try_from(value: &Value<'_>) -> Result<Self> {
        ObjectPath::try_from(value).map(|o| Self(o.to_owned()))
    }
}

impl<'de> Deserialize<'de> for TrackId {
    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        String::deserialize(deserializer)
            .and_then(|s| ObjectPath::try_from(s).map_err(|e| de::Error::custom(e.to_string())))
            .map(Self)
    }
}

impl fmt::Display for TrackId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.as_str(), f)
    }
}