1318663Sdim// Attempts to load a UCL structure from a string
2318663Sdim#include <ucl.h>
3318663Sdim#include <Python.h>
4318663Sdim
5318663Sdimstatic PyObject *SchemaError;
6318663Sdim
7318663Sdimstatic PyObject *
8318663Sdim_basic_ucl_type (ucl_object_t const *obj)
9318663Sdim{
10318663Sdim	switch (obj->type) {
11318663Sdim	case UCL_INT:
12318663Sdim		return Py_BuildValue ("L", (long long)ucl_object_toint (obj));
13318663Sdim	case UCL_FLOAT:
14318663Sdim		return Py_BuildValue ("d", ucl_object_todouble (obj));
15319799Sdim	case UCL_STRING:
16319799Sdim		return Py_BuildValue ("s", ucl_object_tostring (obj));
17327952Sdim	case UCL_BOOLEAN:
18319799Sdim		return PyBool_FromLong (ucl_object_toboolean (obj));
19319799Sdim	case UCL_TIME:
20319799Sdim		return Py_BuildValue ("d", ucl_object_todouble (obj));
21318663Sdim	case UCL_NULL:
22318663Sdim		Py_RETURN_NONE;
23320397Sdim	}
24320397Sdim	return NULL;
25320397Sdim}
26318663Sdim
27318663Sdimstatic PyObject *
28318663Sdim_iterate_valid_ucl (ucl_object_t const *obj)
29319479Sdim{
30319479Sdim	const ucl_object_t *tmp;
31319479Sdim	ucl_object_iter_t it = NULL;
32319479Sdim
33319479Sdim	tmp = obj;
34319479Sdim
35320041Sdim	while ((obj = ucl_object_iterate (tmp, &it, false))) {
36320041Sdim		PyObject *val;
37320041Sdim
38320041Sdim		val = _basic_ucl_type(obj);
39319799Sdim		if (!val) {
40319799Sdim			PyObject *key = NULL;
41319799Sdim
42318663Sdim			if (obj->key != NULL) {
43318663Sdim				key = Py_BuildValue("s", ucl_object_key(obj));
44320397Sdim			}
45318663Sdim
46318663Sdim			if (obj->type == UCL_OBJECT) {
47318663Sdim				const ucl_object_t *cur;
48318663Sdim				ucl_object_iter_t it_obj = NULL;
49318663Sdim
50318663Sdim				val = PyDict_New();
51320397Sdim
52318663Sdim				while ((cur = ucl_object_iterate (obj, &it_obj, true))) {
53318663Sdim					PyObject *keyobj = Py_BuildValue("s",ucl_object_key(cur));
54318663Sdim					PyDict_SetItem(val, keyobj, _iterate_valid_ucl(cur));
55318663Sdim				}
56318663Sdim			} else if (obj->type == UCL_ARRAY) {
57318663Sdim				const ucl_object_t *cur;
58318663Sdim				ucl_object_iter_t it_obj = NULL;
59318663Sdim
60327952Sdim				val = PyList_New(0);
61327952Sdim
62327952Sdim				while ((cur = ucl_object_iterate (obj, &it_obj, true))) {
63327952Sdim					PyList_Append(val, _iterate_valid_ucl(cur));
64318663Sdim				}
65318663Sdim			} else if (obj->type == UCL_USERDATA) {
66318663Sdim				// XXX: this should be
67327952Sdim				// PyBytes_FromStringAndSize; where is the
68327952Sdim				// length from?
69327952Sdim				val = PyBytes_FromString(obj->value.ud);
70327952Sdim			}
71327952Sdim		}
72327952Sdim		return val;
73327952Sdim	}
74327952Sdim
75327952Sdim	PyErr_SetString(PyExc_SystemError, "unhandled type");
76318663Sdim	return NULL;
77318663Sdim}
78318663Sdim
79318663Sdimstatic PyObject *
80318663Sdim_internal_load_ucl (char *uclstr)
81318663Sdim{
82318663Sdim	PyObject *ret;
83318663Sdim	struct ucl_parser *parser =
84318663Sdim	    ucl_parser_new (UCL_PARSER_NO_TIME|UCL_PARSER_NO_IMPLICIT_ARRAYS);
85318663Sdim	bool r = ucl_parser_add_string(parser, uclstr, 0);
86318663Sdim
87318663Sdim	if (r) {
88318663Sdim		if (ucl_parser_get_error (parser)) {
89319479Sdim			PyErr_SetString(PyExc_ValueError, ucl_parser_get_error(parser));
90319479Sdim			ucl_parser_free(parser);
91319479Sdim			ret = NULL;
92319479Sdim			goto return_with_parser;
93319479Sdim		} else {
94319479Sdim			ucl_object_t *uclobj = ucl_parser_get_object(parser);
95319479Sdim			ret = _iterate_valid_ucl(uclobj);
96319479Sdim			ucl_object_unref(uclobj);
97319479Sdim			goto return_with_parser;
98319479Sdim		}
99319479Sdim	}
100319479Sdim	else {
101319479Sdim		PyErr_SetString(PyExc_ValueError, ucl_parser_get_error (parser));
102319479Sdim		ret = NULL;
103319479Sdim		goto return_with_parser;
104319479Sdim	}
105319479Sdim
106318663Sdimreturn_with_parser:
107320397Sdim	ucl_parser_free(parser);
108320397Sdim	return ret;
109319479Sdim}
110320397Sdim
111319479Sdimstatic PyObject*
112319479Sdimucl_load (PyObject *self, PyObject *args)
113319479Sdim{
114319479Sdim	char *uclstr;
115319479Sdim
116319479Sdim	if (PyArg_ParseTuple(args, "z", &uclstr)) {
117319479Sdim		if (!uclstr) {
118320397Sdim			Py_RETURN_NONE;
119319479Sdim		}
120319479Sdim
121319479Sdim		return _internal_load_ucl(uclstr);
122320397Sdim	}
123319479Sdim
124320397Sdim	return NULL;
125319479Sdim}
126318663Sdim
127318663Sdimstatic ucl_object_t *
128318663Sdim_iterate_python (PyObject *obj)
129319799Sdim{
130319479Sdim	if (obj == Py_None) {
131319479Sdim		return ucl_object_new();
132319479Sdim	}
133327952Sdim	else if (PyBool_Check (obj)) {
134327952Sdim		return ucl_object_frombool (obj == Py_True);
135327952Sdim	}
136327952Sdim#if PY_MAJOR_VERSION < 3
137327952Sdim	else if (PyInt_Check (obj)) {
138327952Sdim		return ucl_object_fromint (PyInt_AsLong (obj));
139327952Sdim	}
140327952Sdim#endif
141327952Sdim	else if (PyLong_Check (obj)) {
142327952Sdim		return ucl_object_fromint (PyLong_AsLong (obj));
143327952Sdim	}
144327952Sdim	else if (PyFloat_Check (obj)) {
145327952Sdim		return ucl_object_fromdouble (PyFloat_AsDouble (obj));
146327952Sdim	}
147319479Sdim	else if (PyUnicode_Check (obj)) {
148319479Sdim		ucl_object_t *ucl_str;
149319479Sdim		PyObject *str = PyUnicode_AsASCIIString(obj);
150319479Sdim		ucl_str = ucl_object_fromstring (PyBytes_AsString (str));
151319799Sdim		Py_DECREF(str);
152319799Sdim		return ucl_str;
153320041Sdim	}
154320041Sdim#if PY_MAJOR_VERSION < 3
155320041Sdim	else if (PyString_Check (obj)) {
156320041Sdim		return ucl_object_fromstring (PyString_AsString (obj));
157320041Sdim	}
158320041Sdim#endif
159319799Sdim	else if (PyDict_Check(obj)) {
160319799Sdim		PyObject *key, *value;
161320041Sdim		Py_ssize_t pos = 0;
162319799Sdim		ucl_object_t *top, *elm;
163319799Sdim		char *keystr = NULL;
164319479Sdim
165319479Sdim		top = ucl_object_typed_new (UCL_OBJECT);
166319479Sdim
167319479Sdim		while (PyDict_Next(obj, &pos, &key, &value)) {
168319479Sdim			elm = _iterate_python(value);
169319479Sdim
170320041Sdim			if (PyUnicode_Check(key)) {
171320041Sdim				PyObject *keyascii = PyUnicode_AsASCIIString(key);
172319479Sdim				keystr = PyBytes_AsString(keyascii);
173319479Sdim				Py_DECREF(keyascii);
174319479Sdim			}
175320041Sdim#if PY_MAJOR_VERSION < 3
176320041Sdim			else if (PyString_Check(key)) {
177320041Sdim				keystr = PyString_AsString(key);
178320041Sdim			}
179320041Sdim#endif
180319479Sdim			else {
181319479Sdim				PyErr_SetString(PyExc_TypeError, "Unknown key type");
182319479Sdim				return NULL;
183319799Sdim			}
184319799Sdim
185319799Sdim			ucl_object_insert_key (top, elm, keystr, 0, true);
186319799Sdim		}
187319479Sdim
188319799Sdim		return top;
189319799Sdim	}
190319799Sdim	else if (PySequence_Check(obj)) {
191319799Sdim		PyObject *value;
192319799Sdim		Py_ssize_t len, pos;
193319799Sdim		ucl_object_t *top, *elm;
194319799Sdim
195319799Sdim		len  = PySequence_Length(obj);
196319799Sdim		top = ucl_object_typed_new (UCL_ARRAY);
197319799Sdim
198319799Sdim		for (pos = 0; pos < len; pos++) {
199319799Sdim			value = PySequence_GetItem(obj, pos);
200319799Sdim			elm = _iterate_python(value);
201319799Sdim			ucl_array_append(top, elm);
202319799Sdim		}
203319799Sdim
204319799Sdim		return top;
205319799Sdim	}
206319799Sdim	else {
207319799Sdim		PyErr_SetString(PyExc_TypeError, "Unhandled object type");
208319799Sdim		return NULL;
209319799Sdim	}
210319799Sdim
211319799Sdim	return NULL;
212319799Sdim}
213319799Sdim
214319479Sdimstatic PyObject *
215320041Sdimucl_dump (PyObject *self, PyObject *args)
216320041Sdim{
217319479Sdim	PyObject *obj;
218320041Sdim	ucl_emitter_t emitter;
219319479Sdim	ucl_object_t *root = NULL;
220319479Sdim
221319479Sdim	emitter = UCL_EMIT_CONFIG;
222319479Sdim
223319479Sdim	if (!PyArg_ParseTuple(args, "O|i", &obj, &emitter)) {
224320041Sdim		PyErr_SetString(PyExc_TypeError, "Unhandled object type");
225320041Sdim		return NULL;
226319479Sdim	}
227320041Sdim
228319479Sdim	if (emitter >= UCL_EMIT_MAX) {
229319479Sdim		PyErr_SetString(PyExc_TypeError, "Invalid emitter type");
230319479Sdim		return NULL;
231319479Sdim	}
232319479Sdim
233319479Sdim	if (obj == Py_None) {
234319479Sdim		Py_RETURN_NONE;
235319799Sdim	}
236319799Sdim
237319479Sdim	root = _iterate_python(obj);
238319479Sdim	if (root) {
239319799Sdim		PyObject *ret;
240319799Sdim		char *buf;
241319799Sdim
242319479Sdim		buf = (char *) ucl_object_emit (root, emitter);
243319479Sdim		ucl_object_unref (root);
244319799Sdim#if PY_MAJOR_VERSION < 3
245319799Sdim		ret = PyString_FromString (buf);
246319799Sdim#else
247319479Sdim		ret = PyUnicode_FromString (buf);
248319479Sdim#endif
249319479Sdim		free(buf);
250319479Sdim
251319479Sdim		return ret;
252319479Sdim	}
253319479Sdim
254319479Sdim	return NULL;
255320041Sdim}
256320041Sdim
257319479Sdimstatic PyObject *
258319479Sdimucl_validate (PyObject *self, PyObject *args)
259319479Sdim{
260320397Sdim	PyObject *dataobj, *schemaobj;
261319479Sdim	ucl_object_t *data, *schema;
262344779Sdim	bool r;
263319479Sdim	struct ucl_schema_error err;
264319479Sdim
265319479Sdim	if (!PyArg_ParseTuple (args, "OO", &schemaobj, &dataobj)) {
266319479Sdim		PyErr_SetString (PyExc_TypeError, "Unhandled object type");
267320397Sdim		return NULL;
268319479Sdim	}
269319479Sdim
270319479Sdim	schema = _iterate_python(schemaobj);
271319799Sdim	if (!schema)
272320041Sdim		return NULL;
273319479Sdim
274319479Sdim	data = _iterate_python(dataobj);
275319479Sdim	if (!data)
276319479Sdim		return NULL;
277319479Sdim
278319479Sdim	// validation
279319479Sdim	r = ucl_object_validate (schema, data, &err);
280319479Sdim	ucl_object_unref (schema);
281319479Sdim	ucl_object_unref (data);
282319479Sdim
283319479Sdim	if (!r) {
284319479Sdim		PyErr_SetString (SchemaError, err.msg);
285319479Sdim		return NULL;
286319479Sdim	}
287319479Sdim
288319479Sdim	Py_RETURN_TRUE;
289319479Sdim}
290319479Sdim
291319799Sdimstatic PyMethodDef uclMethods[] = {
292319799Sdim	{"load", ucl_load, METH_VARARGS, "Load UCL from stream"},
293319799Sdim	{"dump", ucl_dump, METH_VARARGS, "Dump UCL to stream"},
294319799Sdim	{"validate", ucl_validate, METH_VARARGS, "Validate ucl stream against schema"},
295319799Sdim	{NULL, NULL, 0, NULL}
296320397Sdim};
297319799Sdim
298319799Sdimstatic void
299319799Sdiminit_macros(PyObject *mod)
300320397Sdim{
301319799Sdim	PyModule_AddIntMacro(mod, UCL_EMIT_JSON);
302319799Sdim	PyModule_AddIntMacro(mod, UCL_EMIT_JSON_COMPACT);
303319799Sdim	PyModule_AddIntMacro(mod, UCL_EMIT_CONFIG);
304319799Sdim	PyModule_AddIntMacro(mod, UCL_EMIT_YAML);
305319799Sdim	PyModule_AddIntMacro(mod, UCL_EMIT_MSGPACK);
306320397Sdim
307319799Sdim	SchemaError = PyErr_NewException("ucl.SchemaError", NULL, NULL);
308319799Sdim	Py_INCREF(SchemaError);
309319799Sdim	PyModule_AddObject(mod, "SchemaError", SchemaError);
310319799Sdim}
311319799Sdim
312319799Sdim#if PY_MAJOR_VERSION >= 3
313319799Sdimstatic struct PyModuleDef uclmodule = {
314319799Sdim	PyModuleDef_HEAD_INIT,
315319799Sdim	"ucl",
316319799Sdim	NULL,
317319799Sdim	-1,
318319799Sdim	uclMethods
319320397Sdim};
320319799Sdim
321320397SdimPyMODINIT_FUNC
322319799SdimPyInit_ucl (void)
323319799Sdim{
324319799Sdim	PyObject *mod = PyModule_Create (&uclmodule);
325319799Sdim	init_macros (mod);
326319799Sdim
327319799Sdim	return mod;
328319799Sdim}
329319799Sdim#else
330319799Sdimvoid initucl (void)
331319799Sdim{
332319799Sdim	PyObject *mod = Py_InitModule ("ucl", uclMethods);
333319799Sdim	init_macros (mod);
334319799Sdim}
335319799Sdim#endif
336319799Sdim