use crate::Binding;
use crate::Scenario;
use crate::StepKind;
use crate::SubplotError;
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(
template: &str,
scen: &Scenario,
bindings: &Bindings,
) -> Result<MatchedScenario, SubplotError> {
let steps: Result<Vec<MatchedStep>, SubplotError> = scen
.steps()
.iter()
.map(|step| bindings.find(template, step))
.collect();
Ok(MatchedScenario {
title: scen.title().to_string(),
steps: steps?,
})
}
pub fn steps(&self) -> &[MatchedStep] {
&self.steps
}
pub fn title(&self) -> &str {
&self.title
}
}
#[derive(Debug)]
pub struct MatchedSteps {
matches: Vec<MatchedStep>,
}
impl MatchedSteps {
pub fn new(matches: Vec<MatchedStep>) -> Self {
Self { matches }
}
}
impl std::fmt::Display for MatchedSteps {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
let matches: Vec<String> = self.matches.iter().map(|m| m.pattern()).collect();
write!(f, "{}", matches.join("\n"))
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MatchedStep {
kind: StepKind,
pattern: String,
text: String,
parts: Vec<PartialStep>,
function: Option<String>,
cleanup: Option<String>,
types: HashMap<String, CaptureType>,
}
impl MatchedStep {
pub fn new(binding: &Binding, template: &str) -> MatchedStep {
let bimpl = binding.step_impl(template);
MatchedStep {
kind: binding.kind(),
pattern: binding.pattern().to_string(),
text: "".to_string(),
parts: vec![],
function: bimpl.clone().map(|b| b.function().to_owned()),
cleanup: bimpl.and_then(|b| b.cleanup().map(String::from)),
types: binding.types().map(|(s, c)| (s.to_string(), c)).collect(),
}
}
pub fn kind(&self) -> StepKind {
self.kind
}
pub fn function(&self) -> Option<&str> {
self.function.as_deref()
}
pub fn append_part(&mut self, part: PartialStep) {
self.parts.push(part);
self.text = self.update_text();
}
pub fn parts(&self) -> impl Iterator<Item = &PartialStep> {
self.parts.iter()
}
fn pattern(&self) -> String {
self.pattern.to_string()
}
fn update_text(&self) -> String {
let mut t = String::new();
for part in self.parts() {
t.push_str(part.as_text());
}
t
}
pub fn text(&self) -> &str {
&self.text
}
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),
}
}
}