Changeset

12131:b4c0efff8dd3

util.jsonpointer: Resolve JSON Pointers per RFC 6901
author Kim Alvefur <zash@zash.se>
date Wed, 29 Dec 2021 16:52:09 +0100
parents 12130:c4ca226ff386
children 12132:4ff0d33dfb2b
files GNUmakefile teal-src/util/jsonpointer.tl util/jsonpointer.lua
diffstat 3 files changed, 87 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/GNUmakefile	Wed Dec 29 16:51:13 2021 +0100
+++ b/GNUmakefile	Wed Dec 29 16:52:09 2021 +0100
@@ -110,7 +110,7 @@
 	tl -I teal-src/ --gen-compat off --gen-target 5.1 gen $^ -o $@
 	-lua-format -i $@
 
-teal: util/jsonschema.lua util/datamapper.lua
+teal: util/jsonschema.lua util/datamapper.lua util/jsonpointer.lua
 
 util/%.so:
 	$(MAKE) install -C util-src
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/teal-src/util/jsonpointer.tl	Wed Dec 29 16:52:09 2021 +0100
@@ -0,0 +1,46 @@
+
+local enum ptr_error
+	"invalid-table"
+	"invalid-path"
+end
+
+local function unescape_token(escaped_token : string) : string
+	local unescaped = escaped_token:gsub("~1", "/"):gsub("~0", "~")
+	return unescaped
+end
+
+local function resolve_json_pointer(ref : table, path : string) : any, ptr_error
+	local ptr_len = #path+1
+	for part, pos in path:gmatch("/([^/]*)()") do
+		local token = unescape_token(part)
+		if not ref is table then
+			return nil
+		end
+		local idx = next(ref)
+		local new_ref : any
+
+		if idx is string then
+			new_ref = ref[token]
+		elseif idx is integer then
+			local i = tonumber(token)
+			if token == "-" then i = #ref + 1 end
+			new_ref = ref[i]
+		else
+			return nil, "invalid-table"
+		end
+
+		if pos as integer == ptr_len then
+			return new_ref
+		elseif new_ref is table then
+			ref = new_ref
+		elseif not ref is table then
+			return nil, "invalid-path"
+		end
+
+	end
+	return ref
+end
+
+return {
+	resolve = resolve_json_pointer,
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/jsonpointer.lua	Wed Dec 29 16:52:09 2021 +0100
@@ -0,0 +1,40 @@
+local function unescape_token(escaped_token)
+	local unescaped = escaped_token:gsub("~1", "/"):gsub("~0", "~")
+	return unescaped
+end
+
+local function resolve_json_pointer(ref, path)
+	local ptr_len = #path + 1
+	for part, pos in path:gmatch("/([^/]*)()") do
+		local token = unescape_token(part)
+		if not (type(ref) == "table") then
+			return nil
+		end
+		local idx = next(ref)
+		local new_ref
+
+		if type(idx) == "string" then
+			new_ref = ref[token]
+		elseif math.type(idx) == "integer" then
+			local i = tonumber(token)
+			if token == "-" then
+				i = #ref + 1
+			end
+			new_ref = ref[i]
+		else
+			return nil, "invalid-table"
+		end
+
+		if pos == ptr_len then
+			return new_ref
+		elseif type(new_ref) == "table" then
+			ref = new_ref
+		elseif not (type(ref) == "table") then
+			return nil, "invalid-path"
+		end
+
+	end
+	return ref
+end
+
+return { resolve = resolve_json_pointer }