From 52b52ea63c9f94f62d0dc39cffe2dd2b22747c7a Mon Sep 17 00:00:00 2001
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
Date: Tue, 5 May 2020 12:17:08 +0300
Subject: [PATCH] session/endpoint/node: proxy the object-manager API for
 looking up child objects

---
 lib/wp/endpoint.c | 114 +++++++++++++++++++++-----
 lib/wp/endpoint.h |  22 ++++-
 lib/wp/node.c     |  95 +++++++++++++++++----
 lib/wp/node.h     |  20 ++++-
 lib/wp/session.c  | 204 +++++++++++++++++++++++++++++++++++++++-------
 lib/wp/session.h  |  46 +++++++++--
 6 files changed, 422 insertions(+), 79 deletions(-)

diff --git a/lib/wp/endpoint.c b/lib/wp/endpoint.c
index 1ee3aef3..3f1bb234 100644
--- a/lib/wp/endpoint.c
+++ b/lib/wp/endpoint.c
@@ -370,6 +370,8 @@ wp_endpoint_get_direction (WpEndpoint * self)
  * wp_endpoint_get_n_streams:
  * @self: the endpoint
  *
+ * Requires %WP_ENDPOINT_FEATURE_STREAMS
+ *
  * Returns: the number of streams of this endpoint
  */
 guint
@@ -384,43 +386,115 @@ wp_endpoint_get_n_streams (WpEndpoint * self)
 }
 
 /**
- * wp_endpoint_find_stream:
+ * wp_endpoint_iterate_streams:
  * @self: the endpoint
- * @bound_id: the bound id of the stream object to find
  *
- * Returns: (transfer full) (nullable): the endpoint stream that has the given
- *    @bound_id, or %NULL if there is no such stream
+ * Requires %WP_ENDPOINT_FEATURE_STREAMS
+ *
+ * Returns: (transfer full): a #WpIterator that iterates over all
+ *   the endpoint streams that belong to this endpoint
  */
-WpEndpointStream *
-wp_endpoint_find_stream (WpEndpoint * self, guint32 bound_id)
+WpIterator *
+wp_endpoint_iterate_streams (WpEndpoint * self)
 {
   g_return_val_if_fail (WP_IS_ENDPOINT (self), NULL);
   g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
           WP_ENDPOINT_FEATURE_STREAMS, NULL);
 
   WpEndpointPrivate *priv = wp_endpoint_get_instance_private (self);
-  return (WpEndpointStream *) wp_object_manager_lookup (priv->streams_om,
-      WP_TYPE_ENDPOINT_STREAM,
-      WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", bound_id,
-      NULL);
+  return wp_object_manager_iterate (priv->streams_om);
 }
 
 /**
- * wp_endpoint_iterate_streams:
+ * wp_endpoint_iterate_streams_filtered:
  * @self: the endpoint
+ * @...: a list of constraints, terminated by %NULL
+ *
+ * Requires %WP_ENDPOINT_FEATURE_STREAMS
+ *
+ * The constraints specified in the variable arguments must follow the rules
+ * documented in wp_object_interest_new().
  *
  * Returns: (transfer full): a #WpIterator that iterates over all
- *   the endpoint streams that belong to this endpoint
+ *   the streams that belong to this endpoint and match the constraints
  */
 WpIterator *
-wp_endpoint_iterate_streams (WpEndpoint * self)
+wp_endpoint_iterate_streams_filtered (WpEndpoint * self, ...)
+{
+  WpObjectInterest *interest;
+  va_list args;
+  va_start (args, self);
+  interest = wp_object_interest_new_valist (WP_TYPE_ENDPOINT_STREAM, &args);
+  va_end (args);
+  return wp_endpoint_iterate_streams_filtered_full (self, interest);
+}
+
+/**
+ * wp_endpoint_iterate_streams_filtered_full: (rename-to wp_endpoint_iterate_streams_filtered)
+ * @self: the endpoint
+ * @interest: (transfer full): the interest
+ *
+ * Requires %WP_ENDPOINT_FEATURE_STREAMS
+ *
+ * Returns: (transfer full): a #WpIterator that iterates over all
+ *   the streams that belong to this endpoint and match the @interest
+ */
+WpIterator *
+wp_endpoint_iterate_streams_filtered_full (WpEndpoint * self,
+    WpObjectInterest * interest)
 {
   g_return_val_if_fail (WP_IS_ENDPOINT (self), NULL);
   g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
           WP_ENDPOINT_FEATURE_STREAMS, NULL);
 
   WpEndpointPrivate *priv = wp_endpoint_get_instance_private (self);
