use std::path::Path;
use crate::types::{Clause, ClauseId, Keyword, SourceLocation};
use super::ids::{content_hash, slugify};
use super::state::PendingItem;
pub(super) fn build_clauses(
file: &Path,
spec_name: &str,
section_path: &[String],
item: PendingItem,
given_condition: Option<String>,
) -> Vec<Clause> {
let mut result = Vec::new();
match item.keyword {
Keyword::Given => {
let condition = Some(item.text.clone());
for nested in item.nested_items {
let nested_clauses =
build_clauses(file, spec_name, section_path, nested, condition.clone());
result.extend(nested_clauses);
}
}
_ => {
let condition = given_condition;
let keyword_slug = match item.keyword {
Keyword::Must => "must",
Keyword::MustNot => "must_not",
Keyword::Should => "should",
Keyword::ShouldNot => "should_not",
Keyword::May => "may",
Keyword::Wont => "wont",
Keyword::MustAlways => "must_always",
Keyword::MustBy => "must_by",
Keyword::Otherwise => "otherwise",
Keyword::Given => unreachable!(),
};
let text_slug = slugify(&item.text);
let summary = if text_slug.is_empty() {
keyword_slug.to_string()
} else {
format!("{}_{}", keyword_slug, text_slug)
};
let mut id_parts: Vec<&str> = Vec::new();
id_parts.push(spec_name);
for sp in section_path {
id_parts.push(sp);
}
id_parts.push(&summary);
let id_str = id_parts.join("::");
let id_str = if id_str.len() > 120 {
let mut s = id_str[..120].to_string();
while s.ends_with('_') || s.ends_with(':') {
s.pop();
}
s
} else {
id_str
};
let hash = content_hash(item.keyword, &item.text, &condition, item.pending);
let mut otherwise_clauses = Vec::new();
let mut other_nested = Vec::new();
for nested in item.nested_items {
if nested.keyword == Keyword::Otherwise {
let ow_pending = item.pending || nested.pending;
let ow_summary = format!("otherwise_{}", slugify(&nested.text));
let mut ow_id_parts: Vec<&str> = Vec::new();
ow_id_parts.push(spec_name);
for sp in section_path {
ow_id_parts.push(sp);
}
ow_id_parts.push(&ow_summary);
let ow_id_str = ow_id_parts.join("::");
let ow_hash = content_hash(
Keyword::Otherwise,
&nested.text,
&condition,
ow_pending,
);
otherwise_clauses.push(Clause {
id: ClauseId(ow_id_str),
keyword: Keyword::Otherwise,
severity: item.keyword.severity(), text: nested.text,
condition: condition.clone(),
otherwise: Vec::new(),
temporal: None,
hints: nested.hints,
source_location: SourceLocation {
file: file.to_path_buf(),
line: nested.line,
},
content_hash: ow_hash,
pending: ow_pending,
});
} else {
other_nested.push(nested);
}
}
let clause = Clause {
id: ClauseId(id_str),
keyword: item.keyword,
severity: item.keyword.severity(),
text: item.text,
condition,
otherwise: otherwise_clauses,
temporal: item.temporal,
hints: item.hints,
source_location: SourceLocation {
file: file.to_path_buf(),
line: item.line,
},
content_hash: hash,
pending: item.pending,
};
result.push(clause);
for nested in other_nested {
let nested_clauses = build_clauses(file, spec_name, section_path, nested, None);
result.extend(nested_clauses);
}
}
}
result
}