util.datamapper: Add support for mapping of elements where only one attribute matters
authorKim Alvefur <zash@zash.se>
Sun, 07 Mar 2021 12:48:49 +0100
changeset 11443 9abcdfdcdb01
parent 11442 b7807583de34
child 11444 d5288c99bb5a
util.datamapper: Add support for mapping of elements where only one attribute matters E.g. <feature var='foo'/> in XEP-0030 and some other simple specifications.
spec/util_datamapper_spec.lua
teal-src/util/datamapper.tl
teal-src/util/jsonschema.tl
util/datamapper.lua
--- a/spec/util_datamapper_spec.lua	Sun Mar 07 01:41:39 2021 +0100
+++ b/spec/util_datamapper_spec.lua	Sun Mar 07 12:48:49 2021 +0100
@@ -35,6 +35,10 @@
 					type = "boolean";
 					xml = {x_name_is_value = true; name = "fallback"; namespace = "urn:xmpp:fallback:0"};
 				};
+				origin_id = {
+					type = "string";
+					xml = {name = "origin-id"; namespace = "urn:xmpp:sid:0"; x_single_attribute = "id"};
+				};
 			};
 		};
 
@@ -44,6 +48,7 @@
 				<delay xmlns='urn:xmpp:delay' from='test' stamp='2021-03-07T15:59:08+00:00'>Becasue</delay>
 				<active xmlns='http://jabber.org/protocol/chatstates'/>
 				<fallback xmlns='urn:xmpp:fallback:0'/>
+				<origin-id xmlns='urn:xmpp:sid:0' id='qgkmMdPB'/>
 				</message>
 				]];
 
@@ -57,6 +62,7 @@
 			delay = {from = "test"; stamp = "2021-03-07T15:59:08+00:00"; reason = "Becasue"};
 			state = "active";
 			fallback = true;
+			origin_id = "qgkmMdPB";
 		};
 	end);
 
@@ -75,6 +81,7 @@
 			assert.equal(x:get_child_text("body"), u:get_child_text("body"));
 			assert.equal(x:get_child_text("delay", "urn:xmpp:delay"), u:get_child_text("delay", "urn:xmpp:delay"));
 			assert.same(x:get_child("delay", "urn:xmpp:delay").attr, u:get_child("delay", "urn:xmpp:delay").attr);
+			assert.same(x:get_child("origin-id", "urn:xmpp:sid:0").attr, u:get_child("origin-id", "urn:xmpp:sid:0").attr);
 		end);
 	end);
 end)
--- a/teal-src/util/datamapper.tl	Sun Mar 07 01:41:39 2021 +0100
+++ b/teal-src/util/datamapper.tl	Sun Mar 07 12:48:49 2021 +0100
@@ -20,6 +20,7 @@
 			local is_attribute = false
 			local is_text = false
 			local name_is_value = false;
+			local single_attribute : string
 
 			local proptype : js.schema_t.type_e
 			if propschema is js.schema_t then
@@ -44,6 +45,8 @@
 					is_text = true
 				elseif propschema.xml.x_name_is_value then
 					name_is_value = true
+				elseif propschema.xml.x_single_attribute then
+					single_attribute = propschema.xml.x_single_attribute
 				end
 			end
 
@@ -78,6 +81,16 @@
 					out[prop] = tonumber(s:get_text())
 				end
 
+			elseif single_attribute then
+				local c = s:get_child(name, namespace)
+				local a = c and c.attr[single_attribute]
+				if proptype == "string" then
+					out[prop] = a
+				elseif proptype == "integer" or proptype == "number" then
+					out[prop] = tonumber(a)
+				elseif proptype == "boolean" then
+					out[prop] = toboolean(a)
+				end
 			else
 
 				if proptype == "string" then
@@ -136,6 +149,7 @@
 				local is_attribute = false
 				local is_text = false
 				local name_is_value = false;
+				local single_attribute : string
 
 				if propschema is js.schema_t and propschema.xml then
 
