[gnome-software: 2/24] gs-app-query: Add immutable query object for listing apps




commit 4423aa68193a3db51e7cd81abc6999598f0d01c5
Author: Philip Withnall <pwithnall endlessos org>
Date:   Wed Apr 6 17:15:49 2022 +0100

    gs-app-query: Add immutable query object for listing apps
    
    This will be used in upcoming commits, with the `list_apps_async()`
    vfunc on `GsPlugin`, to specify which apps should be returned.
    
    It’s a separate object, rather than a series of arguments to
    `list_apps_async()`, to make the argument list more manageable and
    extensible in future.
    
    It’s immutable so that there are no concerns about thread safety when
    using `GsAppQuery` in plugins which use multiple threads. After
    construction, it’s read-only.
    
    Signed-off-by: Philip Withnall <pwithnall endlessos org>
    
    Helps: #1472

 doc/api/gnome-software-docs.xml |   1 +
 lib/gnome-software.h            |   1 +
 lib/gs-app-query.c              | 370 ++++++++++++++++++++++++++++++++++++++++
 lib/gs-app-query.h              |  35 ++++
 lib/meson.build                 |   2 +
 5 files changed, 409 insertions(+)
---
diff --git a/doc/api/gnome-software-docs.xml b/doc/api/gnome-software-docs.xml
index 7c9eb8bc9..6ae6a7212 100644
--- a/doc/api/gnome-software-docs.xml
+++ b/doc/api/gnome-software-docs.xml
@@ -706,6 +706,7 @@ gs_plugin_adopt_app (GsPlugin *plugin, GsApp *app)
     <xi:include href="xml/gs-app.xml"/>
     <xi:include href="xml/gs-app-collation.xml"/>
     <xi:include href="xml/gs-app-list.xml"/>
+    <xi:include href="xml/gs-app-query.xml"/>
     <xi:include href="xml/gs-appstream.xml"/>
     <xi:include href="xml/gs-category.xml"/>
     <xi:include href="xml/gs-category-manager.xml"/>
diff --git a/lib/gnome-software.h b/lib/gnome-software.h
index a341f6a34..8c3bd4f15 100644
--- a/lib/gnome-software.h
+++ b/lib/gnome-software.h
@@ -15,6 +15,7 @@
 #include <gs-app.h>
 #include <gs-app-list.h>
 #include <gs-app-collation.h>
+#include <gs-app-query.h>
 #include <gs-category.h>
 #include <gs-category-manager.h>
 #include <gs-desktop-data.h>
