Lines
91.23 %
Functions
93.75 %
Branches
100 %
use std::{collections::HashMap, fmt};
use ashpd::WindowIdentifier;
use futures_util::{Stream, StreamExt};
use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value};
use super::{
Collection, DBusSecret, DESTINATION, Item, PATH, Prompt, Properties, Session, Unlockable,
};
use crate::{
AsAttributes, Key,
dbus::{Algorithm, Error, ServiceError},
#[derive(Type)]
#[zvariant(signature = "o")]
#[doc(alias = "org.freedesktop.secrets")]
pub struct Service<'a>(zbus::Proxy<'a>);
impl zbus::proxy::Defaults for Service<'_> {
const INTERFACE: &'static Option<zbus::names::InterfaceName<'static>> = &Some(
zbus::names::InterfaceName::from_static_str_unchecked("org.freedesktop.Secret.Service"),
);
const DESTINATION: &'static Option<zbus::names::BusName<'static>> = &Some(DESTINATION);
const PATH: &'static Option<ObjectPath<'static>> = &Some(PATH);
}
impl<'a> From<zbus::Proxy<'a>> for Service<'a> {
fn from(value: zbus::Proxy<'a>) -> Self {
Self(value)
impl<'a> Service<'a> {
pub async fn new(connection: &zbus::Connection) -> Result<Service<'a>, Error> {
zbus::proxy::Builder::new(connection)
.build()
.await
.map_err(From::from)
pub fn inner(&self) -> &zbus::Proxy<'_> {
&self.0
#[doc(alias = "CollectionCreated")]
pub async fn receive_collection_created(
&self,
) -> Result<impl Stream<Item = Collection<'a>> + '_, Error> {
let stream = self.inner().receive_signal("CollectionCreated").await?;
let conn = self.inner().connection();
Ok(stream.filter_map(move |message| async move {
let path = message.body().deserialize::<OwnedObjectPath>().ok()?;
Collection::new(conn, path).await.ok()
}))
#[doc(alias = "CollectionDeleted")]
pub async fn receive_collection_deleted(
) -> Result<impl Stream<Item = OwnedObjectPath>, Error> {
let stream = self.inner().receive_signal("CollectionDeleted").await?;
message.body().deserialize::<OwnedObjectPath>().ok()
#[doc(alias = "CollectionChanged")]
pub async fn receive_collection_changed(
let stream = self.inner().receive_signal("CollectionChanged").await?;
pub async fn collections(&self) -> Result<Vec<Collection<'a>>, Error> {
let collections_paths = self
.inner()
.get_property::<Vec<ObjectPath>>("Collections")
.await?;
Collection::from_paths(self.inner().connection(), collections_paths).await
#[doc(alias = "OpenSession")]
pub async fn open_session(
client_public_key: Option<Key>,
) -> Result<(Option<Key>, Session<'a>), Error> {
let (algorithm, key): (_, Value<'_>) = match client_public_key {
None => (Algorithm::Plain, zvariant::Str::default().into()),
Some(key) => (Algorithm::Encrypted, key.into()),
let (service_key, session_path) = self
.call_method("OpenSession", &(&algorithm, key))
.map_err::<ServiceError, _>(From::from)?
.body()
.deserialize::<(OwnedValue, OwnedObjectPath)>()?;
let session = Session::new(self.inner().connection(), session_path).await?;
let key = match algorithm {
Algorithm::Plain => None,
Algorithm::Encrypted => Some(Key::try_from(service_key)?),
Ok((key, session))
#[doc(alias = "CreateCollection")]
pub async fn create_collection(
label: &str,
alias: Option<&str>,
window_id: Option<WindowIdentifier>,
) -> Result<Collection<'a>, Error> {
let properties = Properties::for_collection(label);
let (collection_path, prompt_path) = self
.call_method("CreateCollection", &(properties, alias.unwrap_or_default()))
.deserialize::<(OwnedObjectPath, OwnedObjectPath)>()?;
let collection_path =
if let Some(prompt) = Prompt::new(self.inner().connection(), prompt_path).await? {
let response = prompt.receive_completed(window_id).await?;
OwnedObjectPath::try_from(response)?
} else {
collection_path
Collection::new(self.inner().connection(), collection_path).await
#[doc(alias = "SearchItems")]
pub async fn search_items(
attributes: &impl AsAttributes,
) -> Result<(Vec<Item<'a>>, Vec<Item<'a>>), Error> {
let (unlocked_item_paths, locked_item_paths) = self
.call_method("SearchItems", &(attributes.as_attributes()))
.deserialize::<(Vec<OwnedObjectPath>, Vec<OwnedObjectPath>)>()?;
let cnx = self.inner().connection();
let unlocked_items = Item::from_paths(cnx, unlocked_item_paths).await?;
let locked_items = Item::from_paths(cnx, locked_item_paths).await?;
Ok((unlocked_items, locked_items))
pub async fn unlock(
items: &[impl Unlockable],
) -> Result<Vec<OwnedObjectPath>, Error> {
let (mut unlocked_item_paths, prompt_path) = self
.call_method("Unlock", &(items))
.deserialize::<(Vec<OwnedObjectPath>, OwnedObjectPath)>()?;
if let Some(prompt) = Prompt::new(cnx, prompt_path).await? {
let locked_paths = Vec::<OwnedObjectPath>::try_from(response)?;
unlocked_item_paths.extend(locked_paths);
Ok(unlocked_item_paths)
pub async fn lock(
let (mut locked_item_paths, prompt_path) = self
.call_method("Lock", &(items))
locked_item_paths.extend(locked_paths);
Ok(locked_item_paths)
#[doc(alias = "GetSecrets")]
pub async fn secrets(
items: &[Item<'_>],
session: &Session<'_>,
) -> Result<HashMap<Item<'_>, DBusSecret<'_>>, Error> {
let secrets = self
.call_method("GetSecrets", &(items, session))
.deserialize::<HashMap<OwnedObjectPath, super::secret::DBusSecretInner>>()?;
// Item's Hash implementation doesn't make use of any mutable internals
#[allow(clippy::mutable_key_type)]
let mut output = HashMap::with_capacity(secrets.capacity());
for (path, secret_inner) in secrets {
output.insert(
Item::new(cnx, path).await?,
DBusSecret::from_inner(cnx, secret_inner).await?,
Ok(output)
#[doc(alias = "ReadAlias")]
pub async fn read_alias(&self, name: &str) -> Result<Option<Collection<'a>>, Error> {
let collection_path = self
.call_method("ReadAlias", &(name))
.deserialize::<OwnedObjectPath>()?;
if collection_path != OwnedObjectPath::default() {
let collection = Collection::new(self.inner().connection(), collection_path).await?;
Ok(Some(collection))
Ok(None)
#[doc(alias = "SetAlias")]
pub async fn set_alias(&self, name: &str, collection: &Collection<'_>) -> Result<(), Error> {
self.inner()
.call_method("SetAlias", &(name, collection))
.map_err::<ServiceError, _>(From::from)?;
Ok(())
impl fmt::Debug for Service<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Service")
.field(&self.inner().path().as_str())
.finish()