[gnome-builder] libide/vcs: add IdeVcsCloneRequest
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] libide/vcs: add IdeVcsCloneRequest
- Date: Tue, 12 Jul 2022 06:39:10 +0000 (UTC)
commit cad051bfdb8766ce324e52f8423133ddf213abaa
Author: Christian Hergert <chergert redhat com>
Date: Mon Jul 11 18:04:54 2022 -0700
libide/vcs: add IdeVcsCloneRequest
This is a helper object to deal with the process of cloning a remote
repository. It implements much of the bridging features required.
src/libide/vcs/ide-vcs-branch.c | 41 +-
src/libide/vcs/ide-vcs-branch.h | 13 +-
src/libide/vcs/ide-vcs-clone-request.c | 687 +++++++++++++++++++++++++++++++++
src/libide/vcs/ide-vcs-clone-request.h | 92 +++++
src/libide/vcs/ide-vcs-tag.c | 15 +-
src/libide/vcs/ide-vcs-tag.h | 8 +-
src/libide/vcs/libide-vcs.h | 1 +
src/libide/vcs/meson.build | 3 +
8 files changed, 820 insertions(+), 40 deletions(-)
---
diff --git a/src/libide/vcs/ide-vcs-branch.c b/src/libide/vcs/ide-vcs-branch.c
index ece6e632f..0248f7718 100644
--- a/src/libide/vcs/ide-vcs-branch.c
+++ b/src/libide/vcs/ide-vcs-branch.c
@@ -1,6 +1,6 @@
/* ide-vcs-branch.c
*
- * Copyright 2019 Christian Hergert <chergert redhat com>
+ * Copyright 2019-2022 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,47 +29,46 @@ G_DEFINE_INTERFACE (IdeVcsBranch, ide_vcs_branch, G_TYPE_OBJECT)
static void
ide_vcs_branch_default_init (IdeVcsBranchInterface *iface)
{
+ g_object_interface_install_property (iface,
+ g_param_spec_string ("id", NULL, NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+ g_object_interface_install_property (iface,
+ g_param_spec_string ("name", NULL, NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+
}
/**
- * ide_vcs_branch_get_name:
+ * ide_vcs_branch_dup_name:
* @self: an #IdeVcsBranch
*
* Gets the name of the branch, which is used in various UI elements
* to display to the user.
*
* Returns: (transfer full): a string containing the branch name
- *
- * Since: 3.32
*/
-gchar *
-ide_vcs_branch_get_name (IdeVcsBranch *self)
+char *
+ide_vcs_branch_dup_name (IdeVcsBranch *self)
{
+ char *name = NULL;
g_return_val_if_fail (IDE_IS_VCS_BRANCH (self), NULL);
-
- if (IDE_VCS_BRANCH_GET_IFACE (self)->get_name)
- return IDE_VCS_BRANCH_GET_IFACE (self)->get_name (self);
-
- return NULL;
+ g_object_get (self, "name", &name, NULL);
+ return name;
}
/**
- * ide_vcs_branch_get_id:
+ * ide_vcs_branch_dup_id:
* @self: an #IdeVcsBranch
*
* Gets the identifier of the branch.
*
* Returns: (transfer full): a string containing the branch identifier
- *
- * Since: 3.34
*/
-gchar *
-ide_vcs_branch_get_id (IdeVcsBranch *self)
+char *
+ide_vcs_branch_dup_id (IdeVcsBranch *self)
{
+ char *id = NULL;
g_return_val_if_fail (IDE_IS_VCS_BRANCH (self), NULL);
-
- if (IDE_VCS_BRANCH_GET_IFACE (self)->get_id)
- return IDE_VCS_BRANCH_GET_IFACE (self)->get_id (self);
-
- return NULL;
+ g_object_get (self, "id", &id, NULL);
+ return id;
}
diff --git a/src/libide/vcs/ide-vcs-branch.h b/src/libide/vcs/ide-vcs-branch.h
index dd14f8527..c83b13b72 100644
--- a/src/libide/vcs/ide-vcs-branch.h
+++ b/src/libide/vcs/ide-vcs-branch.h
@@ -26,20 +26,17 @@ G_BEGIN_DECLS
#define IDE_TYPE_VCS_BRANCH (ide_vcs_branch_get_type ())
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (IdeVcsBranch, ide_vcs_branch, IDE, VCS_BRANCH, GObject)
struct _IdeVcsBranchInterface
{
GTypeInterface parent;
-
- gchar *(*get_name) (IdeVcsBranch *self);
- gchar *(*get_id) (IdeVcsBranch *self);
};
-IDE_AVAILABLE_IN_3_32
-gchar *ide_vcs_branch_get_name (IdeVcsBranch *self);
-IDE_AVAILABLE_IN_3_34
-gchar *ide_vcs_branch_get_id (IdeVcsBranch *self);
+IDE_AVAILABLE_IN_ALL
+char *ide_vcs_branch_dup_name (IdeVcsBranch *self);
+IDE_AVAILABLE_IN_ALL
+char *ide_vcs_branch_dup_id (IdeVcsBranch *self);
G_END_DECLS
diff --git a/src/libide/vcs/ide-vcs-clone-request.c b/src/libide/vcs/ide-vcs-clone-request.c
new file mode 100644
index 000000000..c134b1a4c
--- /dev/null
+++ b/src/libide/vcs/ide-vcs-clone-request.c
@@ -0,0 +1,687 @@
+/* ide-vcs-clone-request.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-vcs-clone-request"
+
+#include "config.h"
+
+#include <libide-plugins.h>
+#include <libide-threading.h>
+
+#include "ide-vcs-cloner.h"
+#include "ide-vcs-clone-request.h"
+#include "ide-vcs-uri.h"
+
+struct _IdeVcsCloneRequest
+{
+ IdeObject parent_instance;
+
+ GListModel *branch_model;
+ GCancellable *cancellable;
+ GFile *directory;
+
+ IdeVcsCloner *cloner;
+
+ char *author_email;
+ char *author_name;
+ char *branch_name;
+ char *module_name;
+ char *uri;
+
+ guint fetching_branches;
+};
+
+enum {
+ PROP_0,
+ PROP_AUTHOR_EMAIL,
+ PROP_AUTHOR_NAME,
+ PROP_CAN_SELECT_BRANCH,
+ PROP_BRANCH_MODEL,
+ PROP_BRANCH_MODEL_BUSY,
+ PROP_BRANCH_NAME,
+ PROP_DIRECTORY,
+ PROP_MODULE_NAME,
+ PROP_URI,
+ N_PROPS
+};
+
+G_DEFINE_FINAL_TYPE (IdeVcsCloneRequest, ide_vcs_clone_request, IDE_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
+
+static gboolean
+ide_vcs_clone_request_get_can_select_branch (IdeVcsCloneRequest *self)
+{
+ g_assert (IDE_IS_VCS_CLONE_REQUEST (self));
+
+ return !ide_str_empty0 (self->uri) &&
+ self->cloner != NULL &&
+ IDE_VCS_CLONER_GET_IFACE (self->cloner)->list_branches_async != NULL;
+}
+
+static void
+ide_vcs_clone_request_set_cloner (IdeVcsCloneRequest *self,
+ IdeVcsCloner *cloner)
+{
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_VCS_CLONE_REQUEST (self));
+ g_assert (!cloner || IDE_IS_VCS_CLONER (cloner));
+
+ g_debug ("Setting cloner to %s",
+ cloner ? G_OBJECT_TYPE_NAME (cloner) : "NULL");
+
+ if (g_set_object (&self->cloner, cloner))
+ {
+ g_clear_object (&self->branch_model);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BRANCH_MODEL]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_SELECT_BRANCH]);
+ }
+
+ IDE_EXIT;
+}
+
+static void
+ide_vcs_clone_request_dispose (GObject *object)
+{
+ IdeVcsCloneRequest *self = (IdeVcsCloneRequest *)object;
+
+ g_clear_object (&self->branch_model);
+ g_clear_object (&self->cancellable);
+ g_clear_object (&self->directory);
+
+ ide_clear_and_destroy_object (&self->cloner);
+
+ ide_clear_string (&self->author_email);
+ ide_clear_string (&self->author_name);
+ ide_clear_string (&self->branch_name);
+ ide_clear_string (&self->module_name);
+ ide_clear_string (&self->uri);
+
+ G_OBJECT_CLASS (ide_vcs_clone_request_parent_class)->dispose (object);
+}
+
+static void
+ide_vcs_clone_request_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeVcsCloneRequest *self = IDE_VCS_CLONE_REQUEST (object);
+
+ switch (prop_id)
+ {
+ case PROP_AUTHOR_EMAIL:
+ g_value_set_string (value, ide_vcs_clone_request_get_author_email (self));
+ break;
+
+ case PROP_AUTHOR_NAME:
+ g_value_set_string (value, ide_vcs_clone_request_get_author_name (self));
+ break;
+
+ case PROP_BRANCH_MODEL:
+ g_value_set_object (value, ide_vcs_clone_request_get_branch_model (self));
+ break;
+
+ case PROP_BRANCH_MODEL_BUSY:
+ g_value_set_boolean (value, self->fetching_branches);
+ break;
+
+ case PROP_BRANCH_NAME:
+ g_value_set_string (value, ide_vcs_clone_request_get_branch_name (self));
+ break;
+
+ case PROP_CAN_SELECT_BRANCH:
+ g_value_set_boolean (value, ide_vcs_clone_request_get_can_select_branch (self));
+ break;
+
+ case PROP_DIRECTORY:
+ g_value_set_object (value, ide_vcs_clone_request_get_directory (self));
+ break;
+
+ case PROP_MODULE_NAME:
+ g_value_set_string (value, ide_vcs_clone_request_get_module_name (self));
+ break;
+
+ case PROP_URI:
+ g_value_set_string (value, ide_vcs_clone_request_get_uri (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_vcs_clone_request_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeVcsCloneRequest *self = IDE_VCS_CLONE_REQUEST (object);
+
+ switch (prop_id)
+ {
+ case PROP_AUTHOR_EMAIL:
+ ide_vcs_clone_request_set_author_email (self, g_value_get_string (value));
+ break;
+
+ case PROP_AUTHOR_NAME:
+ ide_vcs_clone_request_set_author_name (self, g_value_get_string (value));
+ break;
+
+ case PROP_BRANCH_NAME:
+ ide_vcs_clone_request_set_branch_name (self, g_value_get_string (value));
+ break;
+
+ case PROP_DIRECTORY:
+ ide_vcs_clone_request_set_directory (self, g_value_get_object (value));
+ break;
+
+ case PROP_MODULE_NAME:
+ ide_vcs_clone_request_set_module_name (self, g_value_get_string (value));
+ break;
+
+ case PROP_URI:
+ ide_vcs_clone_request_set_uri (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_vcs_clone_request_class_init (IdeVcsCloneRequestClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = ide_vcs_clone_request_dispose;
+ object_class->get_property = ide_vcs_clone_request_get_property;
+ object_class->set_property = ide_vcs_clone_request_set_property;
+
+ properties [PROP_AUTHOR_EMAIL] =
+ g_param_spec_string ("author-email", NULL, NULL, NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_AUTHOR_NAME] =
+ g_param_spec_string ("author-name", NULL, NULL, NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * IdeVcsCloneRequest:branch-model:
+ *
+ * The "branch-model" contains a #GListModel of #IdeVcsBranch that
+ * represents the names of branches that may be available on the peer.
+ *
+ * This model is not automatically updated until
+ * ide_vcs_clone_request_populate_branches() is called. This is to make it
+ * clear to the user that it is being done in response to an action (such
+ * as showing a popover) since user/password information may be requested
+ * from the user.
+ *
+ * The UI may use this to show a popover/selection of branches for the
+ * user to select.
+ */
+ properties [PROP_BRANCH_MODEL] =
+ g_param_spec_object ("branch-model", NULL, NULL, G_TYPE_LIST_MODEL,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_BRANCH_MODEL_BUSY] =
+ g_param_spec_boolean ("branch-model-busy", NULL, NULL, FALSE,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_BRANCH_NAME] =
+ g_param_spec_string ("branch-name", NULL, NULL, NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_CAN_SELECT_BRANCH] =
+ g_param_spec_boolean ("can-select-branch", NULL, NULL, FALSE,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_DIRECTORY] =
+ g_param_spec_object ("directory", NULL, NULL, G_TYPE_FILE,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_MODULE_NAME] =
+ g_param_spec_string ("module-name", NULL, NULL, NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_URI] =
+ g_param_spec_string ("uri", NULL, NULL, NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_vcs_clone_request_init (IdeVcsCloneRequest *self)
+{
+ /* Set to default projects directory */
+ ide_vcs_clone_request_set_directory (self, NULL);
+}
+
+const char *
+ide_vcs_clone_request_get_author_email (IdeVcsCloneRequest *self)
+{
+ g_return_val_if_fail (IDE_IS_VCS_CLONE_REQUEST (self), NULL);
+
+ return self->author_email ? self->author_email : "";
+}
+
+const char *
+ide_vcs_clone_request_get_author_name (IdeVcsCloneRequest *self)
+{
+ const char *ret;
+
+ g_return_val_if_fail (IDE_IS_VCS_CLONE_REQUEST (self), NULL);
+
+ ret = self->author_name;
+
+ if (ide_str_empty0 (ret))
+ ret = g_get_real_name ();
+
+ if (ide_str_empty0 (ret))
+ return "";
+
+ return ret;
+}
+
+const char *
+ide_vcs_clone_request_get_branch_name (IdeVcsCloneRequest *self)
+{
+ g_return_val_if_fail (IDE_IS_VCS_CLONE_REQUEST (self), NULL);
+
+ return self->branch_name ? self->branch_name : "";
+}
+
+const char *
+ide_vcs_clone_request_get_module_name (IdeVcsCloneRequest *self)
+{
+ g_return_val_if_fail (IDE_IS_VCS_CLONE_REQUEST (self), NULL);
+
+ return self->module_name;
+}
+
+/**
+ * ide_vcs_clone_request_get_directory:
+ * @self: a #IdeVcsCloneRequest
+ *
+ * Gets the directory to use which will contain the new subdirectory
+ * created when checking out the project.
+ *
+ * Returns: (transfer none) (not nullable): a #GFile
+ */
+GFile *
+ide_vcs_clone_request_get_directory (IdeVcsCloneRequest *self)
+{
+ g_return_val_if_fail (IDE_IS_VCS_CLONE_REQUEST (self), NULL);
+ g_return_val_if_fail (G_IS_FILE (self->directory), NULL);
+
+ return self->directory;
+}
+
+const char *
+ide_vcs_clone_request_get_uri (IdeVcsCloneRequest *self)
+{
+ g_return_val_if_fail (IDE_IS_VCS_CLONE_REQUEST (self), NULL);
+
+ return self->uri ? self->uri : "";
+}
+
+void
+ide_vcs_clone_request_set_author_email (IdeVcsCloneRequest *self,
+ const char *author_email)
+{
+ g_return_if_fail (IDE_IS_VCS_CLONE_REQUEST (self));
+
+ if (ide_set_string (&self->author_email, author_email))
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_AUTHOR_EMAIL]);
+}
+
+void
+ide_vcs_clone_request_set_author_name (IdeVcsCloneRequest *self,
+ const char *author_name)
+{
+ g_return_if_fail (IDE_IS_VCS_CLONE_REQUEST (self));
+
+ if (ide_set_string (&self->author_name, author_name))
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_AUTHOR_NAME]);
+}
+
+void
+ide_vcs_clone_request_set_branch_name (IdeVcsCloneRequest *self,
+ const char *branch_name)
+{
+ g_return_if_fail (IDE_IS_VCS_CLONE_REQUEST (self));
+
+ if (ide_set_string (&self->branch_name, branch_name))
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BRANCH_NAME]);
+}
+
+void
+ide_vcs_clone_request_set_module_name (IdeVcsCloneRequest *self,
+ const char *module_name)
+{
+ g_return_if_fail (IDE_IS_VCS_CLONE_REQUEST (self));
+
+ if (ide_set_string (&self->module_name, module_name))
+ {
+ g_autoptr(PeasExtension) exten = NULL;
+
+ if (module_name != NULL)
+ {
+ PeasEngine *engine = peas_engine_get_default ();
+ PeasPluginInfo *plugin_info = peas_engine_get_plugin_info (engine, module_name);
+
+ if (plugin_info != NULL &&
+ peas_engine_provides_extension (engine, plugin_info, IDE_TYPE_VCS_CLONER))
+ exten = peas_engine_create_extension (engine,
+ plugin_info,
+ IDE_TYPE_VCS_CLONER,
+ "parent", IDE_OBJECT (self),
+ NULL);
+ }
+
+ ide_vcs_clone_request_set_cloner (self, IDE_VCS_CLONER (exten));
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MODULE_NAME]);
+ }
+}
+
+void
+ide_vcs_clone_request_set_directory (IdeVcsCloneRequest *self,
+ GFile *directory)
+{
+ g_autoptr(GFile) default_directory = NULL;
+
+ g_return_if_fail (IDE_IS_VCS_CLONE_REQUEST (self));
+ g_return_if_fail (!directory || G_IS_FILE (directory));
+
+ if (directory == NULL)
+ {
+ default_directory = g_file_new_for_path (ide_get_projects_dir ());
+ directory = default_directory;
+ }
+
+ g_assert (G_IS_FILE (directory));
+
+ if (g_set_object (&self->directory, directory))
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DIRECTORY]);
+}
+
+void
+ide_vcs_clone_request_set_uri (IdeVcsCloneRequest *self,
+ const char *uri)
+{
+ g_return_if_fail (IDE_IS_VCS_CLONE_REQUEST (self));
+
+ if (ide_set_string (&self->uri, uri))
+ {
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_SELECT_BRANCH]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_URI]);
+ }
+}
+
+/**
+ * ide_vcs_clone_request_get_branch_model:
+ * @self: a #IdeVcsCloneRequest
+ *
+ * Gets the #GListModel of #IdeVcsBranch once available.
+ *
+ * Returns: (transfer none): a #GListModel of #IdeVcsBranch, or %NULL
+ * if the model has not yet been created.
+ */
+GListModel *
+ide_vcs_clone_request_get_branch_model (IdeVcsCloneRequest *self)
+{
+ g_return_val_if_fail (IDE_IS_VCS_CLONE_REQUEST (self), NULL);
+
+ return self->branch_model;
+}
+
+static void
+ide_vcs_clone_request_populate_branches_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeVcsCloner *cloner = (IdeVcsCloner *)object;
+ g_autoptr(IdeVcsCloneRequest) self = user_data;
+ g_autoptr(GListModel) branches = NULL;
+ g_autoptr(GError) error = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_VCS_CLONER (cloner));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_VCS_CLONE_REQUEST (self));
+
+ if (!(branches = ide_vcs_cloner_list_branches_finish (cloner, result, &error)))
+ g_warning ("Failed to list branches: %s", error->message);
+
+ if (g_set_object (&self->branch_model, branches))
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BRANCH_MODEL]);
+
+ self->fetching_branches--;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BRANCH_MODEL_BUSY]);
+
+ IDE_EXIT;
+}
+
+void
+ide_vcs_clone_request_populate_branches (IdeVcsCloneRequest *self)
+{
+ g_autoptr(IdeVcsUri) uri = NULL;
+ const char *uri_str;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_VCS_CLONE_REQUEST (self));
+ g_return_if_fail (IDE_IS_VCS_CLONER (self->cloner));
+
+ if (!ide_vcs_clone_request_get_can_select_branch (self))
+ {
+ g_message ("IdeVcsCloner does not support listing branches");
+ IDE_EXIT;
+ }
+
+ if (!(uri_str = ide_vcs_clone_request_get_uri (self)) ||
+ !ide_vcs_uri_is_valid (uri_str) ||
+ !(uri = ide_vcs_uri_new (uri_str)))
+ {
+ g_debug ("Unvalid VCS uri, cannot populate branches");
+ IDE_EXIT;
+ }
+
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+ self->cancellable = g_cancellable_new ();
+
+ self->fetching_branches++;
+
+ ide_vcs_cloner_list_branches_async (self->cloner,
+ uri,
+ self->cancellable,
+ ide_vcs_clone_request_populate_branches_cb,
+ g_object_ref (self));
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BRANCH_MODEL_BUSY]);
+
+ IDE_EXIT;
+}
+
+IdeVcsCloneRequestValidation
+ide_vcs_clone_request_validate (IdeVcsCloneRequest *self)
+{
+ IdeVcsCloneRequestValidation flags = 0;
+ g_autoptr(IdeVcsUri) uri = NULL;
+
+ g_return_val_if_fail (IDE_IS_VCS_CLONE_REQUEST (self), 0);
+ g_return_val_if_fail (IDE_IS_VCS_CLONER (self->cloner), 0);
+
+ if (ide_str_empty0 (self->uri) || !ide_vcs_uri_is_valid (self->uri))
+ flags |= IDE_VCS_CLONE_REQUEST_INVAL_URI;
+ else
+ uri = ide_vcs_uri_new (self->uri);
+
+ if (uri != NULL)
+ {
+ const char *path;
+
+ if ((path = ide_vcs_uri_get_path (uri)) && !ide_str_empty0 (path))
+ {
+ g_autofree char *name = ide_vcs_cloner_get_directory_name (self->cloner, uri);
+
+ if (!ide_str_empty0 (name))
+ {
+ g_autoptr(GFile) new_directory = g_file_get_child (self->directory, name);
+
+ if (g_file_query_exists (new_directory, NULL))
+ flags |= IDE_VCS_CLONE_REQUEST_INVAL_DIRECTORY;
+ }
+ }
+ }
+
+ /* I mean, who really wants to validate email anyway */
+ if (!ide_str_empty0 (self->author_email) &&
+ strchr (self->author_email, '@') == NULL)
+ flags |= IDE_VCS_CLONE_REQUEST_INVAL_EMAIL;
+
+ return flags;
+}
+
+static void
+ide_vcs_clone_request_clone_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeVcsCloner *cloner = (IdeVcsCloner *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ GFile *file;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_VCS_CLONER (cloner));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ file = ide_task_get_task_data (task);
+ g_assert (G_IS_FILE (file));
+
+ if (!ide_vcs_cloner_clone_finish (cloner, result, &error))
+ ide_task_return_error (task, g_steal_pointer (&error));
+ else
+ ide_task_return_pointer (task, g_object_ref (file), g_object_unref);
+
+ IDE_EXIT;
+}
+
+void
+ide_vcs_clone_request_clone_async (IdeVcsCloneRequest *self,
+ IdeNotification *notif,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(IdeVcsUri) uri = NULL;
+ g_autoptr(GFile) clone_dir = NULL;
+ g_autofree char *name = NULL;
+ GVariantDict params;
+ const char *uri_str;
+ const char *author_name;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_VCS_CLONE_REQUEST (self));
+ g_return_if_fail (IDE_IS_NOTIFICATION (notif));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_vcs_clone_request_clone_async);
+
+ if (ide_vcs_clone_request_validate (self) != 0)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ "Cannot clone, invalid arguments for request");
+ IDE_EXIT;
+ }
+
+ author_name = ide_vcs_clone_request_get_author_name (self);
+
+ uri_str = ide_vcs_clone_request_get_uri (self);
+ uri = ide_vcs_uri_new (uri_str);
+ name = ide_vcs_cloner_get_directory_name (self->cloner, uri);
+ clone_dir = g_file_get_child (self->directory, name);
+ ide_task_set_task_data (task, g_object_ref (clone_dir), g_object_unref);
+
+ g_variant_dict_init (¶ms, NULL);
+ if (!ide_str_empty0 (author_name) &&
+ !ide_str_equal0 (author_name, g_get_real_name ()))
+ g_variant_dict_insert (¶ms, "user.name", "s", author_name);
+ if (!ide_str_empty0 (self->author_email))
+ g_variant_dict_insert (¶ms, "user.email", "s", self->author_email);
+ if (!ide_str_empty0 (self->branch_name))
+ g_variant_dict_insert (¶ms, "branch", "s", self->branch_name);
+
+ ide_vcs_cloner_clone_async (self->cloner,
+ uri_str,
+ g_file_peek_path (clone_dir),
+ g_variant_dict_end (¶ms),
+ notif,
+ cancellable,
+ ide_vcs_clone_request_clone_cb,
+ g_steal_pointer (&task));
+
+ IDE_EXIT;
+}
+
+/**
+ * ide_vcs_clone_request_clone_finish:
+ * @self: a #IdeVcsCloneRequest
+ * @result: a #GAsyncResult
+ * @error: a location for a #GError
+ *
+ * Complete a clone request.
+ *
+ * The result of the request is the directory that the clone was
+ * completed within. This is the subdirectory within
+ * #IdeVcsCloneRequest:directory.
+ *
+ * Returns: (transfer full): a #GFile or %NULL and @error is set.
+ */
+GFile *
+ide_vcs_clone_request_clone_finish (IdeVcsCloneRequest *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ GFile *ret;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_VCS_CLONE_REQUEST (self), NULL);
+ g_return_val_if_fail (IDE_IS_TASK (result), NULL);
+
+ ret = ide_task_propagate_pointer (IDE_TASK (result), error);
+
+ IDE_RETURN (ret);
+}
diff --git a/src/libide/vcs/ide-vcs-clone-request.h b/src/libide/vcs/ide-vcs-clone-request.h
new file mode 100644
index 000000000..68b2d95a9
--- /dev/null
+++ b/src/libide/vcs/ide-vcs-clone-request.h
@@ -0,0 +1,92 @@
+/* ide-vcs-clone-request.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_VCS_INSIDE) && !defined (IDE_VCS_COMPILATION)
+# error "Only <libide-vcs.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_VCS_CLONE_REQUEST (ide_vcs_clone_request_get_type())
+
+typedef enum
+{
+ IDE_VCS_CLONE_REQUEST_VALID = 0,
+ IDE_VCS_CLONE_REQUEST_INVAL_URI = 1 << 0,
+ IDE_VCS_CLONE_REQUEST_INVAL_DIRECTORY = 1 << 1,
+ IDE_VCS_CLONE_REQUEST_INVAL_EMAIL = 1 << 2,
+} IdeVcsCloneRequestValidation;
+
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeVcsCloneRequest, ide_vcs_clone_request, IDE, VCS_CLONE_REQUEST, IdeObject)
+
+IDE_AVAILABLE_IN_ALL
+IdeVcsCloneRequest *ide_vcs_clone_request_new (void);
+IDE_AVAILABLE_IN_ALL
+const char *ide_vcs_clone_request_get_module_name (IdeVcsCloneRequest *self);
+IDE_AVAILABLE_IN_ALL
+void ide_vcs_clone_request_set_module_name (IdeVcsCloneRequest *self,
+ const char *module_name);
+IDE_AVAILABLE_IN_ALL
+const char *ide_vcs_clone_request_get_author_name (IdeVcsCloneRequest *self);
+IDE_AVAILABLE_IN_ALL
+void ide_vcs_clone_request_set_author_name (IdeVcsCloneRequest *self,
+ const char *author_name);
+IDE_AVAILABLE_IN_ALL
+const char *ide_vcs_clone_request_get_author_email (IdeVcsCloneRequest *self);
+IDE_AVAILABLE_IN_ALL
+void ide_vcs_clone_request_set_author_email (IdeVcsCloneRequest *self,
+ const char *author_email);
+GListModel *ide_vcs_clone_request_get_branch_model (IdeVcsCloneRequest *self);
+IDE_AVAILABLE_IN_ALL
+const char *ide_vcs_clone_request_get_branch_name (IdeVcsCloneRequest *self);
+IDE_AVAILABLE_IN_ALL
+void ide_vcs_clone_request_set_branch_name (IdeVcsCloneRequest *self,
+ const char *branch_name);
+IDE_AVAILABLE_IN_ALL
+const char *ide_vcs_clone_request_get_uri (IdeVcsCloneRequest *self);
+IDE_AVAILABLE_IN_ALL
+void ide_vcs_clone_request_set_uri (IdeVcsCloneRequest *self,
+ const char *uri);
+IDE_AVAILABLE_IN_ALL
+GFile *ide_vcs_clone_request_get_directory (IdeVcsCloneRequest *self);
+IDE_AVAILABLE_IN_ALL
+void ide_vcs_clone_request_set_directory (IdeVcsCloneRequest *self,
+ GFile *directory);
+IDE_AVAILABLE_IN_ALL
+void ide_vcs_clone_request_populate_branches (IdeVcsCloneRequest *self);
+IDE_AVAILABLE_IN_ALL
+IdeVcsCloneRequestValidation ide_vcs_clone_request_validate (IdeVcsCloneRequest *self);
+IDE_AVAILABLE_IN_ALL
+void ide_vcs_clone_request_clone_async (IdeVcsCloneRequest *self,
+ IdeNotification *notif,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_ALL
+GFile *ide_vcs_clone_request_clone_finish (IdeVcsCloneRequest *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/libide/vcs/ide-vcs-tag.c b/src/libide/vcs/ide-vcs-tag.c
index 6acd80d9c..5ec2986bc 100644
--- a/src/libide/vcs/ide-vcs-tag.c
+++ b/src/libide/vcs/ide-vcs-tag.c
@@ -29,26 +29,27 @@ G_DEFINE_INTERFACE (IdeVcsTag, ide_vcs_tag, G_TYPE_OBJECT)
static void
ide_vcs_tag_default_init (IdeVcsTagInterface *iface)
{
+ g_object_interface_install_property (iface,
+ g_param_spec_string ("name", NULL, NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
}
/**
- * ide_vcs_tag_get_name:
+ * ide_vcs_tag_dup_name:
* @self: an #IdeVcsTag
*
* Gets the name of the tag, which is used in various UI elements
* to display to the user.
*
* Returns: (transfer full): a string containing the tag name
- *
- * Since: 3.32
*/
-gchar *
-ide_vcs_tag_get_name (IdeVcsTag *self)
+char *
+ide_vcs_tag_dup_name (IdeVcsTag *self)
{
g_return_val_if_fail (IDE_IS_VCS_TAG (self), NULL);
- if (IDE_VCS_TAG_GET_IFACE (self)->get_name)
- return IDE_VCS_TAG_GET_IFACE (self)->get_name (self);
+ if (IDE_VCS_TAG_GET_IFACE (self)->dup_name)
+ return IDE_VCS_TAG_GET_IFACE (self)->dup_name (self);
return NULL;
}
diff --git a/src/libide/vcs/ide-vcs-tag.h b/src/libide/vcs/ide-vcs-tag.h
index 8992d9cd4..3754532e3 100644
--- a/src/libide/vcs/ide-vcs-tag.h
+++ b/src/libide/vcs/ide-vcs-tag.h
@@ -26,17 +26,17 @@ G_BEGIN_DECLS
#define IDE_TYPE_VCS_TAG (ide_vcs_tag_get_type ())
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (IdeVcsTag, ide_vcs_tag, IDE, VCS_TAG, GObject)
struct _IdeVcsTagInterface
{
GTypeInterface parent;
- gchar *(*get_name) (IdeVcsTag *self);
+ char *(*dup_name) (IdeVcsTag *self);
};
-IDE_AVAILABLE_IN_3_32
-gchar *ide_vcs_tag_get_name (IdeVcsTag *self);
+IDE_AVAILABLE_IN_ALL
+char *ide_vcs_tag_dup_name (IdeVcsTag *self);
G_END_DECLS
diff --git a/src/libide/vcs/libide-vcs.h b/src/libide/vcs/libide-vcs.h
index aafb4ed38..4f1a48c0d 100644
--- a/src/libide/vcs/libide-vcs.h
+++ b/src/libide/vcs/libide-vcs.h
@@ -28,6 +28,7 @@
#include "ide-directory-vcs.h"
#include "ide-vcs-branch.h"
#include "ide-vcs-cloner.h"
+#include "ide-vcs-clone-request.h"
#include "ide-vcs-config.h"
#include "ide-vcs-enums.h"
#include "ide-vcs-initializer.h"
diff --git a/src/libide/vcs/meson.build b/src/libide/vcs/meson.build
index c762afb64..4f7fda4a3 100644
--- a/src/libide/vcs/meson.build
+++ b/src/libide/vcs/meson.build
@@ -11,6 +11,7 @@ libide_vcs_public_headers = [
'ide-vcs-branch.h',
'ide-vcs-config.h',
'ide-vcs-cloner.h',
+ 'ide-vcs-clone-request.h',
'ide-vcs-file-info.h',
'ide-vcs-initializer.h',
'ide-vcs-monitor.h',
@@ -36,6 +37,7 @@ libide_vcs_public_sources = [
'ide-vcs-branch.c',
'ide-vcs-config.c',
'ide-vcs-cloner.c',
+ 'ide-vcs-clone-request.c',
'ide-vcs-file-info.c',
'ide-vcs-initializer.c',
'ide-vcs-monitor.c',
@@ -69,6 +71,7 @@ libide_vcs_deps = [
libide_core_dep,
libide_io_dep,
+ libide_plugins_dep,
libide_threading_dep,
]
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]