-  return wp_object_manager_iterate (priv->streams_om);
+  return wp_object_manager_iterate_filtered_full (priv->streams_om, interest);
+}
+
+/**
+ * wp_endpoint_lookup_stream:
+ * @self: the endpoint
+ * @...: a list of constraints, terminated by %NULL
+ *
+ * Requires %WP_ENDPOINT_FEATURE_STREAMS
+ *
+ * The constraints specified in the variable arguments must follow the rules
+ * documented in wp_object_interest_new().
+ *
+ * Returns: (transfer full) (nullable): the first stream that matches the
+ *    constraints, or %NULL if there is no such stream
+ */
+WpEndpointStream *
+wp_endpoint_lookup_stream (WpEndpoint * self, ...)
+{
+  WpObjectInterest *interest;
+  va_list args;
+  va_start (args, self);
+  interest = wp_object_interest_new_valist (WP_TYPE_ENDPOINT_STREAM, &args);
+  va_end (args);
+  return wp_endpoint_lookup_stream_full (self, interest);
+}
+
+/**
+ * wp_endpoint_lookup_stream_full: (rename-to wp_endpoint_lookup_stream)
+ * @self: the endpoint
+ * @interest: (transfer full): the interest
+ *
+ * Requires %WP_ENDPOINT_FEATURE_STREAMS
+ *
+ * Returns: (transfer full) (nullable): the first stream that matches the
+ *    @interest, or %NULL if there is no such stream
+ */
+WpEndpointStream *
+wp_endpoint_lookup_stream_full (WpEndpoint * self, WpObjectInterest * interest)
+{
+  g_return_val_if_fail (WP_IS_ENDPOINT (self), NULL);
+  g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
+          WP_ENDPOINT_FEATURE_STREAMS, NULL);
+
+  WpEndpointPrivate *priv = wp_endpoint_get_instance_private (self);
+  return (WpEndpointStream *)
+      wp_object_manager_lookup_full (priv->streams_om, interest);
 }
 
 /**
@@ -701,20 +775,18 @@ impl_create_link (void *object, const struct spa_dict *props)
     g_autoptr (WpEndpoint) peer_ep_proxy = NULL;
     g_autoptr (WpEndpointStream) peer_stream_proxy = NULL;
 
-    peer_ep_proxy = wp_session_find_endpoint (session, peer_ep_id);
+    peer_ep_proxy = wp_session_lookup_endpoint (session,
+        WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", peer_ep_id, NULL);
     if (!peer_ep_proxy) {
       wp_warning_object (self, "endpoint %d not found in session", peer_ep_id);
       return -EINVAL;
     }
 
     if (peer_stream_id != SPA_ID_INVALID) {
-      peer_stream_proxy = wp_endpoint_find_stream (peer_ep_proxy,
-          peer_stream_id);
+      peer_stream_proxy = wp_endpoint_lookup_stream (peer_ep_proxy,
+          WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", peer_stream_id, NULL);
     } else {
-      g_autoptr (WpIterator) it = wp_endpoint_iterate_streams (peer_ep_proxy);
-      g_auto (GValue) val = G_VALUE_INIT;
-      if (wp_iterator_next (it, &val))
-        peer_stream_proxy = g_value_dup_object (&val);
+      peer_stream_proxy = wp_endpoint_lookup_stream (peer_ep_proxy, NULL);
     }
 
     if (!peer_stream_proxy) {
diff --git a/lib/wp/endpoint.h b/lib/wp/endpoint.h
index e7beebc4..a5686e80 100644
--- a/lib/wp/endpoint.h
+++ b/lib/wp/endpoint.h
@@ -14,14 +14,15 @@
 #include "port.h"
 #include "endpoint-stream.h"
 #include "iterator.h"
+#include "object-interest.h"
 
 G_BEGIN_DECLS
 
 /**
  * WpEndpointFeatures:
  * @WP_ENDPOINT_FEATURE_STREAMS: caches information about streams, enabling
- *   the use of wp_endpoint_get_n_streams(), wp_endpoint_find_stream() and
- *   wp_endpoint_iterate_streams()
+ *   the use of wp_endpoint_get_n_streams(), wp_endpoint_lookup_stream(),
+ *   wp_endpoint_iterate_streams() and related methods
  *
  * An extension of #WpProxyFeatures
  */
