mod interpreter
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
parent
4688712904
commit
934376e974
4 changed files with 191 additions and 1 deletions
|
@ -11,6 +11,7 @@ nom = "7.1"
|
||||||
jid = "0.9"
|
jid = "0.9"
|
||||||
minidom = "0.15.1"
|
minidom = "0.15.1"
|
||||||
nom_locate = "4.0.0"
|
nom_locate = "4.0.0"
|
||||||
|
rand = "0.8"
|
||||||
|
|
||||||
# [patch.crates-io]
|
# [patch.crates-io]
|
||||||
# jid = { path = "../xmpp-rs/jid" }
|
# jid = { path = "../xmpp-rs/jid" }
|
||||||
|
|
|
@ -201,6 +201,7 @@ impl<'a> PartialEq<Vec<Node>> for ScanNodes<'a, NonStrictComparison> {
|
||||||
/// Comparison between elements needs to take into accounts the `scansion:strict` attribute which
|
/// Comparison between elements needs to take into accounts the `scansion:strict` attribute which
|
||||||
/// changes the way the comparison is done.
|
/// changes the way the comparison is done.
|
||||||
/// Also uses the custom ScanNode implementation.
|
/// Also uses the custom ScanNode implementation.
|
||||||
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ScanElement<'a> {
|
pub struct ScanElement<'a> {
|
||||||
elem: Element,
|
elem: Element,
|
||||||
|
|
186
src/interpreter.rs
Normal file
186
src/interpreter.rs
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
// Copyright (C) 2023-2099 The crate authors.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Affero General Public License as published by the
|
||||||
|
// Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
// option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
||||||
|
// for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::element::ScanElement;
|
||||||
|
use crate::parsers::parse_spec;
|
||||||
|
use crate::types::{Action, Context, Entity, Spec};
|
||||||
|
use jid::Jid;
|
||||||
|
use minidom::{Element, Error as MinidomError};
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
|
||||||
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InOutStanza<'a> {
|
||||||
|
pub inbound: Vec<ScanElement<'a>>,
|
||||||
|
pub outbound: Vec<ScanElement<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> InOutStanza<'a> {
|
||||||
|
fn new() -> Self {
|
||||||
|
InOutStanza {
|
||||||
|
inbound: Vec::new(),
|
||||||
|
outbound: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sends(&mut self, scan: ScanElement<'a>) {
|
||||||
|
self.inbound.push(scan)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receives(&mut self, scan: ScanElement<'a>) {
|
||||||
|
self.outbound.push(scan)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_resource() -> String {
|
||||||
|
let id: u8 = thread_rng().gen();
|
||||||
|
format!("{}", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't need information other than FullJid and name here as we're going to use that directly
|
||||||
|
// in tests. No need to connect anywhere.
|
||||||
|
/// When connecting, clients are assigned a FullJid during <bind/>. This function adds a random
|
||||||
|
/// resource to clients that only have a BareJid specified.
|
||||||
|
fn bind_context(context: Context) -> Context {
|
||||||
|
context
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, mut context)| {
|
||||||
|
match context {
|
||||||
|
Entity::Client(ref mut client) => match &client.jid {
|
||||||
|
Jid::Bare(bare) => {
|
||||||
|
let jid = bare.clone().with_resource(make_resource());
|
||||||
|
client.jid = Jid::Full(jid)
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
(name, context)
|
||||||
|
})
|
||||||
|
.collect::<Context>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_actions<'a>(spec: Spec, context: &'a Context) -> Result<InOutStanza<'a>, MinidomError> {
|
||||||
|
let mut inout = InOutStanza::new();
|
||||||
|
for action in spec.actions {
|
||||||
|
match action {
|
||||||
|
Action::Receive(name, s) => {
|
||||||
|
let _ = context
|
||||||
|
.get(&name)
|
||||||
|
.expect(format!("Name '{}' not found in clients", name).as_str());
|
||||||
|
|
||||||
|
let elem = s.parse::<Element>()?;
|
||||||
|
inout.receives(ScanElement::new(elem).with_context(Some(&context)));
|
||||||
|
}
|
||||||
|
Action::Send(name, s) => {
|
||||||
|
let _ = context
|
||||||
|
.get(&name)
|
||||||
|
.expect(format!("Name '{}' not found in clients", name).as_str());
|
||||||
|
|
||||||
|
let elem = s.parse::<Element>()?;
|
||||||
|
inout.sends(ScanElement::new(elem).with_context(Some(&context)));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(inout)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_spec<'a>(buf: &str) -> Spec {
|
||||||
|
let mut spec = parse_spec(buf).unwrap();
|
||||||
|
spec.context = bind_context(spec.context.clone());
|
||||||
|
spec
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::{Client, Entity};
|
||||||
|
|
||||||
|
static BUF1: &str = r#"# MUC Creation
|
||||||
|
# Single user MUC creation
|
||||||
|
## muc
|
||||||
|
|
||||||
|
[Client] louise
|
||||||
|
jid: louise@localhost
|
||||||
|
password: password
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
louise connects
|
||||||
|
|
||||||
|
louise sends:
|
||||||
|
<presence from="${louise's full JID}" to="room@muc.localhost/louise" xmlns="jabber:client">
|
||||||
|
<x xmlns="http://jabber.org/protocol/muc"/>
|
||||||
|
</presence>
|
||||||
|
|
||||||
|
louise receives:
|
||||||
|
<presence to="${louise's full JID}" from="room@muc.localhost/louise" id="{scansion:any}" xmlns="jabber:client">
|
||||||
|
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||||
|
<status code="201"/>
|
||||||
|
<item affiliation="owner" jid="${louise's full JID}" role="moderator"/>
|
||||||
|
<status code="110"/>
|
||||||
|
</x>
|
||||||
|
</presence>
|
||||||
|
"#;
|
||||||
|
|
||||||
|
static JOIN: &str = r#"<presence from="${louise's full JID}" to="room@muc.localhost/louise" xmlns="jabber:client">
|
||||||
|
<x xmlns="http://jabber.org/protocol/muc"/>
|
||||||
|
</presence>"#;
|
||||||
|
static CONFIRM: &str = r#"<presence to="${louise's full JID}" from="room@muc.localhost/louise" id="{scansion:any}" xmlns="jabber:client">
|
||||||
|
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||||
|
<status code="201"/>
|
||||||
|
<item affiliation="owner" jid="${louise's full JID}" role="moderator"/>
|
||||||
|
<status code="110"/>
|
||||||
|
</x>
|
||||||
|
</presence>"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bind_context() {
|
||||||
|
let spec = parse_spec(BUF1).unwrap();
|
||||||
|
|
||||||
|
let context = bind_context(spec.context);
|
||||||
|
assert_eq!(context.len(), 1);
|
||||||
|
match context.get("louise") {
|
||||||
|
Some(Entity::Client(Client {
|
||||||
|
jid: Jid::Full(full),
|
||||||
|
password,
|
||||||
|
..
|
||||||
|
})) => {
|
||||||
|
assert_eq!(full.node, Some(String::from("louise")));
|
||||||
|
assert_eq!(full.domain, String::from("localhost"));
|
||||||
|
assert_eq!(password, "password");
|
||||||
|
}
|
||||||
|
other => panic!("Error: context: {other:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_actions() {
|
||||||
|
let spec = parse_spec(BUF1.clone()).unwrap();
|
||||||
|
let context: Context = bind_context(spec.context.clone());
|
||||||
|
let res = InOutStanza {
|
||||||
|
inbound: vec![
|
||||||
|
ScanElement::new(JOIN.parse::<Element>().unwrap()).with_context(Some(&context))
|
||||||
|
],
|
||||||
|
outbound: vec![
|
||||||
|
ScanElement::new(CONFIRM.parse::<Element>().unwrap()).with_context(Some(&context))
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(read_actions(spec, &context).unwrap(), res);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,9 +7,11 @@
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
|
||||||
pub mod element;
|
pub mod element;
|
||||||
|
pub mod interpreter;
|
||||||
pub mod parsers;
|
pub mod parsers;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
pub use element::ScanElement;
|
pub use element::ScanElement;
|
||||||
|
pub use interpreter::{read_actions, read_spec};
|
||||||
pub use parsers::parse_spec;
|
pub use parsers::parse_spec;
|
||||||
pub use types::{Action, Client, Context, Metadata, Name, Spec};
|
pub use types::{Action, Client, Context, Entity, Metadata, Name, Spec};
|
||||||
|
|
Loading…
Reference in a new issue