Refernces contain three pieces of data which we already keep in the erlang record. Keep the repository it belongs to in the record as well and remove resource in the NIF. This makes the code easier to reason about, as we don't need to keep any reference counts or GC the C struct. This does mean we cannot re-use references across geef_repo restarts, but that's probably a good thing.
421 lines
9.5 KiB
C
421 lines
9.5 KiB
C
#include "geef.h"
|
|
#include "repository.h"
|
|
#include "reference.h"
|
|
#include "oid.h"
|
|
#include <string.h>
|
|
#include <git2.h>
|
|
|
|
ERL_NIF_TERM
|
|
geef_reference_list(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
size_t i;
|
|
git_strarray array;
|
|
geef_repository *repo;
|
|
ERL_NIF_TERM list;
|
|
|
|
if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo))
|
|
return enif_make_badarg(env);
|
|
|
|
if (git_reference_list(&array, repo->repo) < 0)
|
|
return geef_error(env);
|
|
|
|
list = enif_make_list(env, 0);
|
|
for (i = 0; i < array.count; i++) {
|
|
ErlNifBinary bin;
|
|
size_t len = strlen(array.strings[i]);
|
|
|
|
if (!enif_alloc_binary(len, &bin))
|
|
goto on_error;
|
|
|
|
memcpy(bin.data, array.strings[i], len);
|
|
list = enif_make_list_cell(env, enif_make_binary(env, &bin), list);
|
|
}
|
|
|
|
git_strarray_free(&array);
|
|
|
|
return list;
|
|
|
|
on_error:
|
|
git_strarray_free(&array);
|
|
return geef_oom(env);
|
|
}
|
|
|
|
static int ref_target(ERL_NIF_TERM *out, ErlNifEnv *env, git_reference *ref)
|
|
{
|
|
ErlNifBinary bin;
|
|
|
|
if (git_reference_type(ref) == GIT_REF_OID) {
|
|
const git_oid *id;
|
|
id = git_reference_target(ref);
|
|
|
|
if (geef_oid_bin(&bin, id) < 0)
|
|
return -1;
|
|
} else {
|
|
const char *name;
|
|
size_t len;
|
|
|
|
name = git_reference_symbolic_target(ref);
|
|
len = strlen(name);
|
|
|
|
if (enif_alloc_binary(len, &bin) < 0)
|
|
return -1;
|
|
|
|
memcpy(bin.data, name, len);
|
|
}
|
|
|
|
*out = enif_make_binary(env, &bin);
|
|
return 0;
|
|
}
|
|
|
|
static ERL_NIF_TERM ref_type(git_reference *ref)
|
|
{
|
|
ERL_NIF_TERM type;
|
|
|
|
switch (git_reference_type(ref)) {
|
|
case GIT_REF_OID:
|
|
type = atoms.oid;
|
|
break;
|
|
case GIT_REF_SYMBOLIC:
|
|
type = atoms.symbolic;
|
|
break;
|
|
default:
|
|
type = atoms.error;
|
|
break;
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
geef_reference_lookup(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
geef_repository *repo;
|
|
git_reference *ref = NULL;
|
|
ERL_NIF_TERM target, type;
|
|
ErlNifBinary bin;
|
|
int error;
|
|
|
|
if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo))
|
|
return enif_make_badarg(env);
|
|
|
|
if (!enif_inspect_iolist_as_binary(env, argv[1], &bin))
|
|
return enif_make_badarg(env);
|
|
|
|
if (!geef_terminate_binary(&bin))
|
|
goto on_oom;
|
|
|
|
error = git_reference_lookup(&ref, repo->repo, (char *)bin.data);
|
|
enif_release_binary(&bin);
|
|
if (error < 0)
|
|
return geef_error(env);
|
|
|
|
type = ref_type(ref);
|
|
if (ref_target(&target, env, ref) < 0)
|
|
goto on_oom;
|
|
|
|
return enif_make_tuple3(env, atoms.ok, type, target);
|
|
|
|
on_oom:
|
|
git_reference_free(ref);
|
|
enif_release_binary(&bin);
|
|
|
|
return geef_oom(env);
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
geef_reference_iterator(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
ErlNifBinary bin;
|
|
int globbing, error;
|
|
geef_repository *repo;
|
|
ERL_NIF_TERM term_iter;
|
|
geef_ref_iter *res_iter;
|
|
git_reference_iterator *iter;
|
|
|
|
if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo))
|
|
return enif_make_badarg(env);
|
|
|
|
|
|
if (enif_is_identical(argv[1], atoms.undefined)) {
|
|
globbing = 0;
|
|
} else if (enif_inspect_iolist_as_binary(env, argv[1], &bin)) {
|
|
globbing = 1;
|
|
} else {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (globbing && !geef_terminate_binary(&bin))
|
|
return atoms.error;
|
|
|
|
if (globbing)
|
|
error = git_reference_iterator_glob_new(&iter, repo->repo, (char *) bin.data);
|
|
else
|
|
error = git_reference_iterator_new(&iter, repo->repo);
|
|
|
|
if (error < 0)
|
|
return geef_error(env);
|
|
|
|
res_iter = enif_alloc_resource(geef_ref_iter_type, sizeof(geef_ref_iter));
|
|
res_iter->iter = iter;
|
|
res_iter->repo = repo;
|
|
enif_keep_resource(repo);
|
|
term_iter = enif_make_resource(env, res_iter);
|
|
enif_release_resource(res_iter);
|
|
|
|
return enif_make_tuple2(env, atoms.ok, term_iter);
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
geef_reference_next(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
int error;
|
|
size_t len;
|
|
const char *name;
|
|
git_reference *ref;
|
|
ErlNifBinary bin;
|
|
geef_ref_iter *iter;
|
|
ERL_NIF_TERM type, target;
|
|
|
|
if (!enif_get_resource(env, argv[0], geef_ref_iter_type, (void **) &iter))
|
|
return enif_make_badarg(env);
|
|
|
|
error = git_reference_next(&ref, iter->iter);
|
|
if (error == GIT_ITEROVER)
|
|
return enif_make_tuple2(env, atoms.error, atoms.iterover);
|
|
if (error < 0)
|
|
return geef_error(env);
|
|
|
|
type = ref_type(ref);
|
|
if (ref_target(&target, env, ref) < 0) {
|
|
git_reference_free(ref);
|
|
return geef_oom(env);
|
|
}
|
|
|
|
name = git_reference_name(ref);
|
|
len = strlen(name);
|
|
if (!enif_alloc_binary(len, &bin)) {
|
|
git_reference_free(ref);
|
|
return geef_oom(env);
|
|
}
|
|
|
|
memcpy(bin.data, name, len);
|
|
|
|
return enif_make_tuple4(env, atoms.ok, enif_make_binary(env, &bin), type, target);
|
|
}
|
|
|
|
void geef_ref_iter_free(ErlNifEnv *env, void *cd)
|
|
{
|
|
geef_ref_iter *ref = (geef_ref_iter *) cd;
|
|
git_reference_iterator_free(ref->iter);
|
|
enif_release_resource(ref->repo);
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
geef_reference_resolve(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
size_t len;
|
|
const char *name;
|
|
ErlNifBinary bin, id;
|
|
geef_repository *repo;
|
|
git_reference *ref, *resolved;
|
|
|
|
if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo))
|
|
return enif_make_badarg(env);
|
|
|
|
if (!enif_inspect_binary(env, argv[1], &bin))
|
|
return enif_make_badarg(env);
|
|
|
|
if (!geef_terminate_binary(&bin))
|
|
return geef_oom(env);
|
|
|
|
if (git_reference_lookup(&ref, repo->repo, (char *) bin.data) < 0)
|
|
return geef_error(env);
|
|
|
|
if (git_reference_resolve(&resolved, ref) < 0)
|
|
return geef_error(env);
|
|
|
|
git_reference_free(ref);
|
|
name = git_reference_name(resolved);
|
|
len = strlen(name);
|
|
|
|
if (enif_realloc_binary(&bin, len) < 0)
|
|
goto on_oom;
|
|
|
|
memcpy(bin.data, name, len);
|
|
|
|
if (geef_oid_bin(&id, git_reference_target(resolved)) < 0)
|
|
goto on_oom;
|
|
|
|
git_reference_free(resolved);
|
|
|
|
return enif_make_tuple3(env, atoms.ok, enif_make_binary(env, &bin), enif_make_binary(env, &id));
|
|
|
|
on_oom:
|
|
git_reference_free(resolved);
|
|
return geef_oom(env);
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
geef_reference_dwim(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
ErlNifBinary bin;
|
|
ERL_NIF_TERM target, type;
|
|
geef_repository *repo;
|
|
git_reference *ref;
|
|
const char *name;
|
|
size_t len;
|
|
|
|
if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo))
|
|
return enif_make_badarg(env);
|
|
|
|
if (!enif_inspect_iolist_as_binary(env, argv[1], &bin))
|
|
return enif_make_badarg(env);
|
|
|
|
if (!geef_terminate_binary(&bin))
|
|
return geef_oom(env);
|
|
|
|
if (git_reference_dwim(&ref, repo->repo, (char *)bin.data) < 0) {
|
|
enif_release_binary(&bin);
|
|
return geef_error(env);
|
|
}
|
|
|
|
type = ref_type(ref);
|
|
if (ref_target(&target, env, ref) < 0) {
|
|
git_reference_free(ref);
|
|
return geef_oom(env);
|
|
}
|
|
|
|
name = git_reference_name(ref);
|
|
len = strlen(name);
|
|
if (!enif_realloc_binary(&bin, len)) {
|
|
git_reference_free(ref);
|
|
enif_release_binary(&bin);
|
|
}
|
|
|
|
memcpy(bin.data, name, len);
|
|
|
|
return enif_make_tuple4(env, atoms.ok, enif_make_binary(env, &bin), type, target);
|
|
}
|
|
|
|
struct list_data {
|
|
ErlNifEnv *env;
|
|
ERL_NIF_TERM list;
|
|
};
|
|
|
|
static int append_to_list(const char *name, void *payload)
|
|
{
|
|
struct list_data *data = (struct list_data *) payload;
|
|
ErlNifBinary bin;
|
|
size_t len = strlen(name);
|
|
|
|
if (!enif_alloc_binary(len, &bin))
|
|
return -1;
|
|
|
|
memcpy(bin.data, name, len);
|
|
data->list = enif_make_list_cell(data->env, enif_make_binary(data->env, &bin), data->list);
|
|
return 0;
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
geef_reference_glob(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
int error;
|
|
geef_repository *repo;
|
|
ErlNifBinary bin;
|
|
struct list_data data;
|
|
|
|
if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo))
|
|
return enif_make_badarg(env);
|
|
|
|
if (!enif_inspect_iolist_as_binary(env, argv[1], &bin))
|
|
return enif_make_badarg(env);
|
|
|
|
if (!geef_terminate_binary(&bin))
|
|
return geef_oom(env);
|
|
|
|
data.env = env;
|
|
data.list = enif_make_list(env, 0);
|
|
|
|
error = git_reference_foreach_glob(repo->repo, (char *) bin.data, append_to_list, &data);
|
|
|
|
enif_release_binary(&bin);
|
|
if (error < 0)
|
|
return geef_error(env);
|
|
|
|
return data.list;
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
geef_reference_to_id(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
geef_repository *repo;
|
|
ErlNifBinary bin;
|
|
git_oid id;
|
|
|
|
if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo))
|
|
return enif_make_badarg(env);
|
|
|
|
if (!enif_inspect_iolist_as_binary(env, argv[1], &bin))
|
|
return enif_make_badarg(env);
|
|
|
|
if (!geef_terminate_binary(&bin))
|
|
return geef_oom(env);
|
|
|
|
if (git_reference_name_to_id(&id, repo->repo, (char *)bin.data) < 0)
|
|
return geef_error(env);
|
|
|
|
if (geef_oid_bin(&bin, &id) < 0)
|
|
return geef_oom(env);
|
|
|
|
return enif_make_tuple2(env, atoms.ok, enif_make_binary(env, &bin));
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
geef_reference_create(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
geef_repository *repo;
|
|
ErlNifBinary name, target;
|
|
int error, force;
|
|
git_reference *ref;
|
|
const char *pname, *ptarget;
|
|
|
|
if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo))
|
|
return enif_make_badarg(env);
|
|
|
|
if (!enif_inspect_iolist_as_binary(env, argv[1], &name))
|
|
return enif_make_badarg(env);
|
|
|
|
if (!enif_inspect_iolist_as_binary(env, argv[3], &target))
|
|
return enif_make_badarg(env);
|
|
|
|
if (!geef_terminate_binary(&name))
|
|
return geef_oom(env);
|
|
|
|
force = enif_is_identical(argv[4], atoms.true);
|
|
|
|
pname = (const char *) name.data;
|
|
if (enif_is_identical(argv[2], atoms.oid)) {
|
|
const git_oid *oid = (const git_oid *) target.data;
|
|
error = git_reference_create(&ref, repo->repo, pname, oid, force);
|
|
} else if (enif_is_identical(argv[2], atoms.symbolic)) {
|
|
if (!geef_terminate_binary(&target))
|
|
return geef_oom(env);
|
|
|
|
ptarget = (const char *) target.data;
|
|
error = git_reference_symbolic_create(&ref, repo->repo, pname, ptarget, force);
|
|
enif_release_binary(&target);
|
|
} else {
|
|
enif_release_binary(&target);
|
|
enif_release_binary(&name);
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
git_reference_free(ref);
|
|
enif_release_binary(&name);
|
|
|
|
if (error < 0)
|
|
return geef_error(env);
|
|
|
|
return atoms.ok;
|
|
}
|