From 5d8eaf7e65c404a0d10d3705697dd99369630dda Mon Sep 17 00:00:00 2001 From: lhchavez Date: Sat, 5 Dec 2020 13:13:59 -0800 Subject: Refactor all callbacks (#700) This change is a preparation for another change that makes all callback types return a Go error instead of an error code / an integer. That is going to make make things a lot more idiomatic. The reason this change is split is threefold: a) This change is mostly mechanical and should contain no semantic changes. b) This change is backwards-compatible (in the Go API compatibility sense of the word), and thus can be backported to all other releases. c) It makes the other change a bit smaller and more focused on just one thing. Concretely, this change makes all callbacks populate a Go error when they fail. If the callback is invoked from the same stack as the function to which it was passed (e.g. for `Tree.Walk`), it will preserve the error object directly into a struct that also holds the callback function. Otherwise if the callback is pased to one func and will be invoked when run from another one (e.g. for `Repository.InitRebase`), the error string is saved into the libgit2 thread-local storage and then re-created as a `GitError`. --- tree.go | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) (limited to 'tree.go') diff --git a/tree.go b/tree.go index cf85f2e..38c5bdc 100644 --- a/tree.go +++ b/tree.go @@ -8,6 +8,7 @@ extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr); import "C" import ( + "errors" "runtime" "unsafe" ) @@ -120,33 +121,46 @@ func (t *Tree) EntryCount() uint64 { } type TreeWalkCallback func(string, *TreeEntry) int +type treeWalkCallbackData struct { + callback TreeWalkCallback + errorTarget *error +} //export treeWalkCallback func treeWalkCallback(_root *C.char, entry *C.git_tree_entry, ptr unsafe.Pointer) C.int { - root := C.GoString(_root) - - if callback, ok := pointerHandles.Get(ptr).(TreeWalkCallback); ok { - return C.int(callback(root, newTreeEntry(entry))) - } else { + data, ok := pointerHandles.Get(ptr).(*treeWalkCallbackData) + if !ok { panic("invalid treewalk callback") } + + ret := data.callback(C.GoString(_root), newTreeEntry(entry)) + if ret < 0 { + *data.errorTarget = errors.New(ErrorCode(ret).String()) + return C.int(ErrorCodeUser) + } + + return C.int(ErrorCodeOK) } func (t *Tree) Walk(callback TreeWalkCallback) error { + var err error + data := treeWalkCallbackData{ + callback: callback, + errorTarget: &err, + } runtime.LockOSThread() defer runtime.UnlockOSThread() - ptr := pointerHandles.Track(callback) - defer pointerHandles.Untrack(ptr) + handle := pointerHandles.Track(&data) + defer pointerHandles.Untrack(handle) - err := C._go_git_treewalk( - t.cast_ptr, - C.GIT_TREEWALK_PRE, - ptr, - ) + ret := C._go_git_treewalk(t.cast_ptr, C.GIT_TREEWALK_PRE, handle) runtime.KeepAlive(t) - if err < 0 { - return MakeGitError(err) + if ret == C.int(ErrorCodeUser) && err != nil { + return err + } + if ret < 0 { + return MakeGitError(ret) } return nil -- cgit v1.2.3