use crate::Result;
use crate::Scenario;
use crate::StepKind;
use crate::{bindings::CaptureType, Bindings};
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct MatchedScenario {
title: String,
steps: Vec<MatchedStep>,
}
impl MatchedScenario {
pub fn new(scen: &Scenario, bindings: &Bindings) -> Result<MatchedScenario> {
let steps: Result<Vec<MatchedStep>> = scen
.steps()
.iter()
.map(|step| bindings.find(step))
.collect();
Ok(MatchedScenario {
title: scen.title().to_string(),
steps: steps?,
})
}
pub fn steps(&self) -> &[MatchedStep] {
&self.steps
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MatchedStep {
kind: StepKind,
text: String,
parts: Vec<PartialStep>,
function: String,
cleanup: Option<String>,
types: HashMap<String, CaptureType>,
}
impl MatchedStep {
pub fn new(
kind: StepKind,
function: &str,
cleanup: Option<&str>,
types: &HashMap<String, CaptureType>,
) -> MatchedStep {
MatchedStep {
kind,
text: "".to_string(),
parts: vec![],
function: function.to_string(),
cleanup: cleanup.map(String::from),
types: types.clone(),
}
}
pub fn kind(&self) -> StepKind {
self.kind
}
pub fn function(&self) -> &str {
&self.function
}
pub fn append_part(&mut self, part: PartialStep) {
self.parts.push(part);
self.text = self.text();
}
pub fn parts(&self) -> impl Iterator<Item = &PartialStep> {
self.parts.iter()
}
fn text(&self) -> String {
let mut t = String::new();
for part in self.parts() {
t.push_str(part.as_text());
}
t
}
pub fn types(&self) -> &HashMap<String, CaptureType> {
&self.types
}
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum PartialStep {
UncapturedText(StepSnippet),
CapturedText {
name: String,
text: String,
},
}
impl PartialStep {
pub fn uncaptured(text: &str) -> PartialStep {
PartialStep::UncapturedText(StepSnippet::new(text))
}
pub fn text(name: &str, text: &str) -> PartialStep {
PartialStep::CapturedText {
name: name.to_string(),
text: text.to_string(),
}
}
fn as_text(&self) -> &str {
match self {
PartialStep::UncapturedText(snippet) => snippet.text(),
PartialStep::CapturedText { text, .. } => &text,
}
}
}
#[cfg(test)]
mod test_partial_steps {
use super::PartialStep;
#[test]
fn identical_uncaptured_texts_match() {
let p1 = PartialStep::uncaptured("foo");
let p2 = PartialStep::uncaptured("foo");
assert_eq!(p1, p2);
}
#[test]
fn different_uncaptured_texts_dont_match() {
let p1 = PartialStep::uncaptured("foo");
let p2 = PartialStep::uncaptured("bar");
assert_ne!(p1, p2);
}
#[test]
fn identical_captured_texts_match() {
let p1 = PartialStep::text("xxx", "foo");
let p2 = PartialStep::text("xxx", "foo");
assert_eq!(p1, p2);
}
#[test]
fn different_captured_texts_dont_match() {
let p1 = PartialStep::text("xxx", "foo");
let p2 = PartialStep::text("xxx", "bar");
assert_ne!(p1, p2);
}
#[test]
fn differently_named_captured_texts_dont_match() {
let p1 = PartialStep::text("xxx", "foo");
let p2 = PartialStep::text("yyy", "foo");
assert_ne!(p1, p2);
}
#[test]
fn differently_captured_texts_dont_match() {
let p1 = PartialStep::uncaptured("foo");
let p2 = PartialStep::text("xxx", "foo");
assert_ne!(p1, p2);
}
}
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub struct StepSnippet {
text: String,
}
impl StepSnippet {
pub fn new(text: &str) -> StepSnippet {
StepSnippet {
text: text.to_owned(),
}
}
pub fn text(&self) -> &str {
&self.text
}
}
#[cfg(test)]
mod test {
use super::PartialStep;
#[test]
fn returns_uncaptured() {
let p = PartialStep::uncaptured("foo");
match p {
PartialStep::UncapturedText(s) => assert_eq!(s.text(), "foo"),
_ => panic!("expected UncapturedText: {:?}", p),
}
}
#[test]
fn returns_text() {
let p = PartialStep::text("xxx", "foo");
match p {
PartialStep::CapturedText { name, text } => {
assert_eq!(name, "xxx");
assert_eq!(text, "foo");
}
_ => panic!("expected CapturedText: {:?}", p),
}
}
}