@@ -71,10 +72,23 @@ WP_API
 guint wp_endpoint_get_n_streams (WpEndpoint * self);
 
 WP_API
-WpEndpointStream * wp_endpoint_find_stream (WpEndpoint * self, guint32 bound_id);
+WpIterator * wp_endpoint_iterate_streams (WpEndpoint * self);
 
 WP_API
-WpIterator * wp_endpoint_iterate_streams (WpEndpoint * self);
+WpIterator * wp_endpoint_iterate_streams_filtered (WpEndpoint * self, ...)
+    G_GNUC_NULL_TERMINATED;
+
+WP_API
+WpIterator * wp_endpoint_iterate_streams_filtered_full (WpEndpoint * self,
+    WpObjectInterest * interest);
+
+WP_API
+WpEndpointStream * wp_endpoint_lookup_stream (WpEndpoint * self, ...)
+    G_GNUC_NULL_TERMINATED;
+
+WP_API
+WpEndpointStream * wp_endpoint_lookup_stream_full (WpEndpoint * self,
+    WpObjectInterest * interest);
 
 WP_API
 void wp_endpoint_create_link (WpEndpoint * self, WpProperties * props);
diff --git a/lib/wp/node.c b/lib/wp/node.c
index 02131221..27460c6a 100644
--- a/lib/wp/node.c
+++ b/lib/wp/node.c
@@ -402,47 +402,114 @@ wp_node_get_n_ports (WpNode * self)
 }
 
 /**
- * wp_node_find_port:
+ * wp_node_iterate_ports:
  * @self: the node
- * @bound_id: the bound id of the port object to find
  *
  * Requires %WP_NODE_FEATURE_PORTS
  *
- * Returns: (transfer full) (nullable): the port that has the given
- *    @bound_id, or %NULL if there is no such port
+ * Returns: (transfer full): a #WpIterator that iterates over all
+ *   the ports that belong to this node
  */
-WpPort *
-wp_node_find_port (WpNode * self, guint32 bound_id)
+WpIterator *
+wp_node_iterate_ports (WpNode * self)
 {
   g_return_val_if_fail (WP_IS_NODE (self), NULL);
   g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
           WP_NODE_FEATURE_PORTS, NULL);
 
   WpNodePrivate *priv = wp_node_get_instance_private (self);
-  return (WpPort *) wp_object_manager_lookup (priv->ports_om,
-      WP_TYPE_PORT,
-      WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", bound_id,
-      NULL);
+  return wp_object_manager_iterate (priv->ports_om);
 }
 
 /**
- * wp_node_iterate_ports:
+ * wp_node_iterate_ports_filtered:
  * @self: the node
+ * @...: a list of constraints, terminated by %NULL
  *
  * Requires %WP_NODE_FEATURE_PORTS
  *
+ * The constraints specified in the variable arguments must follow the rules
+ * documented in wp_object_interest_new().
+ *
  * Returns: (transfer full): a #WpIterator that iterates over all
- *   the ports that belong to this node
+ *   the ports that belong to this node and match the constraints
  */
 WpIterator *
-wp_node_iterate_ports (WpNode * self)
+wp_node_iterate_ports_filtered (WpNode * self, ...)
+{
+  WpObjectInterest *interest;
+  va_list args;
+  va_start (args, self);
+  interest = wp_object_interest_new_valist (WP_TYPE_PORT, &args);
+  va_end (args);
+  return wp_node_iterate_ports_filtered_full (self, interest);
+}
+
+/**
+ * wp_node_iterate_ports_filtered_full: (rename-to wp_node_iterate_ports_filtered)
+ * @self: the node
+ * @interest: (transfer full): the interest
+ *
+ * Requires %WP_NODE_FEATURE_PORTS
+ *
+ * Returns: (transfer full): a #WpIterator that iterates over all
+ *   the ports that belong to this node and match the @interest
+ */
+WpIterator *
+wp_node_iterate_ports_filtered_full (WpNode * self, WpObjectInterest * interest)
 {
   g_return_val_if_fail (WP_IS_NODE (self), NULL);
   g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
           WP_NODE_FEATURE_PORTS, NULL);
 
   WpNodePrivate *priv = wp_node_get_instance_private (self);
