From 35acb29ff856a4bb3e992bb74cac53e0accc656a Mon Sep 17 00:00:00 2001 From: Kohei Suzuki Date: Tue, 24 Nov 2020 20:10:40 +0900 Subject: [PATCH] Add format APIs to C bindings --- c-bindings-tests/compat_test.py | 121 ++++++++++++++++++++++++++++++++ c-bindings/c-bindings.go | 104 ++++++++++++++++++++++++++- 2 files changed, 223 insertions(+), 2 deletions(-) diff --git a/c-bindings-tests/compat_test.py b/c-bindings-tests/compat_test.py index 708a55d..6c54155 100755 --- a/c-bindings-tests/compat_test.py +++ b/c-bindings-tests/compat_test.py @@ -209,6 +209,64 @@ lib.jsonnet_json_destroy.argtypes = [ ] lib.jsonnet_json_destroy.restype = None +# fmt declaration + +lib.jsonnet_fmt_snippet.argtypes = [ + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.POINTER(ctypes.c_int), +] +lib.jsonnet_fmt_snippet.restype = ctypes.POINTER(ctypes.c_char) + +lib.jsonnet_fmt_indent.argtypes = [ + ctypes.c_void_p, + ctypes.c_int, +] +lib.jsonnet_fmt_indent.restype = None + +lib.jsonnet_fmt_max_blank_lines.argtypes = [ + ctypes.c_void_p, + ctypes.c_int, +] +lib.jsonnet_fmt_max_blank_lines.restype = None + +lib.jsonnet_fmt_string.argtypes = [ + ctypes.c_void_p, + ctypes.c_int, +] +lib.jsonnet_fmt_string.restype = None + +lib.jsonnet_fmt_comment.argtypes = [ + ctypes.c_void_p, + ctypes.c_int, +] +lib.jsonnet_fmt_comment.restype = None + +lib.jsonnet_fmt_pad_arrays.argtypes = [ + ctypes.c_void_p, + ctypes.c_int, +] +lib.jsonnet_fmt_pad_arrays.restype = None + +lib.jsonnet_fmt_pad_objects.argtypes = [ + ctypes.c_void_p, + ctypes.c_int, +] +lib.jsonnet_fmt_pad_objects.restype = None + +lib.jsonnet_fmt_pretty_field_names.argtypes = [ + ctypes.c_void_p, + ctypes.c_int, +] +lib.jsonnet_fmt_pretty_field_names.restype = None + +lib.jsonnet_fmt_sort_imports.argtypes = [ + ctypes.c_void_p, + ctypes.c_int, +] +lib.jsonnet_fmt_sort_imports.restype = None + # utils def free_buffer(vm, buf): @@ -500,5 +558,68 @@ class TestJsonnetJsonValueBindings(unittest.TestCase): def tearDown(self): lib.jsonnet_destroy(self.vm) +class TestJsonnetFormatBindings(unittest.TestCase): + def setUp(self): + self.err = ctypes.c_int() + self.err_ref = ctypes.byref(self.err) + self.vm = lib.jsonnet_make() + + def test_format(self): + res = lib.jsonnet_fmt_snippet(self.vm, b"fmt", b"local a = import 'z.libsonnet';\nlocal b = import 'y.libsonnet';\n{'n':1, s: \"y\", a: [1,2]}", self.err_ref) + self.assertEqual(b"local b = import 'y.libsonnet';\nlocal a = import 'z.libsonnet';\n{ n: 1, s: 'y', a: [1, 2] }\n", to_bytes(res)) + free_buffer(self.vm, res) + + def test_indent(self): + lib.jsonnet_fmt_indent(self.vm, 8) + res = lib.jsonnet_fmt_snippet(self.vm, b"fmt", b"{\nx:1,\ny:2\n}", self.err_ref) + self.assertEqual(b"{\n x: 1,\n y: 2,\n}\n", to_bytes(res)) + free_buffer(self.vm, res) + + def test_max_blank_lines(self): + lib.jsonnet_fmt_max_blank_lines(self.vm, 2) + res = lib.jsonnet_fmt_snippet(self.vm, b"fmt", b"{\nx:1,\n\n\n\n\ny:2\n}", self.err_ref) + self.assertEqual(b"{\n x: 1,\n\n\n y: 2,\n}\n", to_bytes(res)) + free_buffer(self.vm, res) + + def test_string(self): + lib.jsonnet_fmt_string(self.vm, ord('d')) + res = lib.jsonnet_fmt_snippet(self.vm, b"fmt", b"{x:'x'}", self.err_ref) + self.assertEqual(b"{ x: \"x\" }\n", to_bytes(res)) + free_buffer(self.vm, res) + + def test_comment(self): + lib.jsonnet_fmt_comment(self.vm, ord('h')) + res = lib.jsonnet_fmt_snippet(self.vm, b"fmt", b"// comment\n{}", self.err_ref) + self.assertEqual(b"# comment\n{}\n", to_bytes(res)) + free_buffer(self.vm, res) + + def test_pad_arrays(self): + lib.jsonnet_fmt_pad_arrays(self.vm, 1) + res = lib.jsonnet_fmt_snippet(self.vm, b"fmt", b"{x:[1,2,3]}", self.err_ref) + self.assertEqual(b"{ x: [ 1, 2, 3 ] }\n", to_bytes(res)) + free_buffer(self.vm, res) + + def test_pad_objects(self): + lib.jsonnet_fmt_pad_objects(self.vm, 0) + res = lib.jsonnet_fmt_snippet(self.vm, b"fmt", b"{ x: 1 }", self.err_ref) + self.assertEqual(b"{x: 1}\n", to_bytes(res)) + free_buffer(self.vm, res) + + def test_pretty_field_names(self): + lib.jsonnet_fmt_pretty_field_names(self.vm, 0) + res = lib.jsonnet_fmt_snippet(self.vm, b"fmt", b"{ 'x': 1 }", self.err_ref) + self.assertEqual(b"{ 'x': 1 }\n", to_bytes(res)) + free_buffer(self.vm, res) + + def test_sort_imports(self): + lib.jsonnet_fmt_sort_imports(self.vm, 0) + res = lib.jsonnet_fmt_snippet(self.vm, b"fmt", b"local a = import 'z.libsonnet';\nlocal b = import 'y.libsonnet';\na+b", self.err_ref) + self.assertEqual(b"local a = import 'z.libsonnet';\nlocal b = import 'y.libsonnet';\na + b\n", to_bytes(res)) + free_buffer(self.vm, res) + + + def tearDown(self): + lib.jsonnet_destroy(self.vm) + if __name__ == '__main__': unittest.main() diff --git a/c-bindings/c-bindings.go b/c-bindings/c-bindings.go index 501bac5..2712bde 100644 --- a/c-bindings/c-bindings.go +++ b/c-bindings/c-bindings.go @@ -9,6 +9,7 @@ import ( "github.com/google/go-jsonnet" "github.com/google/go-jsonnet/ast" + "github.com/google/go-jsonnet/formatter" // #cgo CXXFLAGS: -std=c++11 -Wall -I../cpp-jsonnet/include // #include "internal.h" @@ -22,7 +23,8 @@ import ( type vm struct { *jsonnet.VM - importer *jsonnet.FileImporter + importer *jsonnet.FileImporter + formatOptions formatter.Options } type jsonValue struct { @@ -100,7 +102,7 @@ func jsonnet_version() *C.char { //export jsonnet_make func jsonnet_make() *C.struct_JsonnetVm { - newVM := &vm{jsonnet.MakeVM(), &jsonnet.FileImporter{}} + newVM := &vm{jsonnet.MakeVM(), &jsonnet.FileImporter{}, formatter.DefaultOptions()} newVM.Importer(newVM.importer) id, err := handles.make(newVM) @@ -539,5 +541,103 @@ func getJSONValue(jsonRef *C.struct_JsonnetJsonValue) *jsonValue { return v } +//export jsonnet_fmt_indent +func jsonnet_fmt_indent(vmRef *C.struct_JsonnetVm, n C.int) { + vm := getVM(vmRef) + vm.formatOptions.Indent = int(n) +} + +//export jsonnet_fmt_max_blank_lines +func jsonnet_fmt_max_blank_lines(vmRef *C.struct_JsonnetVm, n C.int) { + vm := getVM(vmRef) + vm.formatOptions.MaxBlankLines = int(n) +} + +//export jsonnet_fmt_string +func jsonnet_fmt_string(vmRef *C.struct_JsonnetVm, c C.int) { + vm := getVM(vmRef) + switch c { + case 'd': + vm.formatOptions.StringStyle = formatter.StringStyleDouble + case 's': + vm.formatOptions.StringStyle = formatter.StringStyleSingle + case 'l': + vm.formatOptions.StringStyle = formatter.StringStyleLeave + default: + vm.formatOptions.StringStyle = formatter.StringStyleLeave + } +} + +//export jsonnet_fmt_comment +func jsonnet_fmt_comment(vmRef *C.struct_JsonnetVm, c C.int) { + vm := getVM(vmRef) + switch c { + case 'h': + vm.formatOptions.CommentStyle = formatter.CommentStyleHash + case 's': + vm.formatOptions.CommentStyle = formatter.CommentStyleSlash + case 'l': + vm.formatOptions.CommentStyle = formatter.CommentStyleLeave + default: + vm.formatOptions.CommentStyle = formatter.CommentStyleLeave + } +} + +//export jsonnet_fmt_pad_arrays +func jsonnet_fmt_pad_arrays(vmRef *C.struct_JsonnetVm, v C.int) { + vm := getVM(vmRef) + vm.formatOptions.PadArrays = v != 0 +} + +//export jsonnet_fmt_pad_objects +func jsonnet_fmt_pad_objects(vmRef *C.struct_JsonnetVm, v C.int) { + vm := getVM(vmRef) + vm.formatOptions.PadObjects = v != 0 +} + +//export jsonnet_fmt_pretty_field_names +func jsonnet_fmt_pretty_field_names(vmRef *C.struct_JsonnetVm, v C.int) { + vm := getVM(vmRef) + vm.formatOptions.PrettyFieldNames = v != 0 +} + +//export jsonnet_fmt_sort_imports +func jsonnet_fmt_sort_imports(vmRef *C.struct_JsonnetVm, v C.int) { + vm := getVM(vmRef) + vm.formatOptions.SortImports = v != 0 +} + +//export jsonnet_fmt_snippet +func jsonnet_fmt_snippet(vmRef *C.struct_JsonnetVm, filename *C.char, code *C.char, e *C.int) *C.char { + f := C.GoString(filename) + s := C.GoString(code) + return formatSnippet(vmRef, f, s, e) +} + +//export jsonnet_fmt_file +func jsonnet_fmt_file(vmRef *C.struct_JsonnetVm, filename *C.char, e *C.int) *C.char { + f := C.GoString(filename) + data, err := ioutil.ReadFile(f) + if err != nil { + *e = 1 + return C.CString(fmt.Sprintf("Failed to read input file: %s: %s", f, err.Error())) + } + return formatSnippet(vmRef, f, string(data), e) +} + +func formatSnippet(vmRef *C.struct_JsonnetVm, filename string, code string, e *C.int) *C.char { + vm := getVM(vmRef) + out, err := formatter.Format(filename, code, vm.formatOptions) + var result *C.char + if err != nil { + *e = 1 + result = C.CString(err.Error()) + } else { + *e = 0 + result = C.CString(out) + } + return result +} + func main() { }