stanzacmp: Support for captures

Thu, 23 Mar 2023 11:43:25 +0000

author
Matthew Wild <mwild1@gmail.com>
date
Thu, 23 Mar 2023 11:43:25 +0000
changeset 170
db73c4c317ce
parent 169
ecae87f5aaba
child 171
433a1f36d0d3

stanzacmp: Support for captures

scansion/stanzacmp.lua file | annotate | diff | comparison | revisions
spec/stanzacmp_spec.lua file | annotate | diff | comparison | revisions
--- a/scansion/stanzacmp.lua	Sat May 08 13:21:56 2021 +0200
+++ b/scansion/stanzacmp.lua	Thu Mar 23 11:43:25 2023 +0000
@@ -21,21 +21,38 @@
 	error("Unexpected scansion:strict value: "..opt);
 end
 
-local function stanzas_strict_match(stanza1, stanza2)
+local function is_wildcard(k, v)
+	if v == "{scansion:any}" then
+		return "attr:"..k;
+	end
+	return (v:match("^{scansion:capture:([^}]+)}$"));
+end
+
+-- stanza1 == expected, stanza2 == variable
+-- captures is an optional table to store captures (captures["foo"] == {scansion:capture:foo})
+local function stanzas_strict_match(stanza1, stanza2, captures)
 	if stanza1.name ~= stanza2.name or stanza1.attr.xmlns ~= stanza2.attr.xmlns then
 		return false;
 	end
 	
 	for k, v in pairs(stanza1.attr) do
-		if not k:match("^scansion:") and v ~= "{scansion:any}" and stanza2.attr[k] ~= v then
+		local wildcard = is_wildcard(k, v);
+		if not k:match("^scansion:") and not wildcard and stanza2.attr[k] ~= v then
 			return false;
 		end
+		if wildcard and captures then
+			captures[wildcard] = stanza2.attr[k];
+		end
 	end
 	
 	for k, v in pairs(stanza2.attr) do
-		if stanza1.attr[k] ~= "{scansion:any}" and stanza1.attr[k] ~= v then
+		local wildcard = is_wildcard(k, stanza1.attr[k]);
+		if not wildcard and stanza1.attr[k] ~= v then
 			return false;
 		end
+		if wildcard and captures then
+			captures[wildcard] = v;
+		end
 	end
 	
 	if #stanza1.tags ~= #stanza2.tags then
@@ -56,7 +73,7 @@
 			end
 			if type(child) == "table" and child2.name == child.name and child2.attr.xmlns == child.attr.xmlns then
 				-- Strict deep match
-				match = stanzas_strict_match(child, child2);
+				match = stanzas_strict_match(child, child2, captures);
 			elseif type(child) == "string" then -- Text nodes, must be equal, ignoring leading/trailing whitespace
 				match = trim(child) == trim(child2);
 			end
@@ -71,18 +88,22 @@
 
 -- Everything in stanza1 should be present in stanza2
 
-local function stanzas_match(stanza1, stanza2)
+local function stanzas_match(stanza1, stanza2, captures)
 	if wants_strict(stanza1, stanza1.attr.xmlns == nil and "no" or "yes") then
-		return stanzas_strict_match(stanza1, stanza2);
+		return stanzas_strict_match(stanza1, stanza2, captures);
 	end
 	if stanza1.name ~= stanza2.name or stanza1.attr.xmlns ~= stanza2.attr.xmlns then
 		return false;
 	end
 	
 	for k, v in pairs(stanza1.attr) do
-		if not k:match("^scansion:") and v ~= "{scansion:any}" and stanza2.attr[k] ~= v then
+		local wildcard = is_wildcard(k, v);
+		if not k:match("^scansion:") and not wildcard and stanza2.attr[k] ~= v then
 			return false;
 		end
+		if wildcard and captures then
+			captures[wildcard] = stanza2.attr[k];
+		end
 	end
 	
 	local matched_children = {};
@@ -98,7 +119,7 @@
 
 					if type(child2) == type(child) then
 						if type(child) == "table" and child2.name == child.name and child2.attr.xmlns == child.attr.xmlns then
-							match = stanzas_match(child, child2);
+							match = stanzas_match(child, child2, captures);
 						elseif type(child) == "string" then -- Text nodes, must be equal, ignoring leading/trailing whitespace
 							match = trim(child) == trim(child2);
 						end
--- a/spec/stanzacmp_spec.lua	Sat May 08 13:21:56 2021 +0200
+++ b/spec/stanzacmp_spec.lua	Thu Mar 23 11:43:25 2023 +0000
@@ -18,9 +18,9 @@
 	return table.unpack(out, 1, n);
 end
 
-local function yes(s1, s2)
+local function yes(s1, s2, captures)
 	s1, s2 = parse(s1, s2);
-	if not stanzacmp.stanzas_match(s1, s2) then
+	if not stanzacmp.stanzas_match(s1, s2, captures) then
 		print("s1", s1)
 		print("s2", s2)
 		print("literal", tostring(s1) == tostring(s2));
@@ -28,9 +28,9 @@
 	end
 end
 
-local function no(s1, s2)
+local function no(s1, s2, captures)
 	s1, s2 = parse(s1, s2);
-	if stanzacmp.stanzas_match(s1, s2) then
+	if stanzacmp.stanzas_match(s1, s2, captures) then
 		print("s1", s1)
 		print("s2", s2)
 		assert(false, "Match, but they should not.");
@@ -197,4 +197,28 @@
 			</x>
 		</presence>]]);
 	end);
+
+	it("supports captures", function ()
+		local captures = {};
+		yes([[<presence from='room@conference.localhost/Romeo'>
+			<x xmlns='http://jabber.org/protocol/muc#user'>
+				<status code='{scansion:capture:code1}' xmlns='http://jabber.org/protocol/muc#user'/>
+				<item jid='{scansion:any}' role='moderator' xmlns='http://jabber.org/protocol/muc#user' affiliation='{scansion:any}'/>
+				<status code='{scansion:capture:code2}' xmlns='http://jabber.org/protocol/muc#user'/>
+			</x>
+		</presence>]],
+		[[<presence to='user@localhost/KeoGLEr3' from='room@conference.localhost/Romeo'>
+			<x xmlns='http://jabber.org/protocol/muc#user'>
+				<status code='201' xmlns='http://jabber.org/protocol/muc#user'/>
+				<item jid='user@localhost/KeoGLEr3' role='moderator' xmlns='http://jabber.org/protocol/muc#user' affiliation='owner'/>
+				<status code='110' xmlns='http://jabber.org/protocol/muc#user'/>
+			</x>
+		</presence>]], captures);
+		assert.same({
+			["attr:jid"] = 'user@localhost/KeoGLEr3';
+			["attr:affiliation"] = "owner";
+			["code1"] = "201";
+			["code2"] = "110";
+		}, captures);
+	end);
 end);

mercurial