/* ide-vcs-cloner.c * * Copyright 2018-2019 Christian Hergert * * 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 . * * SPDX-License-Identifier: GPL-3.0-or-later */ #define G_LOG_DOMAIN "ide-vcs-cloner" #include "config.h" #include #include #include #include "ide-vcs-cloner.h" G_DEFINE_INTERFACE (IdeVcsCloner, ide_vcs_cloner, IDE_TYPE_OBJECT) static void ide_vcs_cloner_default_init (IdeVcsClonerInterface *iface) { } /** * ide_vcs_cloner_validate_uri: * @self: a #IdeVcsCloner * @uri: a string containing the URI to validate * @errmsg: (out) (optional): a location for an error message * * Checks to see if @uri is valid, and if not, sets @errmsg to a string * describing how the URI is invalid. * * Returns: %TRUE if @uri is valid, otherwise %FALSE and @error is set. * * Since: 3.32 */ gboolean ide_vcs_cloner_validate_uri (IdeVcsCloner *self, const gchar *uri, gchar **errmsg) { g_return_val_if_fail (IDE_IS_VCS_CLONER (self), FALSE); if (errmsg != NULL) *errmsg = NULL; if (IDE_VCS_CLONER_GET_IFACE (self)->validate_uri) return IDE_VCS_CLONER_GET_IFACE (self)->validate_uri (self, uri, errmsg); return FALSE; } /** * ide_vcs_cloner_clone_async: * @self: an #IdeVcsCloner * @uri: a string containing the URI * @destination: a string containing the destination path * @options: a #GVariant containing any user supplied options * @cancellable: (nullable): a #GCancellable * @progress: (nullable): a location for an #IdeNotification, or %NULL * @callback: a #GAsyncReadyCallback to execute upon completion * @user_data: closure data for @callback * * Since: 3.32 */ void ide_vcs_cloner_clone_async (IdeVcsCloner *self, const gchar *uri, const gchar *destination, GVariant *options, IdeNotification *progress, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (IDE_IS_VCS_CLONER (self)); g_return_if_fail (uri != NULL); g_return_if_fail (destination != NULL); g_return_if_fail (options != NULL); g_return_if_fail (g_variant_is_of_type (options, G_VARIANT_TYPE_VARDICT)); g_return_if_fail (!progress || IDE_IS_NOTIFICATION (progress)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); IDE_VCS_CLONER_GET_IFACE (self)->clone_async (self, uri, destination, options, progress, cancellable, callback, user_data); } /** * ide_vcs_cloner_clone_finish: * @self: an #IdeVcsCloner * @result: a #GAsyncResult provided to callback * @error: a location for a #GError, or %NULL * * Returns: %TRUE if successful; otherwise %FALSE and @error is set. * * Since: 3.32 */ gboolean ide_vcs_cloner_clone_finish (IdeVcsCloner *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (IDE_IS_VCS_CLONER (self), FALSE); g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); return IDE_VCS_CLONER_GET_IFACE (self)->clone_finish (self, result, error); } /** * ide_vcs_cloner_get_title: * @self: a #IdeVcsCloner * * Gets the for the cloner, such as "Git". This may be used to present * a selector to the user based on the backend clone engine. Other suitable * titles might be "Subversion" or "CVS". * * Returns: (transfer full): a string containing the title * * Since: 3.32 */ gchar * ide_vcs_cloner_get_title (IdeVcsCloner *self) { g_return_val_if_fail (IDE_IS_VCS_CLONER (self), NULL); if (IDE_VCS_CLONER_GET_IFACE (self)->get_title) return IDE_VCS_CLONER_GET_IFACE (self)->get_title (self); return NULL; } typedef struct { GMutex mutex; GCond cond; IdeContext *context; const gchar *module_name; const gchar *url; const gchar *branch; const gchar *destination; IdeNotification *notif; GCancellable *cancellable; GError *error; } CloneSimple; static void ide_vcs_cloner_clone_simple_clone_cb (GObject *object, GAsyncResult *result, gpointer user_data) { IdeVcsCloner *cloner = (IdeVcsCloner *)object; g_autoptr(GError) error = NULL; CloneSimple *state = user_data; g_assert (IDE_IS_VCS_CLONER (cloner)); g_assert (G_IS_ASYNC_RESULT (result)); g_assert (state->module_name != NULL); g_assert (state->url != NULL); g_assert (state->destination != NULL); g_assert (!state->notif || IDE_IS_NOTIFICATION (state->notif)); g_assert (!state->cancellable || IDE_IS_NOTIFICATION (state->cancellable)); g_assert (state->error == NULL); ide_vcs_cloner_clone_finish (cloner, result, &state->error); g_mutex_lock (&state->mutex); g_cond_signal (&state->cond); g_mutex_unlock (&state->mutex); } static gboolean ide_vcs_cloner_clone_simple_idle_cb (CloneSimple *state) { g_autoptr(PeasExtension) exten = NULL; g_autoptr(GVariant) options = NULL; PeasPluginInfo *plugin_info; PeasEngine *engine; GVariantDict dict; g_assert (state != NULL); g_assert (IDE_IS_CONTEXT (state->context)); g_assert (state->module_name != NULL); g_assert (state->url != NULL); g_assert (state->destination != NULL); g_assert (!state->notif || IDE_IS_NOTIFICATION (state->notif)); g_assert (!state->cancellable || IDE_IS_NOTIFICATION (state->cancellable)); g_assert (state->error == NULL); engine = peas_engine_get_default (); if (!(plugin_info = peas_engine_get_plugin_info (engine, state->module_name))) { g_set_error (&state->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "No such module %s", state->module_name); goto notify; } exten = peas_engine_create_extension (engine, plugin_info, IDE_TYPE_VCS_CLONER, "parent", state->context, NULL); if (exten == NULL) { g_set_error (&state->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Failed to create IdeVcsCloner from module %s", state->module_name); goto notify; } g_variant_dict_init (&dict, NULL); if (state->branch != NULL) g_variant_dict_insert (&dict, "branch", "s", state->branch); options = g_variant_take_ref (g_variant_dict_end (&dict)); ide_vcs_cloner_clone_async (IDE_VCS_CLONER (exten), state->url, state->destination, options, state->notif, state->cancellable, ide_vcs_cloner_clone_simple_clone_cb, state); return G_SOURCE_REMOVE; notify: g_mutex_lock (&state->mutex); g_cond_signal (&state->cond); g_mutex_unlock (&state->mutex); return G_SOURCE_REMOVE; } gboolean ide_vcs_cloner_clone_simple (IdeContext *context, const gchar *module_name, const gchar *url, const gchar *branch, const gchar *destination, IdeNotification *notif, GCancellable *cancellable, GError **error) { CloneSimple state = { .context = context, .module_name = module_name, .url = url, .branch = branch, .destination = destination, .notif = notif, .cancellable = cancellable, .error = NULL, }; g_return_val_if_fail (!IDE_IS_MAIN_THREAD (), FALSE); g_return_val_if_fail (IDE_IS_CONTEXT (context), FALSE); g_return_val_if_fail (module_name != NULL, FALSE); g_return_val_if_fail (url != NULL, FALSE); g_return_val_if_fail (destination != NULL, FALSE); g_return_val_if_fail (!notif || IDE_IS_NOTIFICATION (notif), FALSE); g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE); g_mutex_init (&state.mutex); g_cond_init (&state.cond); g_mutex_lock (&state.mutex); gdk_threads_add_idle_full (G_PRIORITY_HIGH, (GSourceFunc) ide_vcs_cloner_clone_simple_idle_cb, &state, NULL); g_cond_wait (&state.cond, &state.mutex); g_mutex_unlock (&state.mutex); g_cond_clear (&state.cond); g_mutex_clear (&state.mutex); if (state.error != NULL) { g_propagate_error (error, g_steal_pointer (&state.error)); return FALSE; } return TRUE; }