-  return wp_object_manager_iterate (priv->ports_om);
+  return wp_object_manager_iterate_filtered_full (priv->ports_om, interest);
+}
+
+/**
+ * wp_node_lookup_port:
+ * @self: the node
+ * @...: a list of constraints, terminated by %NULL
+ *
+ * Requires %WP_NODE_FEATURE_PORTS
+ *
+ * The constraints specified in the variable arguments must follow the rules
+ * documented in wp_object_interest_new().
+ *
+ * Returns: (transfer full) (nullable): the first port that matches the
+ *    constraints, or %NULL if there is no such port
+ */
+WpPort *
+wp_node_lookup_port (WpNode * self, ...)
+{
+  WpObjectInterest *interest;
+  va_list args;
+  va_start (args, self);
+  interest = wp_object_interest_new_valist (WP_TYPE_PORT, &args);
+  va_end (args);
+  return wp_node_lookup_port_full (self, interest);
+}
+
+/**
+ * wp_node_lookup_port_full: (rename-to wp_node_lookup_port)
+ * @self: the node
+ * @interest: (transfer full): the interest
+ *
+ * Requires %WP_NODE_FEATURE_PORTS
+ *
+ * Returns: (transfer full) (nullable): the first port that matches the
+ *    @interest, or %NULL if there is no such port
+ */
+WpPort *
+wp_node_lookup_port_full (WpNode * self, WpObjectInterest * interest)
+{
+  g_return_val_if_fail (WP_IS_NODE (self), NULL);
+  g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
+          WP_NODE_FEATURE_PORTS, NULL);
+
+  WpNodePrivate *priv = wp_node_get_instance_private (self);
+  return (WpPort *)
+      wp_object_manager_lookup_full (priv->ports_om, interest);
 }
 
 
diff --git a/lib/wp/node.h b/lib/wp/node.h
index aeec01de..feb5be4d 100644
--- a/lib/wp/node.h
+++ b/lib/wp/node.h
@@ -12,6 +12,7 @@
 #include "proxy.h"
 #include "port.h"
 #include "iterator.h"
+#include "object-interest.h"
 
 G_BEGIN_DECLS
 
@@ -36,8 +37,8 @@ typedef enum {
 /**
  * WpNodeFeatures:
  * @WP_NODE_FEATURE_PORTS: caches information about ports, enabling
- *   the use of wp_node_get_n_ports(), wp_node_find_port() and
- *   wp_node_iterate_ports()
+ *   the use of wp_node_get_n_ports(), wp_node_lookup_port(),
+ *   wp_node_iterate_ports() and related methods
  *
  * An extension of #WpProxyFeatures
  */
@@ -86,10 +87,21 @@ WP_API
 guint wp_node_get_n_ports (WpNode * self);
 
 WP_API
-WpPort * wp_node_find_port (WpNode * self, guint32 bound_id);
+WpIterator * wp_node_iterate_ports (WpNode * self);
 
 WP_API
-WpIterator * wp_node_iterate_ports (WpNode * self);
+WpIterator * wp_node_iterate_ports_filtered (WpNode * self, ...)
+    G_GNUC_NULL_TERMINATED;
+
+WP_API
+WpIterator * wp_node_iterate_ports_filtered_full (WpNode * self,
+    WpObjectInterest * interest);
+
+WP_API
+WpPort * wp_node_lookup_port (WpNode * self, ...) G_GNUC_NULL_TERMINATED;
+
+WP_API
+WpPort * wp_node_lookup_port_full (WpNode * self, WpObjectInterest * interest);
 
 /**
  * WP_TYPE_IMPL_NODE:
diff --git a/lib/wp/session.c b/lib/wp/session.c
index 36dd6359..9199e7b8 100644
--- a/lib/wp/session.c
+++ b/lib/wp/session.c
@@ -452,6 +452,8 @@ wp_session_set_default_endpoint (WpSession * self, const char * id_name,
  * wp_session_get_n_endpoints:
  * @self: the session
  *
+ * Requires %WP_SESSION_FEATURE_ENDPOINTS
+ *
  * Returns: the number of endpoints of this session
  */
 guint
@@ -466,49 +468,123 @@ wp_session_get_n_endpoints (WpSession * self)
 }
 
 /**
- * wp_session_find_endpoint:
+ * wp_session_iterate_endpoints:
  * @self: the session
- * @bound_id: the bound id of the endpoint object to find
  *
- * Returns: (transfer full) (nullable): the endpoint that has the given
- *    @bound_id, or %NULL if there is no such endpoint
+ * Requires %WP_SESSION_FEATURE_ENDPOINTS
+ *
+ * Returns: (transfer full): a #WpIterator that iterates over all
+ *   the endpoints that belong to this session
  */
