summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2021-08-14 08:59:40 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2021-09-07 17:32:19 +0100
commit204d2e3986ab74853797d7d28f723db1a088ee9f (patch)
tree598ad6f6d90477a3746d61bfce1436e5410474bb /src
parent8f58565372ce664bf9f9e619ba909fcf13f3a84d (diff)
downloadsubplot-204d2e3986ab74853797d7d28f723db1a088ee9f.tar.gz
bindings: Initial move toward multi-lingual bindings
Signed-off-by: Daniel Silverstone <dsilvers@digital-scurf.org>
Diffstat (limited to 'src')
-rw-r--r--src/bindings.rs175
-rw-r--r--src/matches.rs11
2 files changed, 48 insertions, 138 deletions
diff --git a/src/bindings.rs b/src/bindings.rs
index 0291e68..2e87fe0 100644
--- a/src/bindings.rs
+++ b/src/bindings.rs
@@ -12,6 +12,7 @@ use std::collections::HashMap;
use std::fmt::Debug;
use std::path::Path;
use std::str::FromStr;
+use std::sync::Arc;
use lazy_static::lazy_static;
use regex::{escape, Regex, RegexBuilder};
@@ -104,7 +105,7 @@ pub struct BindingImpl {
impl BindingImpl {
/// Create a new binding implementation
///
- /// ```
+ /// ```ignore
/// # use subplot::bindings::BindingImpl;
///
/// let bimpl = BindingImpl::new("foo::bar::func", Some("foo::bar::func_cleanup"));
@@ -118,7 +119,7 @@ impl BindingImpl {
/// Retrieve the function name in this binding
///
- /// ```
+ /// ```ignore
/// # use subplot::bindings::BindingImpl;
/// let bimpl = BindingImpl::new("foo::bar::func", None);
///
@@ -130,7 +131,7 @@ impl BindingImpl {
/// Retrieve the cleanup function name in this binding
///
- /// ```
+ /// ```ignore
/// # use subplot::bindings::BindingImpl;
/// let bimpl = BindingImpl::new("foo::bar::func", None);
///
@@ -155,7 +156,7 @@ pub struct Binding {
kind: StepKind,
pattern: String,
regex: Regex,
- impls: BindingImpl,
+ impls: HashMap<String, Arc<BindingImpl>>,
types: HashMap<String, CaptureType>,
}
@@ -164,8 +165,6 @@ impl Binding {
pub fn new(
kind: StepKind,
pattern: &str,
- function: &str,
- cleanup: Option<&str>,
case_sensitive: bool,
mut types: HashMap<String, CaptureType>,
) -> Result<Binding> {
@@ -183,11 +182,19 @@ impl Binding {
kind,
pattern: pattern.to_owned(),
regex,
- impls: BindingImpl::new(function, cleanup),
+ impls: HashMap::new(),
types,
})
}
+ /// Insert an impl into this binding
+ pub fn add_impl(&mut self, template: &str, function: &str, cleanup: Option<&str>) {
+ self.impls.insert(
+ template.to_string(),
+ Arc::new(BindingImpl::new(function, cleanup)),
+ );
+ }
+
/// Return the kind of step the binding is for.
pub fn kind(&self) -> StepKind {
self.kind
@@ -198,14 +205,19 @@ impl Binding {
&self.pattern
}
+ /// Retrieve a particular implementation by name
+ pub fn step_impl(&self, template: &str) -> Option<Arc<BindingImpl>> {
+ self.impls.get(template).cloned()
+ }
+
/// Return name of function that implements step.
pub fn function(&self) -> &str {
- self.impls.function()
+ self.impls.values().next().unwrap().function()
}
/// Return name of function that implements cleanup.
pub fn cleanup(&self) -> Option<&str> {
- self.impls.cleanup()
+ self.impls.values().next().unwrap().cleanup()
}
/// Return the compiled regular expression for the pattern of the
@@ -302,81 +314,41 @@ mod test_binding {
#[test]
fn creates_new_without_cleanup() {
- let b = Binding::new(
- StepKind::Given,
- "I am Tomjon",
- "set_name",
- None,
- false,
- HashMap::new(),
- )
- .unwrap();
+ let mut b = Binding::new(StepKind::Given, "I am Tomjon", false, HashMap::new()).unwrap();
assert_eq!(b.kind(), StepKind::Given);
assert!(b.regex().is_match("I am Tomjon"));
assert!(!b.regex().is_match("I am Tomjon of Lancre"));
assert!(!b.regex().is_match("Hello, I am Tomjon"));
+ b.add_impl("", "set_name", None);
assert_eq!(b.function(), "set_name");
assert_eq!(b.cleanup(), None);
}
#[test]
fn creates_new_with_cleanup() {
- let b = Binding::new(
- StepKind::Given,
- "I am Tomjon",
- "set_name",
- Some("unset_name"),
- false,
- HashMap::new(),
- )
- .unwrap();
+ let mut b = Binding::new(StepKind::Given, "I am Tomjon", false, HashMap::new()).unwrap();
assert_eq!(b.kind(), StepKind::Given);
assert!(b.regex().is_match("I am Tomjon"));
assert!(!b.regex().is_match("I am Tomjon of Lancre"));
assert!(!b.regex().is_match("Hello, I am Tomjon"));
+ b.add_impl("", "set_name", Some("unset_name"));
assert_eq!(b.function(), "set_name");
assert_eq!(b.cleanup(), Some("unset_name"));
}
#[test]
fn equal() {
- let a = Binding::new(
- StepKind::Given,
- "I am Tomjon",
- "set_name",
- Some("unset"),
- false,
- HashMap::new(),
- )
- .unwrap();
- let b = Binding::new(
- StepKind::Given,
- "I am Tomjon",
- "set_name",
- Some("unset"),
- false,
- HashMap::new(),
- )
- .unwrap();
+ let a = Binding::new(StepKind::Given, "I am Tomjon", false, HashMap::new()).unwrap();
+ let b = Binding::new(StepKind::Given, "I am Tomjon", false, HashMap::new()).unwrap();
assert_eq!(a, b);
}
#[test]
fn not_equal() {
- let a = Binding::new(
- StepKind::Given,
- "I am Tomjon",
- "set_name",
- None,
- false,
- HashMap::new(),
- )
- .unwrap();
+ let a = Binding::new(StepKind::Given, "I am Tomjon", false, HashMap::new()).unwrap();
let b = Binding::new(
StepKind::Given,
"I am Tomjon of Lancre",
- "set_name",
- None,
false,
HashMap::new(),
)
@@ -387,29 +359,21 @@ mod test_binding {
#[test]
fn does_not_match_with_wrong_kind() {
let step = ScenarioStep::new(StepKind::Given, "given", "yo");
- let b = Binding::new(StepKind::When, "yo", "do_yo", None, false, HashMap::new()).unwrap();
+ let b = Binding::new(StepKind::When, "yo", false, HashMap::new()).unwrap();
assert!(b.match_with_step(&step).is_none());
}
#[test]
fn does_not_match_with_wrong_text() {
let step = ScenarioStep::new(StepKind::Given, "given", "foo");
- let b = Binding::new(StepKind::Given, "bar", "yo", None, false, HashMap::new()).unwrap();
+ let b = Binding::new(StepKind::Given, "bar", false, HashMap::new()).unwrap();
assert!(b.match_with_step(&step).is_none());
}
#[test]
fn match_with_fixed_pattern() {
let step = ScenarioStep::new(StepKind::Given, "given", "foo");
- let b = Binding::new(
- StepKind::Given,
- "foo",
- "do_foo",
- None,
- false,
- HashMap::new(),
- )
- .unwrap();
+ let b = Binding::new(StepKind::Given, "foo", false, HashMap::new()).unwrap();
let m = b.match_with_step(&step).unwrap();
assert_eq!(m.kind(), StepKind::Given);
let mut parts = m.parts();
@@ -424,8 +388,6 @@ mod test_binding {
let b = Binding::new(
StepKind::Given,
r"I am (?P<who>\S+), I am",
- "set_name",
- None,
false,
HashMap::new(),
)
@@ -442,25 +404,9 @@ mod test_binding {
#[test]
fn case_sensitive_mismatch() {
let step = ScenarioStep::new(StepKind::Given, "given", "I am Tomjon");
- let b = Binding::new(
- StepKind::Given,
- r"i am tomjon",
- "set_name",
- None,
- false,
- HashMap::new(),
- )
- .unwrap();
+ let b = Binding::new(StepKind::Given, r"i am tomjon", false, HashMap::new()).unwrap();
assert!(b.match_with_step(&step).is_some());
- let b = Binding::new(
- StepKind::Given,
- r"i am tomjon",
- "set_name",
- None,
- true,
- HashMap::new(),
- )
- .unwrap();
+ let b = Binding::new(StepKind::Given, r"i am tomjon", true, HashMap::new()).unwrap();
assert!(b.match_with_step(&step).is_none());
}
}
@@ -623,17 +569,14 @@ fn from_hashmap(parsed: &ParsedBinding) -> Result<Binding> {
?kind,
?pattern,
?types,
- "Successfully prepared binding"
+ "Successfully acquired binding"
);
- Binding::new(
- kind,
- &pattern,
- &parsed.function,
- parsed.cleanup.as_deref(),
- parsed.case_sensitive,
- types,
- )
+ let mut ret = Binding::new(kind, &pattern, parsed.case_sensitive, types)?;
+ event!(Level::TRACE, "Binding parsed OK");
+ ret.add_impl("", &parsed.function, parsed.cleanup.as_deref());
+
+ Ok(ret)
}
#[cfg(test)]
@@ -658,8 +601,6 @@ mod test_bindings {
let binding = Binding::new(
StepKind::Given,
r"I am (?P<name>\S+)",
- "set_name",
- None,
false,
HashMap::new(),
)
@@ -730,15 +671,7 @@ mod test_bindings {
#[test]
fn does_not_find_match_for_unmatching_kind() {
let step = ScenarioStep::new(StepKind::Given, "given", "I am Tomjon");
- let binding = Binding::new(
- StepKind::When,
- r"I am Tomjon",
- "set_foo",
- None,
- false,
- HashMap::new(),
- )
- .unwrap();
+ let binding = Binding::new(StepKind::When, r"I am Tomjon", false, HashMap::new()).unwrap();
let mut bindings = Bindings::new();
bindings.add(binding);
assert!(matches!(
@@ -753,8 +686,6 @@ mod test_bindings {
let binding = Binding::new(
StepKind::Given,
r"I am Tomjon of Lancre",
- "set_foo",
- None,
false,
HashMap::new(),
)
@@ -771,24 +702,12 @@ mod test_bindings {
fn two_matching_bindings() {
let step = ScenarioStep::new(StepKind::Given, "given", "I am Tomjon");
let mut bindings = Bindings::default();
- bindings.add(
- Binding::new(
- StepKind::Given,
- r"I am Tomjon",
- "set_foo",
- None,
- false,
- HashMap::new(),
- )
- .unwrap(),
- );
+ bindings.add(Binding::new(StepKind::Given, r"I am Tomjon", false, HashMap::new()).unwrap());
bindings.add(
Binding::new(
StepKind::Given,
&super::regex_from_simple_pattern(r"I am {name}", false, &mut HashMap::new())
.unwrap(),
- "set_foo",
- None,
false,
HashMap::new(),
)
@@ -803,15 +722,7 @@ mod test_bindings {
#[test]
fn finds_match_for_fixed_string_pattern() {
let step = ScenarioStep::new(StepKind::Given, "given", "I am Tomjon");
- let binding = Binding::new(
- StepKind::Given,
- r"I am Tomjon",
- "set_name",
- None,
- false,
- HashMap::new(),
- )
- .unwrap();
+ let binding = Binding::new(StepKind::Given, r"I am Tomjon", false, HashMap::new()).unwrap();
let mut bindings = Bindings::new();
bindings.add(binding);
let m = bindings.find(&step).unwrap();
@@ -831,8 +742,6 @@ mod test_bindings {
let binding = Binding::new(
StepKind::Given,
r"I am (?P<name>\S+)",
- "set_name",
- None,
false,
HashMap::new(),
)
diff --git a/src/matches.rs b/src/matches.rs
index f6d10b9..db1bbda 100644
--- a/src/matches.rs
+++ b/src/matches.rs
@@ -65,7 +65,7 @@ pub struct MatchedStep {
pattern: String,
text: String,
parts: Vec<PartialStep>,
- function: String,
+ function: Option<String>,
cleanup: Option<String>,
types: HashMap<String, CaptureType>,
}
@@ -73,13 +73,14 @@ pub struct MatchedStep {
impl MatchedStep {
/// Return a new empty match. Empty means it has no step parts.
pub fn new(binding: &Binding, types: &HashMap<String, CaptureType>) -> MatchedStep {
+ let bimpl = binding.step_impl("");
MatchedStep {
kind: binding.kind(),
pattern: binding.pattern().to_string(),
text: "".to_string(),
parts: vec![],
- function: binding.function().to_string(),
- cleanup: binding.cleanup().map(String::from),
+ function: bimpl.clone().map(|b| b.function().to_owned()),
+ cleanup: bimpl.and_then(|b| b.cleanup().map(String::from)),
types: types.clone(),
}
}
@@ -90,8 +91,8 @@ impl MatchedStep {
}
/// The name of the function to call for the step.
- pub fn function(&self) -> &str {
- &self.function
+ pub fn function(&self) -> Option<&str> {
+ self.function.as_deref()
}
/// Append a partial step to the match.