use crate::location::FilePath;
use crate::rule_id::RuleId;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Fact {
pub rule_id: RuleId,
pub file: FilePath,
pub kind: String,
pub data: String,
pub sequence: u32,
}
pub fn sort(facts: &mut [Fact]) {
facts.sort_by(|a, b| {
a.rule_id
.cmp(&b.rule_id)
.then_with(|| a.file.cmp(&b.file))
.then_with(|| a.sequence.cmp(&b.sequence))
});
}
#[cfg(test)]
mod tests {
use super::*;
fn fact(rule: &str, file: &str, sequence: u32) -> Fact {
Fact {
rule_id: rule.parse().expect("valid id"),
file: FilePath::new(file),
kind: "export".to_owned(),
data: format!(r#"{{"kind":"export","n":{sequence}}}"#),
sequence,
}
}
fn order(facts: &[Fact]) -> Vec<(String, String, u32)> {
facts
.iter()
.map(|f| {
(
f.rule_id.to_string(),
f.file.as_str().to_owned(),
f.sequence,
)
})
.collect()
}
#[test]
fn orders_by_rule_then_file_then_sequence() {
let mut facts = vec![
fact("local/b", "a.ts", 0),
fact("local/a", "b.ts", 1),
fact("local/a", "a.ts", 1),
fact("local/a", "b.ts", 0),
fact("local/a", "a.ts", 0),
];
sort(&mut facts);
assert_eq!(
order(&facts),
vec![
("local/a".to_owned(), "a.ts".to_owned(), 0),
("local/a".to_owned(), "a.ts".to_owned(), 1),
("local/a".to_owned(), "b.ts".to_owned(), 0),
("local/a".to_owned(), "b.ts".to_owned(), 1),
("local/b".to_owned(), "a.ts".to_owned(), 0),
]
);
}
#[test]
fn emission_order_within_a_file_survives() {
let mut facts = vec![fact("local/a", "a.ts", 2), fact("local/a", "a.ts", 0)];
sort(&mut facts);
assert_eq!(
facts.iter().map(|f| f.sequence).collect::<Vec<_>>(),
vec![0, 2]
);
}
#[test]
fn the_order_does_not_depend_on_the_order_facts_arrived_in() {
let canonical = {
let mut facts = vec![
fact("local/a", "a.ts", 0),
fact("local/a", "b.ts", 0),
fact("local/b", "a.ts", 0),
];
sort(&mut facts);
order(&facts)
};
let mut shuffled = vec![
fact("local/b", "a.ts", 0),
fact("local/a", "b.ts", 0),
fact("local/a", "a.ts", 0),
];
sort(&mut shuffled);
assert_eq!(order(&shuffled), canonical);
let mut reversed = vec![
fact("local/a", "b.ts", 0),
fact("local/b", "a.ts", 0),
fact("local/a", "a.ts", 0),
];
sort(&mut reversed);
assert_eq!(order(&reversed), canonical);
}
#[test]
fn sorting_is_stable_for_facts_that_compare_equal() {
let mut first = fact("local/a", "a.ts", 0);
first.data = r#"{"kind":"export","tag":"first"}"#.to_owned();
let mut second = fact("local/a", "a.ts", 0);
second.data = r#"{"kind":"export","tag":"second"}"#.to_owned();
let mut facts = vec![first, second];
sort(&mut facts);
assert!(facts[0].data.contains("first"), "stability was lost");
}
}