use crate::{Error, Result}; use serde::{Deserialize, Serialize}; use std::fmt; /// A scenario step. /// /// The scenario parser creates these kinds of data structures to /// represent the parsed scenario step. The step consists of a kind /// (expressed as a StepKind), and the text of the step. /// /// This is just the step as it appears in the scenario in the input /// text. It has not been matched with a binding. See MatchedStep for /// that. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct ScenarioStep { kind: StepKind, text: String, } impl ScenarioStep { /// Construct a new step. pub fn new(kind: StepKind, text: &str) -> ScenarioStep { ScenarioStep { kind, text: text.to_owned(), } } /// Return the kind of a step. pub fn kind(&self) -> StepKind { self.kind } /// Return the text of a step. pub fn text(&self) -> &str { &self.text } /// Construct a step from a line in a scenario. pub fn new_from_str(text: &str) -> Result { let mut words = text.split_whitespace(); let kind = match words.next() { Some("given") => StepKind::Given, Some("when") => StepKind::When, Some("then") => StepKind::Then, _ => return Err(Error::UnknownStepKind), }; let mut joined = String::new(); for word in words { joined.push_str(word); joined.push_str(" "); } if joined.len() > 1 { joined.pop(); } Ok(ScenarioStep::new(kind, &joined)) } } impl fmt::Display for ScenarioStep { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{} {}", self.kind(), self.text()) } } /// The kind of scenario step we have: given, when, or then. /// /// This needs to be extended if the Subplot language gets extended with other /// kinds of steps. However, note that the scenario parser will hide aliases, /// such as "and" to mean the same kind as the previous step. #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum StepKind { /// A "given some precondition" step. Given, /// A "when something happens" step. When, /// A "then some condition" step. Then, } impl fmt::Display for StepKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s = match self { StepKind::Given => "given", StepKind::When => "when", StepKind::Then => "then", }; write!(f, "{}", s) } } #[cfg(test)] mod test { use super::{ScenarioStep, StepKind}; #[test] fn parses_given() { let step = ScenarioStep::new_from_str("given I am Tomjon").unwrap(); assert_eq!(step.kind(), StepKind::Given); assert_eq!(step.text(), "I am Tomjon"); } #[test] fn parses_given_with_extra_spaces() { let step = ScenarioStep::new_from_str(" given I am Tomjon ").unwrap(); assert_eq!(step.kind(), StepKind::Given); assert_eq!(step.text(), "I am Tomjon"); } #[test] fn parses_when() { let step = ScenarioStep::new_from_str("when I declare myself king").unwrap(); assert_eq!(step.kind(), StepKind::When); assert_eq!(step.text(), "I declare myself king"); } #[test] fn parses_then() { let step = ScenarioStep::new_from_str("then everyone accepts it").unwrap(); assert_eq!(step.kind(), StepKind::Then); assert_eq!(step.text(), "everyone accepts it"); } }