diff --git a/Cargo.toml b/Cargo.toml index 0e8d802..cb561f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,9 @@ description = "Reimplementation of the Scansion project in Rust" [dependencies] nom = "7.1" -jid = "0.9" +jid = { version = "0.9", features = [ "minidom" ] } minidom = "0.15.1" +xmpp-parsers = "0.19.2" nom_locate = "4.0.0" rand = "0.8" diff --git a/src/element.rs b/src/element.rs index bcb79fd..0f63597 100644 --- a/src/element.rs +++ b/src/element.rs @@ -43,7 +43,8 @@ use crate::parsers::parse_variable; use crate::types::{Client, Context, Entity, VariableAttr}; use jid::BareJid; -use minidom::{Element, Node}; +use minidom::{Element, Error as MinidomError, Node}; +use xmpp_parsers::{iq::Iq, message::Message, presence::Presence, Error as XMPPParserError}; /// Namespaces used for Client entities pub static DEFAULT_NS: &str = "jabber:client"; @@ -66,23 +67,22 @@ enum NodeType { } #[derive(Debug, Clone)] -struct ScanNode<'a> { +struct ScanNode { node: Node, - context: Option<&'a Context>, } -impl<'a> ScanNode<'a> { - fn new(node: Node, context: Option<&'a Context>) -> ScanNode { - ScanNode { node, context } +impl ScanNode { + fn new(node: Node) -> ScanNode { + ScanNode { node } } } -impl<'a> PartialEq for ScanNode<'a> { +impl PartialEq for ScanNode { fn eq(&self, other: &Node) -> bool { match (&self.node, other) { (Node::Text(text1), Node::Text(text2)) => text1 == text2, (Node::Element(elem1), Node::Element(elem2)) => { - ScanElement::new(elem1.clone()).with_context(self.context) == elem2 + ScanElement::new(elem1.clone()) == *elem2 } _ => false, } @@ -129,30 +129,24 @@ fn filter_whitespace_nodes(nodes: Vec) -> Vec { } #[derive(Debug)] -struct ScanNodes<'a, T: Debug> { +struct ScanNodes { nodes: Vec, - context: Option<&'a Context>, _strict: PhantomData, } -impl<'a> ScanNodes<'a, NonStrictComparison> { - fn new(nodes: Vec, context: Option<&'a Context>) -> ScanNodes<'a, NonStrictComparison> { +impl ScanNodes { + fn new(nodes: Vec) -> ScanNodes { Self { nodes, - context, _strict: PhantomData, } } } -impl<'a> ScanNodes<'a, StrictComparison> { - fn new_strict( - nodes: Vec, - context: Option<&'a Context>, - ) -> ScanNodes<'a, StrictComparison> { +impl ScanNodes { + fn new_strict(nodes: Vec) -> ScanNodes { Self { nodes, - context, _strict: PhantomData, } } @@ -161,11 +155,11 @@ impl<'a> ScanNodes<'a, StrictComparison> { /// Tags with mixed significant text and children tags aren't valid in XMPP, so we know we can /// remove them. Text leaves are compared as is. When comparing strictly, elements must be exactly the /// same. -impl<'a> PartialEq> for ScanNodes<'a, StrictComparison> { +impl PartialEq> for ScanNodes { fn eq(&self, other: &Vec) -> bool { let filtered_self = filter_whitespace_nodes(self.nodes.clone()) .into_iter() - .map(|node| ScanNode::new(node, self.context)) + .map(|node| ScanNode::new(node)) .collect::>(); let filtered_other = filter_whitespace_nodes(other.clone()); @@ -176,7 +170,7 @@ impl<'a> PartialEq> for ScanNodes<'a, StrictComparison> { /// Tags with mixed significant text and children tags aren't valid in XMPP, so we know we can /// remove them. Text leaves are compared as is. When doing non-strict comparison, the target /// element must have all attributes and children of the test element but it can have more. -impl<'a> PartialEq> for ScanNodes<'a, NonStrictComparison> { +impl PartialEq> for ScanNodes { fn eq(&self, other: &Vec) -> bool { let filtered_other = filter_whitespace_nodes(other.clone()); @@ -184,7 +178,7 @@ impl<'a> PartialEq> for ScanNodes<'a, NonStrictComparison> { .into_iter() // Maps nodes to their comparison result .fold(true, |res, node| { - let scan = ScanNode::new(node, self.context); + let scan = ScanNode::new(node); res && filtered_other .iter() .find(|onode| &&scan == onode) @@ -200,12 +194,11 @@ impl<'a> PartialEq> for ScanNodes<'a, NonStrictComparison> { /// Also uses the custom ScanNode implementation. #[cfg_attr(test, derive(PartialEq))] #[derive(Debug, Clone)] -pub struct ScanElement<'a> { - elem: Element, - context: Option<&'a Context>, +pub struct ScanElement { + pub elem: Element, } -impl<'a> Deref for ScanElement<'a> { +impl Deref for ScanElement { type Target = Element; fn deref(&self) -> &Self::Target { @@ -213,26 +206,53 @@ impl<'a> Deref for ScanElement<'a> { } } -impl<'a> ScanElement<'a> { - pub fn new(elem: Element) -> ScanElement<'a> { - Self { - elem, - context: None, +fn apply_context(mut elem: Element, context: &Context) -> Element { + // Parse variables in attributes. + for (_attr, val) in elem.attrs_mut() { + if let Ok((_, var)) = parse_variable(val.as_str().into()) { + match var { + VariableAttr::FullJid(name) => match context.get(&name) { + Some(Entity::Client(Client { jid, .. })) => *val = String::from(jid.clone()), + _ => (), + }, + VariableAttr::BareJid(name) => match context.get(&name) { + Some(Entity::Client(Client { jid, .. })) => { + *val = String::from(BareJid::from(jid.clone())) + } + _ => (), + }, + } } } + + for node in elem.nodes_mut() { + match node { + Node::Element(child) => *node = Node::Element(apply_context(child.clone(), context)), + Node::Text(text) => *node = Node::Text(text.to_string()), + }; + } + + elem +} + +impl ScanElement { + pub fn new(elem: Element) -> ScanElement { + Self { elem } + } + + pub fn apply_context(self, context: &Context) -> ScanElement { + Self { + elem: apply_context(self.elem, context), + } + } + + pub fn into_inner(self) -> Element { + self.elem + } } -impl<'a> ScanElement<'a> { - pub fn with_context(self, context: Option<&'a Context>) -> ScanElement<'a> { - Self { - elem: self.elem, - context, - } - } -} - -impl<'a> PartialEq<&Element> for ScanElement<'a> { - fn eq(&self, other: &&Element) -> bool { +impl PartialEq for ScanElement { + fn eq(&self, other: &Element) -> bool { let self_ns = self.elem.ns(); if self.elem.name() == other.name() && self_ns == other.ns() { let strict_attr = self.elem.attr("scansion:strict"); @@ -249,28 +269,6 @@ impl<'a> PartialEq<&Element> for ScanElement<'a> { continue; } - // Parse variables. If parsing fails, continue attr comparison. - // If context isn't set, skip this and continue attr comparison. - if let Ok((_, var)) = parse_variable(val.into()) && - let Some(context) = self.context { - let res = match var { - VariableAttr::FullJid(name) => match context.get(&name) { - Some(Entity::Client(Client { jid, .. })) => String::from(jid.clone()), - _ => return false, - }, - VariableAttr::BareJid(name) => match context.get(&name) { - Some(Entity::Client(Client { jid, .. })) => String::from(BareJid::from(jid.clone())), - _ => return false, - }, - }; - - if let Some(oval) = other.attr(attr) && res == oval { - continue; - } else { - return false; - } - } - match (attr, other.attr(attr)) { (attr, _) if attr == "scansion:strict" => continue, (_, None) => return false, @@ -292,11 +290,10 @@ impl<'a> PartialEq<&Element> for ScanElement<'a> { _ => (), } - let nodes = - ScanNodes::new_strict(self.elem.nodes().cloned().collect(), self.context); + let nodes = ScanNodes::new_strict(self.elem.nodes().cloned().collect()); nodes == onodes } else { - let nodes = ScanNodes::new(self.elem.nodes().cloned().collect(), self.context); + let nodes = ScanNodes::new(self.elem.nodes().cloned().collect()); nodes == onodes } } else { @@ -320,12 +317,12 @@ mod tests { let elem1 = Node::Element(Element::from_str("").unwrap()); assert_eq!( - ScanNodes::new_strict(vec![elem1.clone()], None), + ScanNodes::new_strict(vec![elem1.clone()]), vec![elem1.clone()], ); assert_ne!( - ScanNodes::new_strict(vec![text1.clone()], None), + ScanNodes::new_strict(vec![text1.clone()]), vec![text2.clone()], ); } @@ -337,32 +334,32 @@ mod tests { let elem1 = Node::Element(Element::from_str("").unwrap()); assert_eq!( - ScanNodes::new_strict(vec![elem1.clone()], None), + ScanNodes::new_strict(vec![elem1.clone()]), vec![elem1.clone()], ); assert_eq!( - ScanNodes::new_strict(vec![text1.clone(), elem1.clone(), text2.clone()], None), + ScanNodes::new_strict(vec![text1.clone(), elem1.clone(), text2.clone()]), vec![elem1.clone()], ); assert_eq!( - ScanNodes::new_strict(vec![text1.clone(), elem1.clone()], None), + ScanNodes::new_strict(vec![text1.clone(), elem1.clone()]), vec![elem1.clone(), text2.clone()], ); assert_eq!( - ScanNodes::new_strict(vec![elem1.clone(), text1.clone(), elem1.clone()], None), + ScanNodes::new_strict(vec![elem1.clone(), text1.clone(), elem1.clone()]), vec![elem1.clone(), elem1.clone()], ); assert_ne!( - ScanNodes::new_strict(vec![elem1.clone(), text1.clone(), elem1.clone()], None), + ScanNodes::new_strict(vec![elem1.clone(), text1.clone(), elem1.clone()]), vec![elem1.clone()], ); assert_ne!( - ScanNodes::new_strict(vec![Node::Text(String::from("\n\tfoo\n"))], None), + ScanNodes::new_strict(vec![Node::Text(String::from("\n\tfoo\n"))]), vec![Node::Text(String::from("\n\tfoo"))], ); } @@ -375,7 +372,7 @@ mod tests { let elem2: Element = "".parse().unwrap(); let scan1 = ScanElement::new(elem1); - assert_eq!(scan1, &elem2); + assert_eq!(scan1, elem2); } #[test] @@ -388,7 +385,7 @@ mod tests { .unwrap(); let scan1 = ScanElement::new(elem1); - assert_ne!(scan1, &elem2); + assert_ne!(scan1, elem2); } #[test] @@ -396,7 +393,7 @@ mod tests { let elem1: Element = "".parse().unwrap(); let scan1 = ScanElement::new(elem1.clone()); - assert_eq!(scan1, &elem1); + assert_eq!(scan1, elem1); let elem2: Element = " @@ -410,7 +407,7 @@ mod tests { .unwrap(); let scan2 = ScanElement::new(elem2); - assert_eq!(scan2, &elem3); + assert_eq!(scan2, elem3); } #[test] @@ -419,7 +416,7 @@ mod tests { let elem2: Element = "".parse().unwrap(); let scan1 = ScanElement::new(elem1); - assert_ne!(scan1, &elem2); + assert_ne!(scan1, elem2); } #[test] @@ -443,7 +440,7 @@ mod tests { .unwrap(); let scan1 = ScanElement::new(elem1); - assert_eq!(scan1, &elem2); + assert_eq!(scan1, elem2); } #[test] @@ -464,7 +461,7 @@ mod tests { .unwrap(); let scan1 = ScanElement::new(elem1); - assert_ne!(scan1, &elem2); + assert_ne!(scan1, elem2); } #[test] @@ -474,7 +471,7 @@ mod tests { .unwrap(); let scan1 = ScanElement::new(elem1.clone()); - assert_eq!(scan1, &elem1); + assert_eq!(scan1, elem1); let elem2: Element = " @@ -488,7 +485,7 @@ mod tests { .unwrap(); let scan2 = ScanElement::new(elem2); - assert_eq!(scan2, &elem3); + assert_eq!(scan2, elem3); } #[test] @@ -499,7 +496,7 @@ mod tests { let elem2: Element = "".parse().unwrap(); let scan1 = ScanElement::new(elem1); - assert_ne!(scan1, &elem2); + assert_ne!(scan1, elem2); let elem2: Element = " @@ -513,7 +510,7 @@ mod tests { .unwrap(); let scan2 = ScanElement::new(elem2); - assert_ne!(scan2, &elem3); + assert_ne!(scan2, elem3); } #[test] @@ -524,7 +521,7 @@ mod tests { let elem2: Element = "".parse().unwrap(); let scan1 = ScanElement::new(elem1); - assert_eq!(scan1, &elem2); + assert_eq!(scan1, elem2); // 'jabber:client' is non strict by default let elem3: Element = "".parse().unwrap(); @@ -533,7 +530,7 @@ mod tests { .unwrap(); let scan3 = ScanElement::new(elem3); - assert_eq!(scan3, &elem4); + assert_eq!(scan3, elem4); } #[test] @@ -550,7 +547,7 @@ mod tests { .unwrap(); let scan2 = ScanElement::new(elem2); - assert_ne!(scan2, &elem3); + assert_ne!(scan2, elem3); } #[test] @@ -567,7 +564,7 @@ mod tests { .unwrap(); let scan1 = ScanElement::new(elem1); - assert_ne!(scan1, &elem2); + assert_ne!(scan1, elem2); } #[test] @@ -578,7 +575,7 @@ mod tests { let elem2: Element = "".parse().unwrap(); let scan1 = ScanElement::new(elem1); - assert_eq!(scan1, &elem2); + assert_eq!(scan1, elem2); } #[test] @@ -586,12 +583,12 @@ mod tests { let louise = Client::new(Jid::from_str("louise@example.com").unwrap(), "passwd"); let rosa_phone = Client::new(Jid::from_str("rosa@example.com/phone").unwrap(), "passwd"); - let context = Some({ + let context = { let mut tmp = HashMap::new(); tmp.insert(String::from("louise"), Entity::Client(louise)); tmp.insert(String::from("rosa's phone"), Entity::Client(rosa_phone)); tmp - }); + }; let elem1: Element = "" .parse() @@ -599,9 +596,9 @@ mod tests { let elem2: Element = "" .parse() .unwrap(); - let scan1 = ScanElement::new(elem1).with_context(context.as_ref()); + let scan1 = ScanElement::new(elem1).apply_context(&context); - assert_eq!(scan1, &elem2); + assert_eq!(scan1, elem2); let elem3: Element = "" .parse() @@ -609,9 +606,9 @@ mod tests { let elem4: Element = "" .parse() .unwrap(); - let scan3 = ScanElement::new(elem3).with_context(context.as_ref()); + let scan3 = ScanElement::new(elem3).apply_context(&context); - assert_eq!(scan3, &elem4); + assert_eq!(scan3, elem4); } #[test] @@ -621,11 +618,11 @@ mod tests { "passwd", ); - let context = Some({ + let context = { let mut tmp = HashMap::new(); tmp.insert(String::from("louise"), Entity::Client(louise)); tmp - }); + }; let elem1: Element = "" .parse() @@ -634,8 +631,8 @@ mod tests { "" .parse() .unwrap(); - let scan1 = ScanElement::new(elem1).with_context(context.as_ref()); + let scan1 = ScanElement::new(elem1).apply_context(&context); - assert_eq!(scan1, &elem2); + assert_eq!(scan1, elem2); } } diff --git a/src/interpreter.rs b/src/interpreter.rs index 53a7cf6..a2af08c 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -22,12 +22,12 @@ use rand::{thread_rng, Rng}; #[cfg_attr(test, derive(PartialEq))] #[derive(Debug)] -pub struct InOutStanza<'a> { - pub inbound: Vec>, - pub outbound: Vec>, +pub struct InOutStanza { + pub inbound: Vec, + pub outbound: Vec, } -impl<'a> InOutStanza<'a> { +impl InOutStanza { fn new() -> Self { InOutStanza { inbound: Vec::new(), @@ -35,11 +35,11 @@ impl<'a> InOutStanza<'a> { } } - fn sends(&mut self, scan: ScanElement<'a>) { + fn sends(&mut self, scan: ScanElement) { self.inbound.push(scan) } - fn receives(&mut self, scan: ScanElement<'a>) { + fn receives(&mut self, scan: ScanElement) { self.outbound.push(scan) } } @@ -72,7 +72,8 @@ fn bind_context(context: Context) -> Context { .collect::() } -pub fn read_actions<'a>(spec: Spec, context: &'a Context) -> Result, MinidomError> { +/// Reads Actions from Spec and converts that to InOutStanza +pub fn read_actions(spec: Spec, context: &Context) -> Result { let mut inout = InOutStanza::new(); for action in spec.actions { match action { @@ -174,10 +175,10 @@ louise receives: let context: Context = bind_context(spec.context.clone()); let res = InOutStanza { inbound: vec![ - ScanElement::new(JOIN.parse::().unwrap()).with_context(Some(&context)) + ScanElement::new(JOIN.parse::().unwrap()).apply_context(&context) ], outbound: vec![ - ScanElement::new(CONFIRM.parse::().unwrap()).with_context(Some(&context)) + ScanElement::new(CONFIRM.parse::().unwrap()).apply_context(&context) ], };