Add Entity and Context types
Scansion supports declaring components in addition to clients. We don't support components yet but with this change it's not far off anymore. Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
parent
0b7718ff43
commit
d97bc62121
4 changed files with 51 additions and 35 deletions
|
@ -38,14 +38,12 @@
|
||||||
//! <message scansion:strict="true"/>
|
//! <message scansion:strict="true"/>
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use crate::parsers::parse_variable;
|
use crate::parsers::parse_variable;
|
||||||
use crate::types::VariableAttr;
|
use crate::types::{Client, Context, Entity, VariableAttr};
|
||||||
use crate::{Client, ClientName};
|
|
||||||
|
|
||||||
use jid::BareJid;
|
use jid::BareJid;
|
||||||
use minidom::{Element, Node};
|
use minidom::{Element, Node};
|
||||||
|
@ -56,8 +54,6 @@ pub static DEFAULT_NS: &str = "jabber:client";
|
||||||
/// Namespace used for scansion attributes
|
/// Namespace used for scansion attributes
|
||||||
pub static SCANSION_NS: &str = "https://matthewwild.co.uk/projects/scansion";
|
pub static SCANSION_NS: &str = "https://matthewwild.co.uk/projects/scansion";
|
||||||
|
|
||||||
pub type Context = HashMap<ClientName, Client>;
|
|
||||||
|
|
||||||
/// Strict Comparison marker
|
/// Strict Comparison marker
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct StrictComparison;
|
struct StrictComparison;
|
||||||
|
@ -261,11 +257,11 @@ impl<'a, 'b> PartialEq<&Element> for ScanElement<'a, 'b> {
|
||||||
let Some(context) = self.context {
|
let Some(context) = self.context {
|
||||||
let res = match var {
|
let res = match var {
|
||||||
VariableAttr::FullJid(name) => match context.get(&name) {
|
VariableAttr::FullJid(name) => match context.get(&name) {
|
||||||
Some(Client { jid, .. }) => String::from(jid.clone()),
|
Some(Entity::Client(Client { jid, .. })) => String::from(jid.clone()),
|
||||||
_ => return false,
|
_ => return false,
|
||||||
},
|
},
|
||||||
VariableAttr::BareJid(name) => match context.get(&name) {
|
VariableAttr::BareJid(name) => match context.get(&name) {
|
||||||
Some(Client { jid, .. }) => String::from(BareJid::from(jid.clone())),
|
Some(Entity::Client(Client { jid, .. })) => String::from(BareJid::from(jid.clone())),
|
||||||
_ => return false,
|
_ => return false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -314,6 +310,7 @@ impl<'a, 'b> PartialEq<&Element> for ScanElement<'a, 'b> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use jid::Jid;
|
use jid::Jid;
|
||||||
|
@ -591,10 +588,10 @@ mod tests {
|
||||||
let louise = Client::new(Jid::from_str("louise@example.com").unwrap(), "passwd");
|
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 rosa_phone = Client::new(Jid::from_str("rosa@example.com/phone").unwrap(), "passwd");
|
||||||
|
|
||||||
let clients = Some({
|
let context = Some({
|
||||||
let mut tmp = HashMap::new();
|
let mut tmp = HashMap::new();
|
||||||
tmp.insert(String::from("louise"), louise);
|
tmp.insert(String::from("louise"), Entity::Client(louise));
|
||||||
tmp.insert(String::from("rosa's phone"), rosa_phone);
|
tmp.insert(String::from("rosa's phone"), Entity::Client(rosa_phone));
|
||||||
tmp
|
tmp
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -604,7 +601,7 @@ mod tests {
|
||||||
let elem2: Element = "<message xmlns='foo' to='louise@example.com' />"
|
let elem2: Element = "<message xmlns='foo' to='louise@example.com' />"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let scan1 = ScanElement::new(&elem1).with_context(clients.as_ref());
|
let scan1 = ScanElement::new(elem1).with_context(context.as_ref());
|
||||||
|
|
||||||
assert_eq!(scan1, &elem2);
|
assert_eq!(scan1, &elem2);
|
||||||
|
|
||||||
|
@ -614,7 +611,7 @@ mod tests {
|
||||||
let elem4: Element = "<message xmlns='foo' to='rosa@example.com' />"
|
let elem4: Element = "<message xmlns='foo' to='rosa@example.com' />"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let scan3 = ScanElement::new(&elem3).with_context(clients.as_ref());
|
let scan3 = ScanElement::new(elem3).with_context(context.as_ref());
|
||||||
|
|
||||||
assert_eq!(scan3, &elem4);
|
assert_eq!(scan3, &elem4);
|
||||||
}
|
}
|
||||||
|
@ -626,9 +623,9 @@ mod tests {
|
||||||
"passwd",
|
"passwd",
|
||||||
);
|
);
|
||||||
|
|
||||||
let clients = Some({
|
let context = Some({
|
||||||
let mut tmp = HashMap::new();
|
let mut tmp = HashMap::new();
|
||||||
tmp.insert(String::from("louise"), louise);
|
tmp.insert(String::from("louise"), Entity::Client(louise));
|
||||||
tmp
|
tmp
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -639,7 +636,7 @@ mod tests {
|
||||||
"<message xmlns='foo'><foo to='louise@example.com/device1' /></message>"
|
"<message xmlns='foo'><foo to='louise@example.com/device1' /></message>"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let scan1 = ScanElement::new(&elem1).with_context(clients.as_ref());
|
let scan1 = ScanElement::new(elem1).with_context(context.as_ref());
|
||||||
|
|
||||||
assert_eq!(scan1, &elem2);
|
assert_eq!(scan1, &elem2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,4 +12,4 @@ pub mod types;
|
||||||
|
|
||||||
pub use element::ScanElement;
|
pub use element::ScanElement;
|
||||||
pub use parsers::parse_spec;
|
pub use parsers::parse_spec;
|
||||||
pub use types::{Action, Client, ClientName, Metadata, Spec};
|
pub use types::{Action, Client, Context, Metadata, Name, Spec};
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use crate::types::{Action, Client, ClientName, Metadata, Spec, VariableAttr};
|
use crate::types::{Action, Client, Context, Entity, Metadata, Name, Spec, VariableAttr};
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
@ -130,7 +130,7 @@ fn parse_meta(s: Span) -> IResult<Span, Metadata> {
|
||||||
Ok((s, meta))
|
Ok((s, meta))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_client(s: Span) -> IResult<Span, (ClientName, Client)> {
|
fn parse_client(s: Span) -> IResult<Span, (Name, Client)> {
|
||||||
let (s, (_, _, _, name, _)) =
|
let (s, (_, _, _, name, _)) =
|
||||||
tuple((allspaces, tag("[Client]"), space0, take_until("\n"), space0))(s)?;
|
tuple((allspaces, tag("[Client]"), space0, take_until("\n"), space0))(s)?;
|
||||||
|
|
||||||
|
@ -189,15 +189,26 @@ fn parse_client(s: Span) -> IResult<Span, (ClientName, Client)> {
|
||||||
Ok((s, (String::from(name), client)))
|
Ok((s, (String::from(name), client)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_clients(s: Span) -> IResult<Span, HashMap<ClientName, Client>> {
|
fn parse_clients(s: Span) -> IResult<Span, HashMap<Name, Client>> {
|
||||||
let (s, clients) = many0(parse_client)(s)?;
|
let (s, clients) = many0(parse_client)(s)?;
|
||||||
let mut map: HashMap<ClientName, Client> = HashMap::new();
|
let mut map: HashMap<Name, Client> = HashMap::new();
|
||||||
for (name, client) in clients {
|
for (name, client) in clients {
|
||||||
map.insert(name, client);
|
map.insert(name, client);
|
||||||
}
|
}
|
||||||
Ok((s, map))
|
Ok((s, map))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_context(s: Span) -> IResult<Span, Context> {
|
||||||
|
let (s, clients) = parse_clients(s)?;
|
||||||
|
Ok((
|
||||||
|
s,
|
||||||
|
clients
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, client)| (name, Entity::Client(client)))
|
||||||
|
.collect::<HashMap<_, _>>(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_sep(s: Span) -> IResult<Span, Token> {
|
fn parse_sep(s: Span) -> IResult<Span, Token> {
|
||||||
let (s, (pos, _)) = delimited(allspaces, tuple((position, many1(tag("-")))), allspaces)(s)?;
|
let (s, (pos, _)) = delimited(allspaces, tuple((position, many1(tag("-")))), allspaces)(s)?;
|
||||||
Ok((s, Token { position: pos }))
|
Ok((s, Token { position: pos }))
|
||||||
|
@ -269,12 +280,12 @@ fn parse_actions(s: Span) -> IResult<Span, Vec<Action>> {
|
||||||
pub fn parse_spec(i: &str) -> Result<Spec, Token> {
|
pub fn parse_spec(i: &str) -> Result<Spec, Token> {
|
||||||
let s: Span = i.into();
|
let s: Span = i.into();
|
||||||
let (s, metadata) = opt(parse_meta)(s)?;
|
let (s, metadata) = opt(parse_meta)(s)?;
|
||||||
let (s, clients) = parse_clients(s)?;
|
let (s, context) = parse_context(s)?;
|
||||||
let (s, _) = parse_sep(s)?;
|
let (s, _) = parse_sep(s)?;
|
||||||
let (_, actions) = parse_actions(s)?;
|
let (_, actions) = parse_actions(s)?;
|
||||||
Ok(Spec {
|
Ok(Spec {
|
||||||
metadata,
|
metadata,
|
||||||
clients,
|
context,
|
||||||
actions,
|
actions,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -300,6 +311,7 @@ pub fn parse_variable(s: Span) -> IResult<Span, VariableAttr> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
fn get_client(name: &str) -> Client {
|
fn get_client(name: &str) -> Client {
|
||||||
Client::new(
|
Client::new(
|
||||||
|
@ -457,7 +469,7 @@ mod tests {
|
||||||
password: password
|
password: password
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let mut clients: HashMap<ClientName, Client> = HashMap::new();
|
let mut clients: HashMap<Name, Client> = HashMap::new();
|
||||||
clients.insert(
|
clients.insert(
|
||||||
String::from("louise"),
|
String::from("louise"),
|
||||||
Client::new(Jid::from_str("louise@localhost").unwrap(), "password"),
|
Client::new(Jid::from_str("louise@localhost").unwrap(), "password"),
|
||||||
|
@ -574,8 +586,8 @@ louise receives:
|
||||||
.with_tags(vec![String::from("tag1"), String::from("tag2")]),
|
.with_tags(vec![String::from("tag1"), String::from("tag2")]),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut clients: HashMap<ClientName, Client> = HashMap::new();
|
let mut context: Context = HashMap::new();
|
||||||
clients.insert(String::from("louise"), get_client("louise"));
|
context.insert(String::from("louise"), Entity::Client(get_client("louise")));
|
||||||
|
|
||||||
let actions = vec![
|
let actions = vec![
|
||||||
Action::Connect(String::from("louise")),
|
Action::Connect(String::from("louise")),
|
||||||
|
@ -588,7 +600,7 @@ louise receives:
|
||||||
|
|
||||||
let spec = Spec {
|
let spec = Spec {
|
||||||
metadata,
|
metadata,
|
||||||
clients,
|
context,
|
||||||
actions,
|
actions,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
25
src/types.rs
25
src/types.rs
|
@ -10,8 +10,8 @@ use jid::Jid;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum VariableAttr {
|
pub enum VariableAttr {
|
||||||
FullJid(ClientName),
|
FullJid(Name),
|
||||||
BareJid(ClientName),
|
BareJid(Name),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -36,7 +36,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ClientName = String;
|
pub type Name = String;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
|
@ -67,18 +67,25 @@ impl Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Entity {
|
||||||
|
Client(Client),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Context = HashMap<Name, Entity>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Action<'a> {
|
pub enum Action<'a> {
|
||||||
Connect(ClientName),
|
Connect(Name),
|
||||||
Send(ClientName, &'a str),
|
Send(Name, &'a str),
|
||||||
Receive(ClientName, &'a str),
|
Receive(Name, &'a str),
|
||||||
ReceiveNone(ClientName),
|
ReceiveNone(Name),
|
||||||
Disconnect(ClientName),
|
Disconnect(Name),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Spec<'a> {
|
pub struct Spec<'a> {
|
||||||
pub metadata: Option<Metadata>,
|
pub metadata: Option<Metadata>,
|
||||||
pub clients: HashMap<ClientName, Client>,
|
pub context: Context,
|
||||||
pub actions: Vec<Action<'a>>,
|
pub actions: Vec<Action<'a>>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue