315 lines
9.7 KiB
C
315 lines
9.7 KiB
C
|
/* ide-vcs-cloner.c
|
||
|
*
|
||
|
* Copyright 2018-2019 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-cloner"
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include <gdk/gdk.h>
|
||
|
|
||
|
#include <libide-threading.h>
|
||
|
#include <libpeas/peas.h>
|
||
|
|
||
|
#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;
|
||
|
}
|
||
|
|