util.stanza: Fix :top_tag() handling of namespaced attributes
authorMatthew Wild <mwild1@gmail.com>
Mon, 25 Mar 2019 14:37:43 +0000
changeset 9928 5a2e53bef031
parent 9927 e83dfcdeab59
child 9929 8d0112413997
util.stanza: Fix :top_tag() handling of namespaced attributes
spec/util_stanza_spec.lua
util/stanza.lua
--- a/spec/util_stanza_spec.lua	Mon Mar 25 15:20:28 2019 +0100
+++ b/spec/util_stanza_spec.lua	Mon Mar 25 14:37:43 2019 +0000
@@ -381,4 +381,35 @@
 			end);
 		end);
 	end);
+
+	describe("top_tag", function ()
+		local xml_parse = require "util.xml".parse;
+		it("works", function ()
+			local s = st.message({type="chat"}, "Hello");
+			local top_tag = s:top_tag();
+			assert.is_string(top_tag);
+			assert.not_equal("/>", top_tag:sub(-2, -1));
+			assert.equal(">", top_tag:sub(-1, -1));
+			local s2 = xml_parse(top_tag.."</message>");
+			assert(st.is_stanza(s2));
+			assert.equal("message", s2.name);
+			assert.equal(0, #s2);
+			assert.equal(0, #s2.tags);
+			assert.equal("chat", s2.attr.type);
+		end);
+
+		it("works with namespaced attributes", function ()
+			local s = xml_parse[[<message foo:bar='true' xmlns:foo='my-awesome-ns'/>]];
+			local top_tag = s:top_tag();
+			assert.is_string(top_tag);
+			assert.not_equal("/>", top_tag:sub(-2, -1));
+			assert.equal(">", top_tag:sub(-1, -1));
+			local s2 = xml_parse(top_tag.."</message>");
+			assert(st.is_stanza(s2));
+			assert.equal("message", s2.name);
+			assert.equal(0, #s2);
+			assert.equal(0, #s2.tags);
+			assert.equal("true", s2.attr["my-awesome-ns\1bar"]);
+		end);
+	end);
 end);
--- a/util/stanza.lua	Mon Mar 25 15:20:28 2019 +0100
+++ b/util/stanza.lua	Mon Mar 25 14:37:43 2019 +0000
@@ -270,6 +270,34 @@
 	until not self
 end
 
+local function _clone(stanza, only_top)
+	local attr, tags = {}, {};
+	for k,v in pairs(stanza.attr) do attr[k] = v; end
+	local old_namespaces, namespaces = stanza.namespaces;
+	if old_namespaces then
+		namespaces = {};
+		for k,v in pairs(old_namespaces) do namespaces[k] = v; end
+	end
+	local new = { name = stanza.name, attr = attr, namespaces = namespaces, tags = tags };
+	if not only_top then
+		for i=1,#stanza do
+			local child = stanza[i];
+			if child.name then
+				child = _clone(child);
+				t_insert(tags, child);
+			end
+			t_insert(new, child);
+		end
+	end
+	return setmetatable(new, stanza_mt);
+end
+
+local function clone(stanza, only_top)
+	if not is_stanza(stanza) then
+		error("bad argument to clone: expected stanza, got "..type(stanza));
+	end
+	return _clone(stanza, only_top);
+end
 
 local escape_table = { ["'"] = "&apos;", ["\""] = "&quot;", ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;" };
 local function xml_escape(str) return (s_gsub(str, "['&<>\"]", escape_table)); end
@@ -310,11 +338,8 @@
 end
 
 function stanza_mt.top_tag(t)
-	local attr_string = "";
-	if t.attr then
-		for k, v in pairs(t.attr) do if type(k) == "string" then attr_string = attr_string .. s_format(" %s='%s'", k, xml_escape(tostring(v))); end end
-	end
-	return s_format("<%s%s>", t.name, attr_string);
+	local top_tag_clone = clone(t, true);
+	return tostring(top_tag_clone):sub(1,-3)..">";
 end
 
 function stanza_mt.get_text(t)
@@ -388,33 +413,6 @@
 	end
 end
 
-local function _clone(stanza)
-	local attr, tags = {}, {};
-	for k,v in pairs(stanza.attr) do attr[k] = v; end
-	local old_namespaces, namespaces = stanza.namespaces;
-	if old_namespaces then
-		namespaces = {};
-		for k,v in pairs(old_namespaces) do namespaces[k] = v; end
-	end
-	local new = { name = stanza.name, attr = attr, namespaces = namespaces, tags = tags };
-	for i=1,#stanza do
-		local child = stanza[i];
-		if child.name then
-			child = _clone(child);
-			t_insert(tags, child);
-		end
-		t_insert(new, child);
-	end
-	return setmetatable(new, stanza_mt);
-end
-
-local function clone(stanza)
-	if not is_stanza(stanza) then
-		error("bad argument to clone: expected stanza, got "..type(stanza));
-	end
-	return _clone(stanza);
-end
-
 local function message(attr, body)
 	if not body then
 		return new_stanza("message", attr);