Merge pull request #90 from libgit2/unchanify

Remove usage of channels for `ForEach`
diff --git a/odb.go b/odb.go
index abfa1cd..7076e20 100644
--- a/odb.go
+++ b/odb.go
@@ -84,30 +84,40 @@
 	return obj, nil
 }
 
+type OdbForEachCallback func(id *Oid) error
+
+type foreachData struct {
+	callback OdbForEachCallback
+	err error
+}
+
 //export odbForEachCb
 func odbForEachCb(id *C.git_oid, payload unsafe.Pointer) int {
-	ch := *(*chan *Oid)(payload)
-	oid := newOidFromC(id)
-	// Because the channel is unbuffered, we never read our own data. If ch is
-	// readable, the user has sent something on it, which means we should
-	// abort.
-	select {
-	case ch <- oid:
-	case <-ch:
-		return -1
+	data := (*foreachData)(payload)
+
+	err := data.callback(newOidFromC(id))
+	if err != nil {
+		data.err = err
+		return C.GIT_EUSER
 	}
+
 	return 0
 }
 
-func (v *Odb) forEachWrap(ch chan *Oid) {
-	C._go_git_odb_foreach(v.ptr, unsafe.Pointer(&ch))
-	close(ch)
-}
+func (v *Odb) ForEach(callback OdbForEachCallback) error {
+	data := foreachData {
+		callback: callback,
+		err: nil,
+	}
 
-func (v *Odb) ForEach() chan *Oid {
-	ch := make(chan *Oid, 0)
-	go v.forEachWrap(ch)
-	return ch
+	ret := C._go_git_odb_foreach(v.ptr, unsafe.Pointer(&data))
+	if ret == C.GIT_EUSER {
+		return data.err
+	} else if ret < 0 {
+		return MakeGitError(ret)
+	}
+
+	return nil
 }
 
 // Hash determines the object-ID (sha1) of a data buffer.
diff --git a/odb_test.go b/odb_test.go
index 17b3ad2..14a3658 100644
--- a/odb_test.go
+++ b/odb_test.go
@@ -3,6 +3,7 @@
 import (
 	"io"
 	"os"
+	"errors"
 	"testing"
 )
 
@@ -48,7 +49,7 @@
 author John Doe <john@doe.com> 1390682018 +0000
 committer John Doe <john@doe.com> 1390682018 +0000
 
-Initial commit.`;
+Initial commit.`
 
 	oid, error := odb.Hash([]byte(str), ObjectCommit)
 	checkFatal(t, error)
@@ -60,3 +61,36 @@
 		t.Fatal("Hash and write Oids are different")
 	}
 }
+
+func TestOdbForeach(t *testing.T) {
+	repo := createTestRepo(t)
+	defer os.RemoveAll(repo.Workdir())
+	_, _ = seedTestRepo(t, repo)
+
+	odb, err := repo.Odb()
+	checkFatal(t, err)
+
+	expect := 3
+	count := 0
+	err = odb.ForEach(func(id *Oid) error {
+		count++
+		return nil
+	})
+
+	checkFatal(t, err)
+	if count != expect {
+		t.Fatalf("Expected %v objects, got %v")
+	}
+
+	expect = 1
+	count = 0
+	to_return := errors.New("not really an error")
+	err = odb.ForEach(func(id *Oid) error {
+		count++
+		return to_return
+	})
+
+	if err != to_return {
+		t.Fatalf("Odb.ForEach() did not return the expected error, got %v", err)
+	}
+}
diff --git a/packbuilder.go b/packbuilder.go
index 70c4530..666f5c4 100644
--- a/packbuilder.go
+++ b/packbuilder.go
@@ -94,55 +94,51 @@
 }
 
 func (pb *Packbuilder) Write(w io.Writer) error {
-	ch, stop := pb.ForEach()
-	for slice := range ch {
+	return pb.ForEach(func(slice []byte) error {
 		_, err := w.Write(slice)
-		if err != nil {
-			close(stop)
-			return err
-		}
-	}
-	return nil
+		return err
+	})
 }
 
 func (pb *Packbuilder) Written() uint32 {
 	return uint32(C.git_packbuilder_written(pb.ptr))
 }
 
+type PackbuilderForeachCallback func([]byte) error
 type packbuilderCbData struct {
-	ch chan<- []byte
-	stop <-chan bool
+	callback PackbuilderForeachCallback
+	err      error
 }
 
 //export packbuilderForEachCb
 func packbuilderForEachCb(buf unsafe.Pointer, size C.size_t, payload unsafe.Pointer) int {
 	data := (*packbuilderCbData)(payload)
-	ch := data.ch
-	stop := data.stop
-
 	slice := C.GoBytes(buf, C.int(size))
-	select {
-	case <- stop:
-		return -1
-	case ch <- slice:
+
+	err := data.callback(slice)
+	if err != nil {
+		data.err = err
+		return C.GIT_EUSER
 	}
 
 	return 0
 }
 
-func (pb *Packbuilder) forEachWrap(data *packbuilderCbData) {
-	C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(data))
-	close(data.ch)
-}
+// ForEach repeatedly calls the callback with new packfile data until
+// there is no more data or the callback returns an error
+func (pb *Packbuilder) ForEach(callback PackbuilderForeachCallback) error {
+	data := packbuilderCbData{
+		callback: callback,
+		err: nil,
+	}
 
-// Foreach sends the packfile as slices through the "data" channel. If
-// you want to stop the pack-building process (e.g. there's an error
-// writing to the output), close or write a value into the "stop"
-// channel.
-func (pb *Packbuilder) ForEach() (<-chan []byte, chan<- bool) {
-	ch := make(chan []byte)
-	stop := make(chan bool)
-	data := packbuilderCbData{ch, stop}
-	go pb.forEachWrap(&data)
-	return ch, stop
+	err := C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(&data))
+	if err == C.GIT_EUSER {
+		return data.err
+	}
+	if err < 0 {
+		return MakeGitError(err)
+	}
+
+	return nil
 }