use super::graph::Card;
use super::outline::Row;
use super::path::{self, Path};
pub(crate) fn owning_card(cards: &[Card], path: &Path) -> Option<usize> {
cards
.iter()
.enumerate()
.filter(|(_, c)| path::starts_with(path, &c.settles))
.max_by_key(|(_, c)| c.settles.len())
.map(|(i, _)| i)
}
pub(crate) fn own_rows(rows: &[Row], cards: &[Card], card: usize) -> Vec<usize> {
rows.iter()
.enumerate()
.filter(|(_, r)| owning_card(cards, &r.path) == Some(card))
.map(|(i, _)| i)
.collect()
}
#[cfg(test)]
mod tests {
use serde_json::{Value, json};
use super::super::{graph, outline};
use super::*;
fn listed(args: &Value, card: usize) -> Vec<String> {
let rows = outline::rows(args);
let chart = graph::chart(args);
own_rows(&rows, &chart.cards, card)
.into_iter()
.map(|i| rows[i].label.clone())
.collect()
}
#[test]
fn a_node_settles_its_own_fields() {
let args = json!({"do": [{"spawn": {"template": "drop", "lifetime": 4.0}}]});
let listed = listed(&args, 1);
assert_eq!(listed.first().map(String::as_str), Some("spawn"));
for field in ["template", "position", "lifetime"] {
assert!(listed.iter().any(|l| l == field), "{field} in {listed:?}");
}
}
#[test]
fn a_branch_keeps_its_condition_and_nothing_below_it() {
let args = json!({"do": [{"if": {
"cond": {"bool": true},
"then": [{"save": null}],
"else": [],
}}]});
let listed = listed(&args, 1);
assert!(listed.iter().any(|l| l == "cond"));
for below in ["then", "else", "save"] {
assert!(
!listed.iter().any(|l| l == below),
"`{below}` has a card of its own: {listed:?}"
);
}
}
#[test]
fn the_trigger_settles_the_source_and_what_the_behavior_declares() {
let args = json!({
"on": {"timer": {"interval": 5.0, "repeat": true}},
"scope": ["Prop"],
"do": [{"save": null}],
});
let listed = listed(&args, 0);
for want in [
"on", "interval", "repeat", "once", "delay", "cooldown", "scope",
] {
assert!(listed.iter().any(|l| l == want), "{want} in {listed:?}");
}
assert!(!listed.iter().any(|l| l == "do"), "the body is not its own");
}
#[test]
fn every_outline_row_belongs_to_exactly_one_card() {
let args = json!({
"on": "tick",
"scope": ["Prop"],
"locals": [{"name": "speed", "value": {"float": 3.0}}],
"queries": [{"name": "player", "has": ["Camera3D"]}],
"do": [
{"if": {"cond": {"bool": true}, "then": [{"save": null}], "else": []}},
{"spawn": {"template": "drop"}},
],
});
let rows = outline::rows(&args);
let chart = graph::chart(&args);
let stranded: Vec<&str> = rows
.iter()
.filter(|r| owning_card(&chart.cards, &r.path).is_none())
.map(|r| r.label.as_str())
.collect();
assert!(stranded.is_empty(), "no card reaches {stranded:?}");
let listed: usize = (0..chart.cards.len())
.map(|i| own_rows(&rows, &chart.cards, i).len())
.sum();
assert_eq!(listed, rows.len(), "a row is listed twice");
}
#[test]
fn a_node_with_no_fields_lists_only_itself() {
let args = json!({"do": [{"save": null}]});
assert_eq!(listed(&args, 1), ["save"]);
}
#[test]
fn a_field_belongs_to_the_node_holding_it() {
let args = json!({"do": [{"spawn": {"template": "drop", "lifetime": 4.0}}]});
let rows = outline::rows(&args);
let chart = graph::chart(&args);
let node = &chart.cards[1].path;
for label in ["spawn", "lifetime"] {
let row = rows.iter().find(|r| r.label == label).expect(label);
let card = owning_card(&chart.cards, &row.path).expect(label);
assert_eq!(
&chart.cards[card].path, node,
"`{label}` answered elsewhere"
);
}
}
#[test]
fn a_declaration_belongs_to_the_trigger() {
let args = json!({"scope": ["Prop"], "do": [{"save": null}]});
let rows = outline::rows(&args);
let chart = graph::chart(&args);
let scope = rows.iter().find(|r| r.label == "scope").expect("scope");
assert_eq!(owning_card(&chart.cards, &scope.path), Some(0));
assert_eq!(chart.cards[0].kind, graph::CardKind::Trigger);
}
#[test]
fn a_list_belongs_to_the_card_that_appends_to_it() {
let args = json!({"do": [{"save": null}]});
let rows = outline::rows(&args);
let chart = graph::chart(&args);
let body = rows.iter().find(|r| r.label == "do").expect("do");
let card = owning_card(&chart.cards, &body.path).expect("a card reaches it");
assert_eq!(chart.cards[card].kind, graph::CardKind::Add);
assert_eq!(chart.cards[card].path, body.path);
}
}