diff --git a/c_src/geef.c b/c_src/geef.c index c3890c8..e2d5bb7 100644 --- a/c_src/geef.c +++ b/c_src/geef.c @@ -19,6 +19,7 @@ ErlNifResourceType *geef_repository_type; ErlNifResourceType *geef_odb_type; ErlNifResourceType *geef_ref_type; +ErlNifResourceType *geef_ref_iter_type; ErlNifResourceType *geef_object_type; ErlNifResourceType *geef_revwalk_type; ErlNifResourceType *geef_index_type; @@ -42,11 +43,17 @@ static int load(ErlNifEnv *env, void **priv, ERL_NIF_TERM load_info) return -1; geef_ref_type = enif_open_resource_type(env, NULL, - "ref_type", geef_ref_free, ERL_NIF_RT_CREATE, NULL); + "ref_type", geef_ref_free, ERL_NIF_RT_CREATE, NULL); if (geef_ref_type == NULL) return -1; + geef_ref_iter_type = enif_open_resource_type(env, NULL, + "ref_iter_type", geef_ref_iter_free, ERL_NIF_RT_CREATE, NULL); + + if (geef_ref_iter_type == NULL) + return -1; + geef_object_type = enif_open_resource_type(env, NULL, "object_type", geef_object_free, ERL_NIF_RT_CREATE, NULL); @@ -70,6 +77,7 @@ static int load(ErlNifEnv *env, void **priv, ERL_NIF_TERM load_info) atoms.tree = enif_make_atom(env, "tree"); atoms.blob = enif_make_atom(env, "blob"); atoms.tag = enif_make_atom(env, "tag"); + atoms.undefined = enif_make_atom(env, "undefined"); /* Revwalk */ atoms.toposort = enif_make_atom(env, "sort_topo"); atoms.timesort = enif_make_atom(env, "sort_time"); @@ -150,6 +158,8 @@ static ErlNifFunc geef_funcs[] = {"reference_to_id", 2, geef_reference_to_id}, {"reference_glob", 2, geef_reference_glob}, {"reference_lookup", 2, geef_reference_lookup}, + {"reference_iterator", 2, geef_reference_iterator}, + {"reference_next", 1, geef_reference_next}, {"reference_resolve", 1, geef_reference_resolve}, {"reference_target", 1, geef_reference_target}, {"reference_type", 1, geef_reference_type}, diff --git a/c_src/geef.h b/c_src/geef.h index 0479081..e3dec2a 100644 --- a/c_src/geef.h +++ b/c_src/geef.h @@ -18,6 +18,7 @@ typedef struct { ERL_NIF_TERM tree; ERL_NIF_TERM blob; ERL_NIF_TERM tag; + ERL_NIF_TERM undefined; ERL_NIF_TERM toposort; ERL_NIF_TERM timesort; ERL_NIF_TERM reversesort; diff --git a/c_src/reference.c b/c_src/reference.c index 14a69d5..cc0503f 100644 --- a/c_src/reference.c +++ b/c_src/reference.c @@ -73,6 +73,82 @@ geef_reference_lookup(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) return enif_make_tuple2(env, atoms.ok, term_ref); } +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; + geef_ref *res_ref; + git_reference *ref; + geef_ref_iter *iter; + ERL_NIF_TERM term_ref; + + 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); + + res_ref = enif_alloc_resource(geef_ref_type, sizeof(geef_ref)); + res_ref->ref = ref; + term_ref = enif_make_resource(env, res_ref); + enif_release_resource(res_ref); + + return enif_make_tuple2(env, atoms.ok, term_ref); +} + +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[]) { diff --git a/c_src/reference.h b/c_src/reference.h index 76bd161..73ea001 100644 --- a/c_src/reference.h +++ b/c_src/reference.h @@ -5,11 +5,17 @@ #include extern ErlNifResourceType *geef_ref_type; +extern ErlNifResourceType *geef_ref_iter_type; typedef struct { git_reference *ref; } geef_ref; +typedef struct { + git_reference_iterator *iter; + geef_repository *repo; +} geef_ref_iter; + ERL_NIF_TERM geef_reference_list(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM geef_reference_to_id(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM geef_reference_glob(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); @@ -20,7 +26,10 @@ ERL_NIF_TERM geef_reference_type(ErlNifEnv *env, int argc, const ERL_NIF_TERM ar ERL_NIF_TERM geef_reference_name(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM geef_reference_create(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM geef_reference_dwim(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM geef_reference_iterator(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM geef_reference_next(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); void geef_ref_free(ErlNifEnv *env, void *cd); +void geef_ref_iter_free(ErlNifEnv *env, void *cd); #endif diff --git a/src/geef_nif.erl b/src/geef_nif.erl index 6ba8fd5..ee01c92 100644 --- a/src/geef_nif.erl +++ b/src/geef_nif.erl @@ -4,6 +4,8 @@ -module(geef_nif). -compile(export_all). +-include("geef_records.hrl"). + -on_load(load_enif/0). -define(NIF_FN, nif_error(?LINE)). @@ -50,6 +52,14 @@ reference_glob(_Repo, _Glob) -> reference_lookup(_Repo, _Refname) -> nif_error(?LINE). +-spec reference_iterator(term(), iolist() | undefined) -> {ok, geef_iterator()} | {error, term()}. +reference_iterator(_Repo, _Regexp) -> + nif_error(?LINE). + +-spec reference_next(geef_iterator()) -> {ok, term()} | {error, iterover | term()}. +reference_next(_Handle) -> + nif_error(?LINE). + reference_target(_Handle) -> nif_error(?LINE). diff --git a/src/geef_records.hrl b/src/geef_records.hrl index 6b9e4f3..5b9b784 100644 --- a/src/geef_records.hrl +++ b/src/geef_records.hrl @@ -13,6 +13,7 @@ -record(geef_request, {service :: atom(), path :: binary(), host :: binary()}). -record(geef_tree_entry, {mode, type, id, name}). -record(geef_signature, {name :: iolist(), email :: iolist(), time :: geef_time()}). +-record(geef_iterator, {type :: atom(), handle}). -type geef_reference() :: #geef_reference{name :: binary(), target :: binary() | geef_oid()}. -type geef_oid() :: #geef_oid{oid :: binary()}. @@ -22,3 +23,4 @@ -type geef_tree_entry() :: #geef_tree_entry{}. -type geef_signature() :: #geef_signature{}. -type geef_time() :: {erlang:timestamp(), non_neg_integer()}. +-type geef_iterator() :: #geef_iterator{}. diff --git a/src/geef_ref.erl b/src/geef_ref.erl index e604503..8b6b3a1 100644 --- a/src/geef_ref.erl +++ b/src/geef_ref.erl @@ -1,6 +1,6 @@ -module(geef_ref). --export([lookup/2, resolve/1, create/4, dwim/2, shorthand/1]). +-export([lookup/2, iterator/1, iterator/2, next/1, resolve/1, create/4, dwim/2, shorthand/1]). -include("geef_records.hrl"). @@ -35,6 +35,28 @@ lookup(Repo, Refname) -> Other end. +-spec iterator(pid(), iolist() | undefined) -> {ok, geef_iterator()} | {error, term()}. +iterator(Repo, Regexp) -> + case geef_repo:iterator(Repo, Regexp) of + {ok, Handle} -> + {ok, #geef_iterator{type=ref, handle=Handle}}; + Other -> + Other + end. + +-spec iterator(pid()) -> {ok, geef_iterator()} | {error, term()}. +iterator(Repo) -> + iterator(Repo, undefined). + +next(#geef_iterator{type=ref, handle=Handle}) -> + case geef_nif:reference_next(Handle) of + {ok, RefHandle} -> + {ok, Name} = geef_nif:reference_name(RefHandle), + {ok, new(Name, RefHandle)}; + Other -> + Other + end. + -spec resolve(geef_reference()) -> {ok, geef_reference()} | {error, term()}. resolve(Ref = #geef_reference{type=oid}) -> {ok, Ref}; % resolving an oid ref is a no-op, skip going into the NIF diff --git a/src/geef_repo.erl b/src/geef_repo.erl index 1050ecb..9f20b08 100644 --- a/src/geef_repo.erl +++ b/src/geef_repo.erl @@ -17,7 +17,7 @@ %% API -export([open/1, init/2, path/1, workdir/1, odb/1, is_bare/1, references/1, discover/1, lookup_object/2, lookup_reference/2, create_reference/4, revwalk/1, stop/1, - reference_dwim/2, handle/1]). + reference_dwim/2, handle/1, iterator/2]). -include("geef_records.hrl"). -record(state, {handle}). @@ -86,6 +86,11 @@ lookup_object(Pid, Oid) -> lookup_reference(Pid, Name) -> gen_server:call(Pid, {lookup_reference, Name}). +%% @private +-spec iterator(pid(), iolist() | undefined) -> {ok, geef_iterator()} | {error, term()}. +iterator(Pid, Regexp) -> + gen_server:call(Pid, {iterator, Regexp}). + %% @private reference_dwim(Pid, Name) -> gen_server:call(Pid, {dwim_reference, Name}). @@ -136,6 +141,9 @@ handle_call({lookup_object, Oid}, _From, State = #state{handle=Handle}) -> handle_call({lookup_reference, Name}, _From, State = #state{handle=Handle}) -> Reply = geef_nif:reference_lookup(Handle, Name), {reply, Reply, State}; +handle_call({iterator, Regexp}, _From, State = #state{handle=Handle}) -> + Reply = geef_nif:reference_iterator(Handle, Regexp), + {reply, Reply, State}; handle_call({dwim_reference, Name}, _From, State = #state{handle=Handle}) -> Reply = geef_nif:reference_dwim(Handle, Name), {reply, Reply, State}; diff --git a/test/repo_test.erl b/test/repo_test.erl index 5ed04a8..e4e373b 100644 --- a/test/repo_test.erl +++ b/test/repo_test.erl @@ -5,7 +5,8 @@ repo_test_() -> {foreach, fun start/0, fun stop/1, [fun bare_test/1, fun odb_write_test/1, - fun ref_test/1, fun index_add_test/1]}. + fun ref_test/1, fun index_add_test/1, + fun ref_iter_test/1]}. start() -> {A, B, C} = now(), @@ -48,6 +49,24 @@ ref_test(Repo) -> ?_assertEqual(<<"branch">>, geef_ref:shorthand(Ref0)), ?_assertEqual(Ref0, Dwimed)]. +ref_iter_test(Repo) -> + odb_write_test(Repo), + Id = geef_oid:parse("c300118399f01fe52b316061b5d32beb27e0adfd"), + {ok, _} = geef_ref:create(Repo, "refs/heads/branch", Id, true), + {ok, _} = geef_ref:create(Repo, "refs/heads/other", "refs/heads/branch", true), + {ok, Iter0} = geef_ref:iterator(Repo), + {ok, Ref0} = geef_ref:next(Iter0), + {ok, Ref1} = geef_ref:next(Iter0), + Res0 = geef_ref:next(Iter0), + {ok, Iter1} = geef_ref:iterator(Repo, "refs/heads/b*"), + {ok, Ref2} = geef_ref:next(Iter1), + Res1 = geef_ref:next(Iter1), + [?_assertEqual(Ref0#geef_reference.name, <<"refs/heads/branch">>), + ?_assertEqual(Ref1#geef_reference.name, <<"refs/heads/other">>), + ?_assertEqual(Res0, {error, iterover}), + ?_assertEqual(Ref2#geef_reference.name, <<"refs/heads/branch">>), + ?_assertEqual(Res1, {error, iterover})]. + rm_r(Path) -> case filelib:is_dir(Path) of false ->