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:
Maxime “pep” Buquet 2023-04-21 17:14:57 +02:00
parent 0b7718ff43
commit d97bc62121
4 changed files with 51 additions and 35 deletions

View file

@ -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);
} }

View file

@ -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};

View file

@ -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,
}; };

View file

@ -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>>,
} }