1
use std::{collections::HashMap, fmt, hash::Hash, time::Duration};
2

            
3
use ashpd::WindowIdentifier;
4
use serde::Serialize;
5
use zbus::zvariant::{ObjectPath, OwnedObjectPath, Type};
6

            
7
use super::{DBusSecret, DESTINATION, Prompt, Session, Unlockable};
8
use crate::{
9
    AsAttributes,
10
    dbus::{Error, ServiceError},
11
};
12

            
13
#[derive(Type, Clone)]
14
#[zvariant(signature = "o")]
15
#[doc(alias = "org.freedesktop.Secret.Item")]
16
pub struct Item<'a>(zbus::Proxy<'a>);
17

            
18
impl zbus::proxy::Defaults for Item<'_> {
19
    const INTERFACE: &'static Option<zbus::names::InterfaceName<'static>> = &Some(
20
        zbus::names::InterfaceName::from_static_str_unchecked("org.freedesktop.Secret.Item"),
21
    );
22
    const DESTINATION: &'static Option<zbus::names::BusName<'static>> = &Some(DESTINATION);
23
    const PATH: &'static Option<ObjectPath<'static>> = &None;
24
}
25

            
26
impl<'a> From<zbus::Proxy<'a>> for Item<'a> {
27
4
    fn from(value: zbus::Proxy<'a>) -> Self {
28
        Self(value)
29
    }
30
}
31

            
32
impl<'a> Item<'a> {
33
20
    pub async fn new<P>(connection: &zbus::Connection, object_path: P) -> Result<Item<'a>, Error>
34
    where
35
        P: TryInto<ObjectPath<'a>>,
36
        P::Error: Into<zbus::Error>,
37
    {
38
40
        zbus::proxy::Builder::new(connection)
39
10
            .path(object_path)?
40
10
            .uncached_properties(&["Label", "Attributes", "Created", "Modified", "Locked"])
41
            .build()
42
30
            .await
43
10
            .map_err(From::from)
44
    }
45

            
46
8
    pub(crate) async fn from_paths<P>(
47
        connection: &zbus::Connection,
48
        paths: Vec<P>,
49
    ) -> Result<Vec<Item<'a>>, Error>
50
    where
51
        P: TryInto<ObjectPath<'a>>,
52
        P::Error: Into<zbus::Error>,
53
    {
54
16
        let mut items = Vec::with_capacity(paths.capacity());
55
32
        for path in paths.into_iter() {
56
32
            items.push(Self::new(connection, path).await?);
57
        }
58
8
        Ok(items)
59
    }
60

            
61
4
    pub fn inner(&self) -> &zbus::Proxy<'_> {
62
        &self.0
63
    }
64

            
65
    #[doc(alias = "Locked")]
66
16
    pub async fn is_locked(&self) -> Result<bool, Error> {
67
12
        self.inner()
68
            .get_property("Locked")
69
16
            .await
70
4
            .map_err(From::from)
71
    }
72

            
73
16
    pub async fn label(&self) -> Result<String, Error> {
74
12
        self.inner().get_property("Label").await.map_err(From::from)
75
    }
76

            
77
16
    pub async fn set_label(&self, label: &str) -> Result<(), Error> {
78
14
        self.inner().set_property("Label", label).await?;
79
4
        Ok(())
80
    }
81

            
82
16
    pub async fn created(&self) -> Result<Duration, Error> {
83
14
        let secs = self.inner().get_property::<u64>("Created").await?;
84
4
        Ok(Duration::from_secs(secs))
85
    }
86

            
87
16
    pub async fn modified(&self) -> Result<Duration, Error> {
88
14
        let secs = self.inner().get_property::<u64>("Modified").await?;
89
4
        Ok(Duration::from_secs(secs))
90
    }
91

            
92
16
    pub async fn attributes(&self) -> Result<HashMap<String, String>, Error> {
93
12
        self.inner()
94
            .get_property("Attributes")
95
16
            .await
96
4
            .map_err(From::from)
97
    }
98

            
99
32
    pub async fn set_attributes(&self, attributes: &impl AsAttributes) -> Result<(), Error> {
100
36
        self.inner()
101
8
            .set_property("Attributes", attributes.as_attributes())
102
34
            .await?;
103
8
        Ok(())
104
    }
105

            
106
16
    pub async fn delete(&self, window_id: Option<WindowIdentifier>) -> Result<(), Error> {
107
20
        let prompt_path = self
108
            .inner()
109
4
            .call_method("Delete", &())
110
16
            .await
111
4
            .map_err::<ServiceError, _>(From::from)?
112
            .body()
113
            .deserialize::<OwnedObjectPath>()?;
114
4
        if let Some(prompt) = Prompt::new(self.inner().connection(), prompt_path).await? {
115
6
            let _ = prompt.receive_completed(window_id).await?;
116
        }
117
4
        Ok(())
118
    }
119

            
120
    #[doc(alias = "GetSecret")]
121
16
    pub async fn secret(&self, session: &Session<'_>) -> Result<DBusSecret<'_>, Error> {
122
24
        let inner = self
123
            .inner()
124
4
            .call_method("GetSecret", &(session))
125
16
            .await
126
6
            .map_err::<ServiceError, _>(From::from)?
127
            .body()
128
            .deserialize::<super::secret::DBusSecretInner>()?;
129
4
        DBusSecret::from_inner(self.inner().connection(), inner).await
130
    }
131

            
132
    #[doc(alias = "SetSecret")]
133
16
    pub async fn set_secret(&self, secret: &DBusSecret<'_>) -> Result<(), Error> {
134
20
        self.inner()
135
4
            .call_method("SetSecret", &(secret,))
136
16
            .await
137
10
            .map_err::<ServiceError, _>(From::from)?;
138
4
        Ok(())
139
    }
140
}
141

            
142
impl Serialize for Item<'_> {
143
4
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
144
    where
145
        S: serde::Serializer,
146
    {
147
8
        ObjectPath::serialize(self.inner().path(), serializer)
148
    }
149
}
150

            
151
impl PartialEq for Item<'_> {
152
2
    fn eq(&self, other: &Self) -> bool {
153
2
        self.inner().path() == other.inner().path()
154
    }
155
}
156

            
157
impl Eq for Item<'_> {}
158

            
159
impl Hash for Item<'_> {
160
2
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
161
2
        self.inner().path().hash(state);
162
    }
163
}
164

            
165
impl fmt::Debug for Item<'_> {
166
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167
        f.debug_tuple("Item")
168
            .field(&self.inner().path().as_str())
169
            .finish()
170
    }
171
}
172

            
173
impl Unlockable for Item<'_> {}