#![warn(clippy::all, clippy::pedantic)]
mod collector;
mod doc_entry;
mod json_writer;
mod story;
mod types;
pub use collector::write_results;
pub struct Feature {
title: String,
kind: Option<String>,
narrative: Option<String>,
tags: Option<Vec<String>>,
glossary: Vec<GlossaryTerm>,
}
pub use types::RawGlossaryTerm as GlossaryTerm;
impl Feature {
#[must_use]
pub fn new(title: &str) -> Self {
Feature {
title: title.to_string(),
kind: None,
narrative: None,
tags: None,
glossary: Vec::new(),
}
}
#[must_use]
pub fn ability(mut self) -> Self {
self.kind = Some("ability".to_string());
self
}
#[must_use]
pub fn business_need(mut self) -> Self {
self.kind = Some("business-need".to_string());
self
}
#[must_use]
pub fn narrative(mut self, narrative: &str) -> Self {
self.narrative = Some(narrative.to_string());
self
}
#[must_use]
pub fn tags(mut self, tags: &[&str]) -> Self {
self.tags = Some(tags.iter().map(|t| (*t).to_string()).collect());
self
}
#[must_use]
pub fn term(mut self, term: &str, definition: &str) -> Self {
self.glossary
.push(GlossaryTerm { term: term.to_string(), definition: definition.to_string() });
self
}
pub fn declare(self, source_file: &str) {
collector::record_feature(types::RawFeature {
source_file: Some(source_file.to_string()),
title: self.title,
kind: self.kind,
narrative: self.narrative,
tags: self.tags,
glossary: if self.glossary.is_empty() { None } else { Some(self.glossary) },
});
}
}
pub use doc_entry::{DocEntry, HtmlOptions};
pub use json_writer::write_raw_run as write_raw_run_to_path;
pub use story::{StepDoc, Story};
pub use types::*;
#[macro_export]
macro_rules! declare_feature {
($feature:expr $(,)?) => {
#[test]
fn declares_the_feature_for_this_file() {
$feature.declare(file!());
}
};
}
#[cfg(test)]
mod source_file_tests {
use crate::Story;
#[test]
fn story_records_the_calling_file() {
let story = Story::new("a scenario declared in lib.rs");
let file = story.source_file.clone().expect("source file recorded");
assert!(file.ends_with("lib.rs"), "recorded {file}, want this file");
}
}