-WpEndpoint *
-wp_session_find_endpoint (WpSession * self, guint32 bound_id)
+WpIterator *
+wp_session_iterate_endpoints (WpSession * self)
 {
   g_return_val_if_fail (WP_IS_SESSION (self), NULL);
   g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
           WP_SESSION_FEATURE_ENDPOINTS, NULL);
 
   WpSessionPrivate *priv = wp_session_get_instance_private (self);
-  return (WpEndpoint *) wp_object_manager_lookup (priv->endpoints_om,
-      WP_TYPE_ENDPOINT,
-      WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", bound_id,
-      NULL);
+  return wp_object_manager_iterate (priv->endpoints_om);
 }
 
 /**
- * wp_session_iterate_endpoints:
+ * wp_session_iterate_endpoints_filtered:
  * @self: the session
+ * @...: a list of constraints, terminated by %NULL
+ *
+ * Requires %WP_SESSION_FEATURE_ENDPOINTS
+ *
+ * The constraints specified in the variable arguments must follow the rules
+ * documented in wp_object_interest_new().
  *
  * Returns: (transfer full): a #WpIterator that iterates over all
- *   the endpoints that belong to this session
+ *   the endpoints that belong to this session and match the constraints
  */
 WpIterator *
-wp_session_iterate_endpoints (WpSession * self)
+wp_session_iterate_endpoints_filtered (WpSession * self, ...)
+{
+  WpObjectInterest *interest;
+  va_list args;
+  va_start (args, self);
+  interest = wp_object_interest_new_valist (WP_TYPE_ENDPOINT, &args);
+  va_end (args);
+  return wp_session_iterate_endpoints_filtered_full (self, interest);
+}
+
+/**
+ * wp_session_iterate_endpoints_filtered_full: (rename-to wp_session_iterate_endpoints_filtered)
+ * @self: the session
+ * @interest: (transfer full): the interest
+ *
+ * Requires %WP_SESSION_FEATURE_ENDPOINTS
+ *
+ * Returns: (transfer full): a #WpIterator that iterates over all
+ *   the endpoints that belong to this session and match the @interest
+ */
+WpIterator *
+wp_session_iterate_endpoints_filtered_full (WpSession * self,
+    WpObjectInterest * interest)
 {
   g_return_val_if_fail (WP_IS_SESSION (self), NULL);
   g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
           WP_SESSION_FEATURE_ENDPOINTS, NULL);
 
   WpSessionPrivate *priv = wp_session_get_instance_private (self);
-  return wp_object_manager_iterate (priv->endpoints_om);
+  return wp_object_manager_iterate_filtered_full (priv->endpoints_om, interest);
+}
+
+/**
+ * wp_session_lookup_endpoint:
+ * @self: the session
+ * @...: a list of constraints, terminated by %NULL
+ *
+ * Requires %WP_SESSION_FEATURE_ENDPOINTS
+ *
+ * The constraints specified in the variable arguments must follow the rules
+ * documented in wp_object_interest_new().
+ *
+ * Returns: (transfer full) (nullable): the first endpoint that matches the
+ *    constraints, or %NULL if there is no such endpoint
+ */
+WpEndpoint *
+wp_session_lookup_endpoint (WpSession * self, ...)
+{
+  WpObjectInterest *interest;
+  va_list args;
+  va_start (args, self);
+  interest = wp_object_interest_new_valist (WP_TYPE_ENDPOINT, &args);
+  va_end (args);
+  return wp_session_lookup_endpoint_full (self, interest);
+}
+
+/**
+ * wp_session_lookup_endpoint_full: (rename-to wp_session_lookup_endpoint)
+ * @self: the session
+ * @interest: (transfer full): the interest
+ *
+ * Requires %WP_SESSION_FEATURE_ENDPOINTS
+ *
+ * Returns: (transfer full) (nullable): the first endpoint that matches the
+ *    @interest, or %NULL if there is no such endpoint
+ */
+WpEndpoint *
+wp_session_lookup_endpoint_full (WpSession * self, WpObjectInterest * interest)
+{
+  g_return_val_if_fail (WP_IS_SESSION (self), NULL);
+  g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
+          WP_SESSION_FEATURE_ENDPOINTS, NULL);
+
+  WpSessionPrivate *priv = wp_session_get_instance_private (self);
+  return (WpEndpoint *)
+      wp_object_manager_lookup_full (priv->endpoints_om, interest);
 }
 
 /**
  * wp_session_get_n_links:
  * @self: the session
  *
+ * Requires %WP_SESSION_FEATURE_LINKS
+ *
  * Returns: the number of endpoint links of this session
  */
 guint
