diff --git a/c_src/geef.c b/c_src/geef.c index 096164c..a270e7a 100644 --- a/c_src/geef.c +++ b/c_src/geef.c @@ -163,6 +163,7 @@ static ErlNifFunc geef_funcs[] = {"commit_tree", 1, geef_commit_tree}, {"commit_tree_id", 1, geef_commit_tree_id}, {"tree_bypath", 2, geef_tree_bypath}, + {"tree_nth", 2, geef_tree_nth}, {"blob_size", 1, geef_blob_size}, {"blob_content", 1, geef_blob_content}, {"tag_peel", 1, geef_tag_peel}, diff --git a/c_src/tree.c b/c_src/tree.c index 0c55f56..3f0f4b6 100644 --- a/c_src/tree.c +++ b/c_src/tree.c @@ -59,3 +59,24 @@ geef_tree_bypath(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) enif_release_binary(&bin); return tree_entry_to_term(env, entry); } + +ERL_NIF_TERM +geef_tree_nth(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + geef_object *obj; + const git_tree_entry *entry; + unsigned int nth; + + if (!enif_get_resource(env, argv[0], geef_object_type, (void **) &obj)) + return enif_make_badarg(env); + + if (!enif_get_uint(env, argv[1], &nth)) + return enif_make_badarg(env); + + entry = git_tree_entry_byindex((git_tree *)obj->obj, nth); + + if (!entry) + return geef_error(env); + + return tree_entry_to_term(env, entry); +} diff --git a/c_src/tree.h b/c_src/tree.h index de553e2..a74418d 100644 --- a/c_src/tree.h +++ b/c_src/tree.h @@ -4,5 +4,6 @@ #include "object.h" ERL_NIF_TERM geef_tree_bypath(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM geef_tree_nth(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); #endif diff --git a/lib/geef/tree.ex b/lib/geef/tree.ex index 728f3b3..9844ac9 100644 --- a/lib/geef/tree.ex +++ b/lib/geef/tree.ex @@ -15,21 +15,26 @@ defrecord Geef.Tree, Record.extract(:geef_object, from: "src/geef_records.hrl") end end - def get(tree, path) do - case :geef_tree.get(rebind(tree), path) do - {:ok, entry} -> - {:ok, TreeEntry.new entry} - error -> - error - end - end + defp maybe_entry({:ok, entry}), do: {:ok, TreeEntry.new entry} + defp maybe_entry(error = {:error, _}), do: error + + def get(tree, path), do: :geef_tree.get(rebind(tree), path) |> maybe_entry + def nth(tree, pos), do: :geef_tree.nth(rebind(tree), pos) |> maybe_entry end defimpl Access, for: Geef.Tree do + alias Geef.Tree + + def access(tree, key) when is_number(key) do + case Tree.nth(tree, key) do + {:ok, entry} -> entry + {:error, _} -> nil + end + end def access(tree, key) do - case Geef.Tree.get(tree, key) do + case Tree.get(tree, key) do {:ok, entry} -> entry {:error, _} -> nil end diff --git a/src/geef_nif.erl b/src/geef_nif.erl index 01ebd35..65155d3 100644 --- a/src/geef_nif.erl +++ b/src/geef_nif.erl @@ -96,6 +96,10 @@ commit_tree(_Handle) -> tree_bypath(_TreeHandle, _Path) -> nif_error(?LINE). +-spec tree_nth(term, non_neg_integer()) -> term(). +tree_nth(_TreeHandle, _Nth) -> + ?NIF_FN. + -spec blob_size(term) -> {ok, integer()} | error. blob_size(_ObjHandle) -> nif_error(?LINE). diff --git a/src/geef_tree.erl b/src/geef_tree.erl index 668bef8..88df9e7 100644 --- a/src/geef_tree.erl +++ b/src/geef_tree.erl @@ -4,19 +4,22 @@ -include_lib("eunit/include/eunit.hrl"). -endif. --export([get/2, lookup/2]). +-export([get/2, nth/2, lookup/2]). -include("geef_records.hrl"). +from_nif({ok, Mode, Type, Oid, Name}) -> + {ok, #geef_tree_entry{mode=Mode, type=Type, id=#geef_oid{oid=Oid}, name=Name}}; +from_nif(Err = {error, _}) -> + Err. + -spec get(geef_object(), iolist()) -> {ok, geef_tree_entry()} | {error, term()}. get(#geef_object{type=tree,handle=Handle}, Path) -> - case geef_nif:tree_bypath(Handle, Path) of - {ok, Mode, Type, Geef_Oid, Name} -> - {ok, #geef_tree_entry{mode=Mode, type=Type, - id=#geef_oid{oid=Geef_Oid}, name=Name}}; - Other -> - Other - end. + from_nif(geef_nif:tree_bypath(Handle, Path)). + +-spec nth(geef_object(), non_neg_integer()) -> {ok, geef_tree_entry()} | {error, term()}. +nth(#geef_object{type=tree,handle=Handle}, Nth) -> + from_nif(geef_nif:tree_nth(Handle, Nth)). -spec lookup(pid(), geef_oid() | iolist()) -> {ok, geef_object()} | {error, term()}. lookup(Repo, Id) -> @@ -26,11 +29,13 @@ lookup(Repo, Id) -> %% This is somewhat hacky, assuming that this is running under .eunit, %% but it's good enough for now -bypath_test() -> +access_test() -> {ok, Repo} = geef_repo:open(".."), {ok, Tree} = geef_obj:lookup(Repo, geef_oid:parse("395e1c39cb203640b78da8458a42afdb92bef7aa")), Id = geef_oid:parse("80d5c15a040c93a4f98f4496a05ebf30cdd58650"), Expected = #geef_tree_entry{mode=8#100644, type=blob, id=Id, name = <<"README.md">>}, - {ok, Actual}= geef_tree:get(Tree, "README.md"), - ?assertEqual(Expected, Actual). + {ok, ActualByPath}= geef_tree:get(Tree, "README.md"), + {ok, ActualNth} = geef_tree:nth(Tree, 2), + ?assertEqual(Expected, ActualByPath), + ?assertEqual(Expected, ActualNth). -endif.