@@ -156,6 +170,8 @@
 						is_text = true
 					elseif propschema.xml.x_name_is_value then
 						name_is_value = true
+					elseif propschema.xml.x_single_attribute then
+						single_attribute = propschema.xml.x_single_attribute
 					end
 				end
 
@@ -180,6 +196,24 @@
 					if v is string then
 						out:text(v)
 					end
+				elseif single_attribute then
+					local propattr : { string : string } = {}
+
+					if namespace ~= current_ns then
+						propattr.xmlns = namespace
+					end
+
+					if proptype == "string" and v is string then
+						propattr[single_attribute] = v
+					elseif proptype == "number" and v is number then
+						propattr[single_attribute] = string.format("%g", v)
+					elseif proptype == "integer" and v is number then
+						propattr[single_attribute] = string.format("%d", v)
+					elseif proptype == "boolean" and v is boolean then
+						propattr[single_attribute] = v and "1" or "0"
+					end
+					out:tag(name, propattr):up();
+
 				else
 					local propattr : { string : string }
 					if namespace ~= current_ns then
--- a/teal-src/util/jsonschema.tl	Sun Mar 07 01:41:39 2021 +0100
+++ b/teal-src/util/jsonschema.tl	Sun Mar 07 12:48:49 2021 +0100
@@ -65,6 +65,7 @@
 		-- nonstantard, maybe in the future
 		text : boolean
 		x_name_is_value : boolean
+		x_single_attribute : string
 	end
 
 	xml : xml_t
--- a/util/datamapper.lua	Sun Mar 07 01:41:39 2021 +0100
+++ b/util/datamapper.lua	Sun Mar 07 12:48:49 2021 +0100
@@ -19,6 +19,7 @@
 			local is_attribute = false
 			local is_text = false
 			local name_is_value = false;
+			local single_attribute
 
 			local proptype
 			if type(propschema) == "table" then
@@ -43,6 +44,8 @@
 					is_text = true
 				elseif propschema.xml.x_name_is_value then
 					name_is_value = true
+				elseif propschema.xml.x_single_attribute then
+					single_attribute = propschema.xml.x_single_attribute
 				end
 			end
 
@@ -77,6 +80,16 @@
 					out[prop] = tonumber(s:get_text())
 				end
 
+			elseif single_attribute then
+				local c = s:get_child(name, namespace)
+				local a = c and c.attr[single_attribute]
+				if proptype == "string" then
+					out[prop] = a
+				elseif proptype == "integer" or proptype == "number" then
+					out[prop] = tonumber(a)
+				elseif proptype == "boolean" then
+					out[prop] = toboolean(a)
+				end
 			else
 
 				if proptype == "string" then
@@ -135,6 +148,7 @@
 				local is_attribute = false
 				local is_text = false
 				local name_is_value = false;
+				local single_attribute
 
 				if type(propschema) == "table" and propschema.xml then
 
@@ -155,6 +169,8 @@
 						is_text = true
 					elseif propschema.xml.x_name_is_value then
 						name_is_value = true
+					elseif propschema.xml.x_single_attribute then
+						single_attribute = propschema.xml.x_single_attribute
 					end
 				end
 
@@ -179,6 +195,24 @@
 					if type(v) == "string" then
 						out:text(v)
 					end
+				elseif single_attribute then
+					local propattr = {}
+
+					if namespace ~= current_ns then
+						propattr.xmlns = namespace
+					end
+
+					if proptype == "string" and type(v) == "string" then
+						propattr[single_attribute] = v
+					elseif proptype == "number" and type(v) == "number" then
+						propattr[single_attribute] = string.format("%g", v)
+					elseif proptype == "integer" and type(v) == "number" then
+						propattr[single_attribute] = string.format("%d", v)
+					elseif proptype == "boolean" and type(v) == "boolean" then
+						propattr[single_attribute] = v and "1" or "0"
+					end
+					out:tag(name, propattr):up();
+
 				else
 					local propattr
 					if namespace ~= current_ns then