From 1acd488c68b9f4b419aee2e177875e4b705f2375 Mon Sep 17 00:00:00 2001
From: Martin Lenders <mlenders@elegosoft.com>
Date: Fri, 8 Jun 2012 17:13:48 +0200
Subject: [PATCH] Wrap git_config_foreach in Config

---
 include/pygit2/config.h |  1 +
 src/pygit2/config.c     | 56 +++++++++++++++++++++++++++++++++++++++++
 test/test_config.py     | 10 ++++++++
 3 files changed, 67 insertions(+)

diff --git a/include/pygit2/config.h b/include/pygit2/config.h
index 9d6017f..3ae9d4b 100644
--- a/include/pygit2/config.h
+++ b/include/pygit2/config.h
@@ -9,6 +9,7 @@ PyObject* Config_get_global_config(void);
 PyObject* Config_get_system_config(void);
 PyObject* Config_add_file(Config *self, PyObject *args);
 PyObject* Config_getitem(Config *self, PyObject *key);
+PyObject* Config_foreach(Config *self, PyObject *args);
 int Config_setitem(Config *self, PyObject *key, PyObject *value);
 
 #endif
diff --git a/src/pygit2/config.c b/src/pygit2/config.c
index b18977a..ab18962 100644
--- a/src/pygit2/config.c
+++ b/src/pygit2/config.c
@@ -186,6 +186,56 @@ Config_setitem(Config *self, PyObject *py_key, PyObject *py_value)
     return 0;
 }
 
+int
+Config_foreach_callback_wrapper(const char *c_name, const char *c_value,
+        void *c_payload)
+{
+    PyObject *args = (PyObject *)c_payload;
+    PyObject *py_callback = NULL;
+    PyObject *py_payload = NULL;
+    PyObject *py_result = NULL;
+    int c_result;
+
+    if (!PyArg_ParseTuple(args, "O|O", &py_callback, &py_payload))
+        return 0;
+
+    if (py_payload)
+        args = Py_BuildValue("ssO", c_name, c_value, py_payload);
+    else
+        args = Py_BuildValue("ss", c_name, c_value);
+
+    if (!(py_result = PyObject_CallObject(py_callback,args)))
+        return 0;
+
+    if (!(c_result = PyLong_AsLong(py_result)))
+        return 0;
+
+    return c_result;
+}
+
+PyObject *
+Config_foreach(Config *self, PyObject *args)
+{
+    int ret;
+    PyObject *py_callback;
+    PyObject *py_payload;
+
+
+    if (!PyArg_ParseTuple(args, "O|O", &py_callback, &py_payload))
+        return NULL;
+
+
+    if (!PyCallable_Check(py_callback)) {
+        PyErr_SetString(PyExc_TypeError,"Argument 'callback' is not callable");
+        return NULL;
+    }
+
+    ret = git_config_foreach(self->config, Config_foreach_callback_wrapper,
+            (void *)args);
+
+    return PyInt_FromLong((long)ret);
+}
+
 PyObject *
 Config_add_file(Config *self, PyObject *args)
 {
@@ -212,6 +262,12 @@ PyMethodDef Config_methods[] = {
     {"get_global_config", (PyCFunction)Config_get_global_config,
      METH_NOARGS | METH_STATIC,
      "Return an object representing the global configuration file."},
+    {"foreach", (PyCFunction)Config_foreach, METH_VARARGS,
+     "Perform an operation on each config variable.\n\n"
+     "The callback must be of type Callable and receives the normalized name "
+     "and value of each variable in the config backend, and an optional "
+     "payload passed to this method. As soon as one of the callbacks returns "
+     "an integer other than 0, this function returns that value."},
     {"add_file", (PyCFunction)Config_add_file, METH_VARARGS,
      "Add a config file instance to an existing config."},
     {NULL}
diff --git a/test/test_config.py b/test/test_config.py
index b9e6aae..328217e 100644
--- a/test/test_config.py
+++ b/test/test_config.py
@@ -38,6 +38,10 @@ __author__ = 'mlenders@elegosoft.com (M. Lenders)'
 
 config_filename = "test_config"
 
+def foreach_test_wrapper(key, name, lst):
+    lst[key] = name
+    return 1
+
 class ConfigTest(utils.RepoTestCase):
 
     def test_config(self):
@@ -135,6 +139,12 @@ class ConfigTest(utils.RepoTestCase):
         del config['core.dummy3']
         self.assertFalse('core.dummy3' in config)
 
+    def test_foreach(self):
+        config = self.repo.config
+        lst = {}
+        config.foreach(foreach_test_wrapper, lst)
+        self.assertTrue('core.bare' in lst)
+        self.assertTrue(lst['core.bare'])
 
 if __name__ == '__main__':
     unittest.main()