use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Examples {
pub bad: String,
pub good: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RuleCard {
pub message: String,
pub remediation: String,
pub examples: Examples,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CardProblem {
EmptyMessage,
EmptyRemediation,
EmptyExample,
IdenticalExamples,
}
impl RuleCard {
pub fn validate(&self) -> Result<(), Vec<CardProblem>> {
let mut problems = Vec::new();
if self.message.trim().is_empty() {
problems.push(CardProblem::EmptyMessage);
}
if self.remediation.trim().is_empty() {
problems.push(CardProblem::EmptyRemediation);
}
if self.examples.bad.trim().is_empty() || self.examples.good.trim().is_empty() {
problems.push(CardProblem::EmptyExample);
} else if self.examples.bad.trim() == self.examples.good.trim() {
problems.push(CardProblem::IdenticalExamples);
}
if problems.is_empty() {
Ok(())
} else {
Err(problems)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn card(message: &str, remediation: &str, bad: &str, good: &str) -> RuleCard {
RuleCard {
message: message.to_owned(),
remediation: remediation.to_owned(),
examples: Examples {
bad: bad.to_owned(),
good: good.to_owned(),
},
}
}
fn valid() -> RuleCard {
card(
"Literal numeric size inside makeStyles",
"Use theme.spacing.* instead",
"padding: 12",
"padding: theme.spacing.md",
)
}
#[test]
fn accepts_a_complete_card() {
assert_eq!(valid().validate(), Ok(()));
}
#[test]
fn rejects_an_empty_message() {
let mut c = valid();
c.message = String::new();
assert_eq!(c.validate(), Err(vec![CardProblem::EmptyMessage]));
}
#[test]
fn rejects_an_empty_remediation() {
let mut c = valid();
c.remediation = String::new();
assert_eq!(c.validate(), Err(vec![CardProblem::EmptyRemediation]));
}
#[test]
fn treats_whitespace_as_empty() {
let c = card(" ", "\t\n", " ", " ");
let problems = c.validate().expect_err("should reject");
assert!(problems.contains(&CardProblem::EmptyMessage));
assert!(problems.contains(&CardProblem::EmptyRemediation));
assert!(problems.contains(&CardProblem::EmptyExample));
}
#[test]
fn rejects_examples_that_demonstrate_nothing() {
let c = card("msg", "fix", "padding: 12", "padding: 12");
assert_eq!(c.validate(), Err(vec![CardProblem::IdenticalExamples]));
}
#[test]
fn compares_examples_ignoring_surrounding_whitespace() {
let c = card("msg", "fix", " padding: 12 ", "padding: 12");
assert_eq!(c.validate(), Err(vec![CardProblem::IdenticalExamples]));
}
#[test]
fn reports_every_problem_at_once() {
let c = card("", "", "", "");
let problems = c.validate().expect_err("should reject");
assert_eq!(problems.len(), 3);
}
#[test]
fn empty_examples_are_reported_instead_of_identical() {
let c = card("msg", "fix", "", "");
assert_eq!(c.validate(), Err(vec![CardProblem::EmptyExample]));
}
#[test]
fn round_trips_through_json() {
let c = valid();
let json = serde_json::to_string(&c).expect("serializes");
let back: RuleCard = serde_json::from_str(&json).expect("deserializes");
assert_eq!(back, c);
}
}