@@ -523,43 +599,115 @@ wp_session_get_n_links (WpSession * self)
 }
 
 /**
- * wp_session_find_link:
+ * wp_session_iterate_links:
  * @self: the session
- * @bound_id: the bound id of the link object to find
  *
- * Returns: (transfer full) (nullable): the endpoint link that has the given
- *    @bound_id, or %NULL if there is no such endpoint link
+ * Requires %WP_SESSION_FEATURE_LINKS
+ *
+ * Returns: (transfer full): a #WpIterator that iterates over all
+ *   the endpoint links that belong to this session
  */
-WpEndpointLink *
-wp_session_find_link (WpSession * self, guint32 bound_id)
+WpIterator *
+wp_session_iterate_links (WpSession * self)
 {
   g_return_val_if_fail (WP_IS_SESSION (self), NULL);
   g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
           WP_SESSION_FEATURE_LINKS, NULL);
 
   WpSessionPrivate *priv = wp_session_get_instance_private (self);
-  return (WpEndpointLink *) wp_object_manager_lookup (priv->links_om,
-      WP_TYPE_ENDPOINT_LINK,
-      WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", bound_id,
-      NULL);
+  return wp_object_manager_iterate (priv->links_om);
 }
 
 /**
- * wp_session_iterate_links:
+ * wp_session_iterate_links_filtered:
  * @self: the session
+ * @...: a list of constraints, terminated by %NULL
+ *
+ * Requires %WP_SESSION_FEATURE_LINKS
+ *
+ * The constraints specified in the variable arguments must follow the rules
+ * documented in wp_object_interest_new().
  *
  * Returns: (transfer full): a #WpIterator that iterates over all
- *   the endpoint links that belong to this session
+ *   the links that belong to this session and match the constraints
  */
 WpIterator *
-wp_session_iterate_links (WpSession * self)
+wp_session_iterate_links_filtered (WpSession * self, ...)
+{
+  WpObjectInterest *interest;
+  va_list args;
+  va_start (args, self);
+  interest = wp_object_interest_new_valist (WP_TYPE_ENDPOINT_LINK, &args);
+  va_end (args);
+  return wp_session_iterate_links_filtered_full (self, interest);
+}
+
+/**
+ * wp_session_iterate_links_filtered_full: (rename-to wp_session_iterate_links_filtered)
+ * @self: the session
+ * @interest: (transfer full): the interest
+ *
+ * Requires %WP_SESSION_FEATURE_LINKS
+ *
+ * Returns: (transfer full): a #WpIterator that iterates over all
+ *   the links that belong to this session and match the @interest
+ */
+WpIterator *
+wp_session_iterate_links_filtered_full (WpSession * self,
+    WpObjectInterest * interest)
 {
   g_return_val_if_fail (WP_IS_SESSION (self), NULL);
   g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
           WP_SESSION_FEATURE_LINKS, NULL);
 
   WpSessionPrivate *priv = wp_session_get_instance_private (self);
