Repository: bilelmoussaoui/oo7 (language: rust)

Issue:
Connecting to SecretService signals
I would like to connect to secret service signals. With what we have the naive implementation would probably look like

```
    pub async fn receive_item_changed(
        &self,
    ) -> Result<impl futures_util::Stream<Item = Item<'_>>, Error> {
        self.inner.receive_item_changed().await.map(|stream| {
            stream.map(|item| {
                Item::new(
                    Arc::clone(&self.service),
                    Arc::clone(&self.session),
                    self.algorithm,
                    item,
                    self.aes_key.as_ref().map(Arc::clone),
                )
            })
        })
    }
```

However, oo7 only deals with `Item<'static>` which we don't get here. I think it's a zbus limitation at the moment.

To work around this, I would like to add `Collection::item_for_path` and `Item::path` functions, since together they would allow us to get a static version of the item.

Any thoughts? Otherwise, I would implement this.


Discussion / hints:
> However, oo7 only deals with Item<'static> which we don't get here. I think it's a zbus limitation at the moment.

What do you mean by oo7 only dealing with `Item<'static>`? 

> To work around this, I would like to add Collection::item_for_path

What would such function do?

> and Item::path functions

I would implement `Deref` for `zbus::Proxy` on all the `dbus::api` types.

Fix patch:
diff --git a/client/src/dbus/api/collection.rs b/client/src/dbus/api/collection.rs
index e73e9f658..7dfaa1015 100644
--- a/client/src/dbus/api/collection.rs
+++ b/client/src/dbus/api/collection.rs
@@ -51,8 +51,8 @@ impl<'a> Collection<'a> {
     }
 
     #[doc(alias = "ItemCreated")]
-    pub async fn receive_item_created(&self) -> Result<impl Stream<Item = Item<'_>>, Error> {
-        let mut stream = self.inner().receive_signal("ItemCreated").await?;
+    pub async fn receive_item_created(&self) -> Result<impl Stream<Item = Item<'a>> + '_, Error> {
+        let stream = self.inner().receive_signal("ItemCreated").await?;
         let conn = self.inner().connection();
         Ok(stream.filter_map(move |message| async move {
             let path = message.body::<OwnedObjectPath>().ok()?;
@@ -61,18 +61,14 @@ impl<'a> Collection<'a> {
     }
 
     #[doc(alias = "ItemDeleted")]
-    pub async fn receive_item_deleted(&self) -> Result<impl Stream<Item = Item<'_>>, Error> {
-        let mut stream = self.inner().receive_signal("ItemDeleted").await?;
-        let conn = self.inner().connection();
-        Ok(stream.filter_map(move |message| async move {
-            let path = message.body::<OwnedObjectPath>().ok()?;
-            Item::new(&conn.clone(), path).await.ok()
-        }))
+    pub async fn receive_item_deleted(&self) -> Result<impl Stream<Item = OwnedObjectPath>, Error> {
+        let stream = self.inner().receive_signal("ItemDeleted").await?;
+        Ok(stream.filter_map(move |message| async move { message.body::<OwnedObjectPath>().ok() }))
     }
 
     #[doc(alias = "ItemChanged")]
-    pub async fn receive_item_changed(&self) -> Result<impl Stream<Item = Item<'_>>, Error> {
-        let mut stream = self.inner().receive_signal("ItemChanged").await?;
+    pub async fn receive_item_changed(&self) -> Result<impl Stream<Item = Item<'a>> + '_, Error> {
+        let stream = self.inner().receive_signal("ItemChanged").await?;
         let conn = self.inner().connection();
         Ok(stream.filter_map(move |message| async move {
             let path = message.body::<OwnedObjectPath>().ok()?;
diff --git a/client/src/dbus/collection.rs b/client/src/dbus/collection.rs
index 0bcaa7ed5..0a67c6e16 100644
--- a/client/src/dbus/collection.rs
+++ b/client/src/dbus/collection.rs
@@ -2,8 +2,10 @@ use std::{collections::HashMap, sync::Arc, time::Duration};
 
 #[cfg(feature = "async-std")]
 use async_lock::RwLock;
+use futures_util::{Stream, StreamExt};
 #[cfg(feature = "tokio")]
 use tokio::sync::RwLock;
+use zbus::zvariant::OwnedObjectPath;
 
 use super::{api, Algorithm, Error, Item};
 use crate::Key;
@@ -63,15 +65,7 @@ impl<'a> Collection<'a> {
                 .items()
                 .await?
                 .into_iter()
-                .map(|item| {
-                    Item::new(
-                        Arc::clone(&self.service),
-                        Arc::clone(&self.session),
-                        self.algorithm,
-                        item,
-                        self.aes_key.as_ref().map(Arc::clone),
-                    )
-                })
+                .map(|item| self.new_item(item))
                 .collect::<Vec<_>>())
         }
     }
@@ -185,13 +179,7 @@ impl<'a> Collection<'a> {
                 .create_item(label, attributes, &secret, replace)
                 .await?;
 
-            Ok(Item::new(
-                Arc::clone(&self.service),
-                Arc::clone(&self.session),
-                self.algorithm,
-                item,
-                self.aes_key.as_ref().map(Arc::clone),
-            ))
+            Ok(self.new_item(item))
         }
     }
 
@@ -225,6 +213,40 @@ impl<'a> Collection<'a> {
             Ok(())
         }
     }
+
+    /// Stream yielding when new items get created
+    pub async fn receive_item_created(&self) -> Result<impl Stream<Item = Item<'a>> + '_, Error> {
+        Ok(self
+            .inner
+            .receive_item_created()
+            .await?
+            .map(|item| self.new_item(item)))
+    }
+
+    /// Stream yielding when existing items get changed
+    pub async fn receive_item_changed(&self) -> Result<impl Stream<Item = Item<'a>> + '_, Error> {
+        Ok(self
+            .inner
+            .receive_item_changed()
+            .await?
+            .map(|item| self.new_item(item)))
+    }
+
+    /// Stream yielding when existing items get deleted
+    pub async fn receive_item_deleted(&self) -> Result<impl Stream<Item = OwnedObjectPath>, Error> {
+        self.inner.receive_item_deleted().await
+    }
+
+    // Get public `Item`` from `api::Item`
+    fn new_item(&self, item: api::Item<'a>) -> Item<'a> {
+        Item::new(
+            Arc::clone(&self.service),
+            Arc::clone(&self.session),
+            self.algorithm,
+            item,
+            self.aes_key.as_ref().map(Arc::clone),
+        )
+    }
 }
 
 #[cfg(test)]
diff --git a/client/src/dbus/item.rs b/client/src/dbus/item.rs
index 7f2e4735a..318bf476f 100644
--- a/client/src/dbus/item.rs
+++ b/client/src/dbus/item.rs
@@ -4,6 +4,7 @@ use std::{collections::HashMap, sync::Arc, time::Duration};
 use async_lock::RwLock;
 #[cfg(feature = "tokio")]
 use tokio::sync::RwLock;
+use zbus::zvariant::ObjectPath;
 use zeroize::Zeroizing;
 
 use super::{api, Algorithm, Error};
@@ -195,4 +196,9 @@ impl<'a> Item<'a> {
             Ok(())
         }
     }
+
+    /// Returns item path
+    pub fn path(&self) -> &ObjectPath<'_> {
+        self.inner.inner().path()
+    }
 }


Test patch: