#![cfg(feature = "cli")]
use rusty_figlet::{FigletBuilder, LayoutFlag, LayoutFlags};
fn one_row_flf_with_rules(rules: u8) -> Vec<u8> {
let full_layout: u32 = 64 | u32::from(rules);
let old_layout: i32 = i32::from(rules);
let mut out = String::new();
out.push_str(&format!("flf2a$ 1 1 2 {old_layout} 2 0 {full_layout} 7\n"));
out.push_str("smush rules fixture (T105)\n");
out.push_str("comment line 2\n");
let endmark = '@';
for cp in 32..=126u32 {
let c = char::from_u32(cp).unwrap();
out.push_str(&format!("{c}{endmark}{endmark}\n"));
}
for cp in [196u32, 214, 220, 228, 246, 252, 223] {
out.push_str(&format!("{cp:X} FIXTURE U+{cp:04X}\n"));
out.push_str(&format!("X{endmark}{endmark}\n"));
}
out.into_bytes()
}
fn render_pair(rules: u8, left: char, right: char) -> String {
let bytes = one_row_flf_with_rules(rules);
let layout = LayoutFlags {
flags: vec![LayoutFlag::Explicit(i32::from(rules))],
};
let banner = FigletBuilder::new()
.font_bytes(&bytes)
.width(80)
.layout(layout)
.build()
.expect("font_bytes -> build")
.render(&format!("{left}{right}"))
.expect("render");
let mut it = banner.lines();
it.next().unwrap_or_default()
}
const RULE_EQUAL: u8 = 0b0000_0001;
const RULE_UNDERSCORE: u8 = 0b0000_0010;
const RULE_HIERARCHY: u8 = 0b0000_0100;
const RULE_OPPOSITE: u8 = 0b0000_1000;
const RULE_BIGX: u8 = 0b0001_0000;
const RULE_HARDBLANK: u8 = 0b0010_0000;
#[test]
fn rule1_equal_matching_pair_merges_to_single_char() {
let row = render_pair(RULE_EQUAL, '|', '|');
assert!(row.contains('|'), "expected merged '|', got: {row:?}");
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['|'], "rule 1 must collapse the pair");
}
#[test]
fn rule1_equal_non_matching_pair_does_not_merge() {
let row = render_pair(RULE_EQUAL, '|', '-');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert!(
chars.contains(&'|') && chars.contains(&'-'),
"expected both '|' and '-', got: {chars:?}"
);
}
#[test]
fn rule1_equal_hardblank_adjacent_abstains() {
let row = render_pair(RULE_EQUAL, '$', '$');
let non_space: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert!(
non_space.is_empty(),
"rule 1 must not merge hardblanks (they print as spaces); got: {row:?}"
);
}
#[test]
fn rule2_underscore_replaced_by_visible_neighbor() {
let row = render_pair(RULE_UNDERSCORE, '_', '|');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['|'], "rule 2 must yield '|' (the visible)");
}
#[test]
fn rule2_underscore_non_matching_pair() {
let row = render_pair(RULE_UNDERSCORE, '_', 'a');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert!(
chars.contains(&'_') && chars.contains(&'a'),
"rule 2 abstains for `_a`; got: {chars:?}"
);
}
#[test]
fn rule2_underscore_hardblank_adjacent_abstains() {
let row = render_pair(RULE_UNDERSCORE, '_', '$');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['_']);
}
#[test]
fn rule3_hierarchy_higher_class_wins() {
let row = render_pair(RULE_HIERARCHY, '(', '|');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['('], "rule 3 must pick the higher class");
}
#[test]
fn rule3_hierarchy_same_class_abstains() {
let row = render_pair(RULE_HIERARCHY, '|', '|');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['|', '|']);
}
#[test]
fn rule3_hierarchy_hardblank_adjacent_abstains() {
let row = render_pair(RULE_HIERARCHY, '$', '|');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['|']);
}
#[test]
fn rule4_opposite_pair_yields_pipe() {
let row = render_pair(RULE_OPPOSITE, '[', ']');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['|'], "rule 4 must yield '|'");
}
#[test]
fn rule4_opposite_pair_same_direction_abstains() {
let row = render_pair(RULE_OPPOSITE, '[', '[');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['[', '[']);
}
#[test]
fn rule4_opposite_pair_hardblank_adjacent_abstains() {
let row = render_pair(RULE_OPPOSITE, '$', '[');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['[']);
}
#[test]
fn rule5_bigx_slash_backslash_yields_pipe() {
let row = render_pair(RULE_BIGX, '/', '\\');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['|'], "rule 5: /\\ -> |");
}
#[test]
fn rule5_bigx_non_pair_abstains() {
let row = render_pair(RULE_BIGX, '/', '/');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['/', '/']);
}
#[test]
fn rule5_bigx_hardblank_adjacent_abstains() {
let row = render_pair(RULE_BIGX, '$', '/');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['/']);
}
#[test]
fn rule6_hardblank_pair_merges() {
let row = render_pair(RULE_HARDBLANK, '$', '$');
let non_space: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert!(
non_space.is_empty(),
"rule 6 yields one merged hardblank → all-space row; got: {row:?}"
);
}
#[test]
fn rule6_hardblank_letter_pair_abstains() {
let row = render_pair(RULE_HARDBLANK, 'a', 'a');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['a', 'a']);
}
#[test]
fn rule6_hardblank_visible_paired_with_hardblank_abstains() {
let row = render_pair(RULE_HARDBLANK, '$', 'a');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['a']);
}
#[test]
fn cross_rules_eq_over_underscore() {
let row = render_pair(RULE_EQUAL | RULE_UNDERSCORE, '_', '_');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['_']);
}
#[test]
fn cross_rules_underscore_over_hierarchy() {
let row = render_pair(RULE_UNDERSCORE | RULE_HIERARCHY, '_', '|');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['|']);
}
#[test]
fn cross_rules_hierarchy_over_opposite() {
let row = render_pair(RULE_HIERARCHY | RULE_OPPOSITE, '(', '|');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['(']);
}
#[test]
fn cross_rules_opposite_over_bigx() {
let row = render_pair(RULE_OPPOSITE | RULE_BIGX, ')', '(');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['|']);
}
#[test]
fn cross_rules_bigx_over_hardblank() {
let row = render_pair(RULE_BIGX | RULE_HARDBLANK, '/', '\\');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['|']);
}
#[test]
fn cross_rules_all_six_letter_pair_yields_equal() {
let all_six =
RULE_EQUAL | RULE_UNDERSCORE | RULE_HIERARCHY | RULE_OPPOSITE | RULE_BIGX | RULE_HARDBLANK;
let row = render_pair(all_six, 'a', 'a');
let chars: Vec<char> = row.chars().filter(|c| *c != ' ').collect();
assert_eq!(chars, vec!['a']);
}