-  return wp_object_manager_iterate (priv->links_om);
+  return wp_object_manager_iterate_filtered_full (priv->links_om, interest);
+}
+
+/**
+ * wp_session_lookup_link:
+ * @self: the session
+ * @...: a list of constraints, terminated by %NULL
+ *
+ * Requires %WP_SESSION_FEATURE_LINKS
+ *
+ * The constraints specified in the variable arguments must follow the rules
+ * documented in wp_object_interest_new().
+ *
+ * Returns: (transfer full) (nullable): the first link that matches the
+ *    constraints, or %NULL if there is no such link
+ */
+WpEndpointLink *
+wp_session_lookup_link (WpSession * self, ...)
+{
+  WpObjectInterest *interest;
+  va_list args;
+  va_start (args, self);
+  interest = wp_object_interest_new_valist (WP_TYPE_ENDPOINT_LINK, &args);
+  va_end (args);
+  return wp_session_lookup_link_full (self, interest);
+}
+
+/**
+ * wp_session_lookup_link_full: (rename-to wp_session_lookup_link)
+ * @self: the session
+ * @interest: (transfer full): the interest
+ *
+ * Requires %WP_SESSION_FEATURE_LINKS
+ *
+ * Returns: (transfer full) (nullable): the first link that matches the
+ *    @interest, or %NULL if there is no such link
+ */
+WpEndpointLink *
+wp_session_lookup_link_full (WpSession * self, WpObjectInterest * interest)
+{
+  g_return_val_if_fail (WP_IS_SESSION (self), NULL);
+  g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
+          WP_SESSION_FEATURE_LINKS, NULL);
+
+  WpSessionPrivate *priv = wp_session_get_instance_private (self);
+  return (WpEndpointLink *)
+      wp_object_manager_lookup_full (priv->links_om, interest);
 }
 
 /* WpImplSession */
diff --git a/lib/wp/session.h b/lib/wp/session.h
index a66ea478..763aead0 100644
--- a/lib/wp/session.h
+++ b/lib/wp/session.h
@@ -18,11 +18,11 @@ G_BEGIN_DECLS
 /**
  * WpSessionFeatures:
  * @WP_SESSION_FEATURE_ENDPOINTS: caches information about endpoints, enabling
- *   the use of wp_session_get_n_endpoints(), wp_session_find_endpoint() and
- *   wp_session_iterate_endpoints()
+ *   the use of wp_session_get_n_endpoints(), wp_session_lookup_endpoint(),
+ *   wp_session_iterate_endpoints() and related methods
  * @WP_SESSION_FEATURE_LINKS: caches information about endpoint links, enabling
- *   the use of wp_session_get_n_links(), wp_session_find_link() and
- *   wp_session_iterate_links()
+ *   the use of wp_session_get_n_links(), wp_session_lookup_link(),
+ *   wp_session_iterate_links() and related methods
  *
  * An extension of #WpProxyFeatures
  */
@@ -69,24 +69,54 @@ WP_API
 void wp_session_set_default_endpoint (WpSession * self, const gchar * id_name,
     guint32 id);
 
+/* endpoints */
+
 WP_API
 guint wp_session_get_n_endpoints (WpSession * self);
 
 WP_API
-WpEndpoint * wp_session_find_endpoint (WpSession * self, guint32 bound_id);
+WpIterator * wp_session_iterate_endpoints (WpSession * self);
 
 WP_API
-WpIterator * wp_session_iterate_endpoints (WpSession * self);
+WpIterator * wp_session_iterate_endpoints_filtered (WpSession * self, ...)
+    G_GNUC_NULL_TERMINATED;
 
 WP_API
-guint wp_session_get_n_links (WpSession * self);
+WpIterator * wp_session_iterate_endpoints_filtered_full (WpSession * self,
+    WpObjectInterest * interest);
+
+WP_API
+WpEndpoint * wp_session_lookup_endpoint (WpSession * self, ...)
+    G_GNUC_NULL_TERMINATED;
+
+WP_API
+WpEndpoint * wp_session_lookup_endpoint_full (WpSession * self,
+    WpObjectInterest * interest);
+
+/* links */
 
 WP_API
-WpEndpointLink * wp_session_find_link (WpSession * self, guint32 bound_id);
+guint wp_session_get_n_links (WpSession * self);
 
 WP_API
 WpIterator * wp_session_iterate_links (WpSession * self);
 
+WP_API
+WpIterator * wp_session_iterate_links_filtered (WpSession * self, ...)
+    G_GNUC_NULL_TERMINATED;
+
+WP_API
+WpIterator * wp_session_iterate_links_filtered_full (WpSession * self,
+    WpObjectInterest * interest);
+
+WP_API
+WpEndpointLink * wp_session_lookup_link (WpSession * self, ...)
+    G_GNUC_NULL_TERMINATED;
+
+WP_API
+WpEndpointLink * wp_session_lookup_link_full (WpSession * self,
+    WpObjectInterest * interest);
+
 /**
  * WP_TYPE_IMPL_SESSION:
  *
-- 
GitLab