diff --git a/lib/gs-app-query.c b/lib/gs-app-query.c
new file mode 100644
index 000000000..2cfe335cb
--- /dev/null
+++ b/lib/gs-app-query.c
@@ -0,0 +1,370 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2022 Endless OS Foundation LLC
+ *
+ * Author: Philip Withnall <pwithnall endlessos org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/**
+ * SECTION:gs-app-query
+ * @short_description: Immutable representation of a query for apps
+ *
+ * #GsAppQuery is an object to represent a query for apps.
+ *
+ * It will typically be used with a #GsPluginJob which searches for matching
+ * apps, but may have multiple consumers. #GsAppQuery only represents the query
+ * and does not provide an implementation for executing that query.
+ *
+ * It is immutable after construction, and hence threadsafe. It may be extended
+ * in future by adding more query properties. The existing query properties are
+ * conjunctive: results should only be returned which match *all* properties
+ * which are set, not _any_ properties which are set.
+ *
+ * The set of apps returned for the query can be controlled with the
+ * #GsAppQuery:refine-flags,
+ * #GsAppQuery:max-results and
+ * #GsAppQuery:dedupe-flags properties. If `refine-flags` is
+ * set, all results must be refined using the given set of refine flags (see
+ * #GsPluginJobRefine). `max-results` and `dedupe-flags` are used to limit the
+ * set of results.
+ *
+ * Since: 43
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "gs-app.h"
+#include "gs-app-list.h"
+#include "gs-app-query.h"
+#include "gs-enums.h"
+#include "gs-plugin-types.h"
+#include "gs-utils.h"
+
+struct _GsAppQuery
+{
+       GObject parent;
+
+       GsPluginRefineFlags refine_flags;
+       guint max_results;
+       GsAppListFilterFlags dedupe_flags;
+
+       GsAppListSortFunc sort_func;
+       gpointer sort_user_data;
+       GDestroyNotify sort_user_data_notify;
+};
+
+G_DEFINE_TYPE (GsAppQuery, gs_app_query, G_TYPE_OBJECT)
+
+typedef enum {
+       PROP_REFINE_FLAGS = 1,
+       PROP_MAX_RESULTS,
+       PROP_DEDUPE_FLAGS,
+       PROP_SORT_FUNC,
+       PROP_SORT_USER_DATA,
+       PROP_SORT_USER_DATA_NOTIFY,
+} GsAppQueryProperty;
+
+static GParamSpec *props[PROP_SORT_USER_DATA_NOTIFY + 1] = { NULL, };
+
+static void
+gs_app_query_get_property (GObject    *object,
+                           guint       prop_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+       GsAppQuery *self = GS_APP_QUERY (object);
+
+       switch ((GsAppQueryProperty) prop_id) {
+       case PROP_REFINE_FLAGS:
+               g_value_set_flags (value, self->refine_flags);
+               break;
+       case PROP_MAX_RESULTS:
+               g_value_set_uint (value, self->max_results);
+               break;
+       case PROP_DEDUPE_FLAGS:
+               g_value_set_flags (value, self->dedupe_flags);
+               break;
+       case PROP_SORT_FUNC:
+               g_value_set_pointer (value, self->sort_func);
+               break;
+       case PROP_SORT_USER_DATA:
+               g_value_set_pointer (value, self->sort_user_data);
+               break;
+       case PROP_SORT_USER_DATA_NOTIFY:
+               g_value_set_pointer (value, self->sort_user_data_notify);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+gs_app_query_set_property (GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+       GsAppQuery *self = GS_APP_QUERY (object);
+
+       switch ((GsAppQueryProperty) prop_id) {
+       case PROP_REFINE_FLAGS:
+               /* Construct only. */
+               g_assert (self->refine_flags == 0);
+               self->refine_flags = g_value_get_flags (value);
+               break;
+       case PROP_MAX_RESULTS:
+               /* Construct only. */
+               g_assert (self->max_results == 0);
+               self->max_results = g_value_get_uint (value);
+               break;
+       case PROP_DEDUPE_FLAGS:
+               /* Construct only. */
+               g_assert (self->dedupe_flags == 0);
+               self->dedupe_flags = g_value_get_flags (value);
+               break;
+       case PROP_SORT_FUNC:
+               /* Construct only. */
+               g_assert (self->sort_func == NULL);
+               self->sort_func = g_value_get_pointer (value);
+               break;
+       case PROP_SORT_USER_DATA:
+               /* Construct only. */
+               g_assert (self->sort_user_data == NULL);
+               self->sort_user_data = g_value_get_pointer (value);
+               break;
+       case PROP_SORT_USER_DATA_NOTIFY:
+               /* Construct only. */
+               g_assert (self->sort_user_data_notify == NULL);
+               self->sort_user_data_notify = g_value_get_pointer (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+gs_app_query_dispose (GObject *object)
+{
+       GsAppQuery *self = GS_APP_QUERY (object);
+
+       if (self->sort_user_data_notify != NULL && self->sort_user_data != NULL) {
+               self->sort_user_data_notify (g_steal_pointer (&self->sort_user_data));
+               self->sort_user_data_notify = NULL;
+       }
+
+       G_OBJECT_CLASS (gs_app_query_parent_class)->dispose (object);
+}
+
+static void
+gs_app_query_class_init (GsAppQueryClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->get_property = gs_app_query_get_property;
+       object_class->set_property = gs_app_query_set_property;
+       object_class->dispose = gs_app_query_dispose;
+
+       /**
+        * GsAppQuery:refine-flags:
+        *
+        * Flags to specify how the returned apps must be refined, if at all.
+        *
+        * Since: 43
+        */
+       props[PROP_REFINE_FLAGS] =
+               g_param_spec_flags ("refine-flags", "Refine Flags",
+                                   "Flags to specify how the returned apps must be refined, if at all.",
+                                   GS_TYPE_PLUGIN_REFINE_FLAGS, GS_PLUGIN_REFINE_FLAGS_NONE,
+                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                   G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+       /**
+        * GsAppQuery:max-results:
+        *
+        * Maximum number of results to return, or 0 for no limit.
+        *
+        * Since: 43
+        */
+       props[PROP_MAX_RESULTS] =
+               g_param_spec_uint ("max-results", "Max Results",
+                                  "Maximum number of results to return, or 0 for no limit.",
+                                  0, G_MAXUINT, 0,
+                                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                  G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+       /**
+        * GsAppQuery:dedupe-flags:
+        *
+        * Flags to specify how to deduplicate the returned apps, if at all.
+        *
+        * Since: 43
+        */
+       props[PROP_DEDUPE_FLAGS] =
+               g_param_spec_flags ("dedupe-flags", "Dedupe Flags",
+                                   "Flags to specify how to deduplicate the returned apps, if at all.",
+                                   GS_TYPE_APP_LIST_FILTER_FLAGS, GS_APP_LIST_FILTER_FLAG_NONE,
+                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                   G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+       /**
+        * GsAppQuery:sort-func: (nullable)
+        *
+        * A sort function to sort the returned apps.
+        *
+        * This must be of type #GsAppListSortFunc.
+        *
+        * Since: 43
+        */
+       props[PROP_SORT_FUNC] =
+               g_param_spec_pointer ("sort-func", "Sort Function",
+                                     "A sort function to sort the returned apps.",
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                     G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+       /**
+        * GsAppQuery:sort-user-data: (nullable)
+        *
+        * User data to pass to #GsAppQuery:sort-func.
+        *
+        * Since: 43
+        */
+       props[PROP_SORT_USER_DATA] =
+               g_param_spec_pointer ("sort-user-data", "Sort User Data",
+                                     "User data to pass to #GsAppQuery:sort-func.",
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                     G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+       /**
+        * GsAppQuery:sort-user-data-notify: (nullable)
+        *
+        * A function to free #GsAppQuery:sort-user-data once it is no longer
+        * needed.
+        *
+        * This must be of type #GDestroyNotify.
+        *
+        * This will be called exactly once between being set and when the
+        * #GsAppQuery is finalized.
+        *
+        * Since: 43
+        */
+       props[PROP_SORT_USER_DATA_NOTIFY] =
+               g_param_spec_pointer ("sort-user-data-notify", "Sort User Data Notify",
+                                     "A function to free #GsAppQuery:sort-user-data once it is no longer 
needed.",
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                     G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+       g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
+}
+
+static void
+gs_app_query_init (GsAppQuery *self)
+{
+}
+
+/**
+ * gs_app_query_new:
+ * @first_property_name: name of the first #GObject property
+ * @...: value for the first property, followed by additional property/value
+ *   pairs, then a terminating %NULL
+ *
+ * Create a new #GsAppQuery containing the given query properties.
+ *
+ * Returns: (transfer full): a new #GsAppQuery
+ * Since: 43
+ */
+GsAppQuery *
+gs_app_query_new (const gchar *first_property_name,
+                 ...)
+{
+       va_list args;
+       g_autoptr(GsAppQuery) query = NULL;
+
+       va_start (args, first_property_name);
+       query = GS_APP_QUERY (g_object_new_valist (GS_TYPE_APP_QUERY, first_property_name, args));
+       va_end (args);
+
+       return g_steal_pointer (&query);
+}
+
+/**
+ * gs_app_query_get_refine_flags:
+ * @self: a #GsAppQuery
+ *
+ * Get the value of #GsAppQuery:refine-flags.
+ *
+ * Returns: the refine flags for the query
+ * Since: 43
+ */
+GsPluginRefineFlags
+gs_app_query_get_refine_flags (GsAppQuery *self)
+{
+       g_return_val_if_fail (GS_IS_APP_QUERY (self), GS_PLUGIN_REFINE_FLAGS_NONE);
+
+       return self->refine_flags;
+}
+
+/**
+ * gs_app_query_get_max_results:
+ * @self: a #GsAppQuery
+ *
+ * Get the value of #GsAppQuery:max-results.
+ *
+ * Returns: the maximum number of results to return for the query, or `0` to
+ *   indicate no limit
+ * Since: 43
+ */
+guint
+gs_app_query_get_max_results (GsAppQuery *self)
+{
+       g_return_val_if_fail (GS_IS_APP_QUERY (self), 0);
+
+       return self->max_results;
+}
+
+/**
+ * gs_app_query_get_dedupe_flags:
+ * @self: a #GsAppQuery
+ *
+ * Get the value of #GsAppQuery:dedupe-flags.
+ *
+ * Returns: the dedupe flags for the query
+ * Since: 43
+ */
+GsAppListFilterFlags
+gs_app_query_get_dedupe_flags (GsAppQuery *self)
+{
+       g_return_val_if_fail (GS_IS_APP_QUERY (self), GS_APP_LIST_FILTER_FLAG_NONE);
+
+       return self->dedupe_flags;
+}
+
+/**
+ * gs_app_query_get_sort_func:
+ * @self: a #GsAppQuery
+ * @user_data_out: (out) (transfer none) (optional) (nullable): return location
+ *   for the #GsAppQuery:sort-user-data, or %NULL to ignore
+ *
+ * Get the value of #GsAppQuery:sort-func.
+ *
+ * Returns: (nullable): the sort function for the query
+ * Since: 43
+ */
+GsAppListSortFunc
+gs_app_query_get_sort_func (GsAppQuery *self,
+                            gpointer   *user_data_out)
+{
+       g_return_val_if_fail (GS_IS_APP_QUERY (self), NULL);
+
+       if (user_data_out != NULL)
+               *user_data_out = self->sort_user_data;
+
+       return self->sort_func;
+}
diff --git a/lib/gs-app-query.h b/lib/gs-app-query.h
new file mode 100644
index 000000000..d178f4ab7
--- /dev/null
+++ b/lib/gs-app-query.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2022 Endless OS Foundation LLC
+ *
+ * Author: Philip Withnall <pwithnall endlessos org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#pragma once
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gs-app-list.h"
+#include "gs-plugin-types.h"
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_APP_QUERY (gs_app_query_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsAppQuery, gs_app_query, GS, APP_QUERY, GObject)
+
+GsAppQuery     *gs_app_query_new       (const gchar *first_property_name,
+                                        ...) G_GNUC_NULL_TERMINATED;
+
+GsPluginRefineFlags     gs_app_query_get_refine_flags  (GsAppQuery *self);
+guint                   gs_app_query_get_max_results   (GsAppQuery *self);
+GsAppListFilterFlags    gs_app_query_get_dedupe_flags  (GsAppQuery *self);
+GsAppListSortFunc       gs_app_query_get_sort_func     (GsAppQuery *self,
+                                                        gpointer   *user_data_out);
+
+G_END_DECLS
diff --git a/lib/meson.build b/lib/meson.build
index c3b752746..7ec69acb2 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -6,6 +6,7 @@ libgnomesoftware_public_headers = [
   'gs-app.h',
   'gs-app-collation.h',
   'gs-app-list.h',
+  'gs-app-query.h',
   'gs-appstream.h',
   'gs-category.h',
   'gs-category-manager.h',
@@ -83,6 +84,7 @@ libgnomesoftware = library(
   sources : [
     'gs-app.c',
     'gs-app-list.c',
+    'gs-app-query.c',
     'gs-appstream.c',
     'gs-category.c',
     'gs-category-manager.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]