use brlapi::{Connection, TtyMode, text::CursorPosition};
use std::thread::sleep;
use std::time::Duration;
fn main() -> Result<(), brlapi::BrlApiError> {
println!("Braille Contraction Demonstration");
println!("=================================");
println!();
let connection = Connection::open()?;
println!("Connected to BRLTTY daemon");
let display = brlapi::Display::from_connection(&connection)?;
println!("Display: {}", display);
println!();
connection.debug_preferences();
println!();
let (tty_mode, tty_number) = TtyMode::enter_auto(&connection, None)?;
println!("Using TTY: {}", tty_number);
let writer = tty_mode.writer();
let test_phrases = [
"Hello world and welcome to the program",
"The quick brown fox jumps over the lazy dog",
"I can see you and the other people",
"This is a test of the emergency broadcast system",
"For more information please visit our website",
];
println!("Demonstrating contracted vs uncontracted braille:");
println!("Press Ctrl+C to exit at any time");
println!();
for (i, phrase) in test_phrases.iter().enumerate() {
println!("Demo {}: {}", i + 1, phrase);
println!(" Writing uncontracted...");
writer.write_computer_braille(phrase, CursorPosition::Off)?;
sleep(Duration::from_secs(3));
println!(" Writing contracted (user's preferred table)...");
match writer.write_contracted_user_preference(phrase, CursorPosition::Off) {
Ok(()) => {
println!(" [SUCCESS] Contraction successful using user preference");
}
Err(e) => {
println!(
" [FALLBACK] Contraction failed ({}), showing uncontracted",
e
);
writer.write_computer_braille(phrase, CursorPosition::Off)?;
}
}
sleep(Duration::from_secs(3));
let user_table = connection
.user_contraction_table()
.unwrap_or_else(|_| "en-us-g2.ctb".to_string());
match liblouis::util::translate_text(&user_table, phrase) {
Ok(contracted) => {
println!(" Original: '{}'", phrase);
println!(" Contracted: '{}'", contracted);
println!(
" Saved {} characters ({:.1}% compression)",
phrase.len() - contracted.len(),
100.0 * (1.0 - (contracted.len() as f32 / phrase.len() as f32))
);
}
Err(e) => {
println!(" Translation comparison failed: {}", e);
}
}
println!();
sleep(Duration::from_secs(1));
}
println!("Demonstrating liblouis table discovery:");
use liblouis::TableResolver;
match TableResolver::index_tables() {
Ok(()) => {
println!(" [SUCCESS] Table discovery initialized");
let queries = [
("English Grade 2", "language:en grade:2"),
("Any Grade 2", "grade:2"),
("English Language", "language:en"),
("US English Grade 2", "language:en region:US grade:2"),
];
for (description, query) in &queries {
match TableResolver::find_table(query) {
Ok(Some(table)) => println!(" {} query '{}' found: {}", description, query, table),
Ok(None) => println!(" {} query '{}' found no matches", description, query),
Err(e) => println!(" {} query '{}' failed: {}", description, query, e),
}
}
println!(" Testing locale resolution:");
let locales = ["en_US", "en_GB", "fr_FR", "de_DE", "unknown"];
for locale in &locales {
match TableResolver::resolve_locale(locale) {
Ok(table) => println!(" {} -> {}", locale, table),
Err(e) => println!(" {} failed: {}", locale, e),
}
}
}
Err(e) => {
println!(" [WARNING] Table discovery failed: {}", e);
println!(" Continuing with hardcoded table names...");
}
}
println!();
println!("Demonstrating different contraction tables:");
let demo_text = "Hello and welcome to our program";
let tables = [
("US Grade 2", "en-us-g2.ctb"),
("UK Grade 2", "en-gb-g2.ctb"),
("US Grade 1", "en-us-g1.ctb"),
];
for (name, table) in &tables {
println!(" Testing {}: {}", name, table);
match writer.write_contracted(demo_text, table, CursorPosition::Off) {
Ok(()) => {
println!(" [SUCCESS] {} table working", name);
if let Ok(contracted) = liblouis::util::translate_text(table, demo_text) {
println!(" Result: '{}'", contracted);
}
}
Err(e) => {
println!(" [ERROR] {} table failed: {}", name, e);
}
}
sleep(Duration::from_secs(2));
}
println!();
println!("Demonstrating cursor position tracking with contractions:");
let cursor_demo_text = "The quick brown fox";
let original_cursor_pos = 10;
let cursor_table = connection
.user_contraction_table()
.unwrap_or_else(|_| "en-us-g2.ctb".to_string());
match writer.write_contracted_with_cursor_tracking(
cursor_demo_text,
&cursor_table,
original_cursor_pos,
) {
Ok(new_cursor_pos) => {
println!(" Original text: '{}'", cursor_demo_text);
println!(
" Original cursor at position {} (character '{}')",
original_cursor_pos,
cursor_demo_text
.chars()
.nth(original_cursor_pos)
.unwrap_or('?')
);
println!(" New cursor position: {}", new_cursor_pos);
if let Ok(contracted) = liblouis::util::translate_text(&cursor_table, cursor_demo_text) {
println!(" Contracted text: '{}'", contracted);
if let Some(cursor_char) = contracted.chars().nth(new_cursor_pos) {
println!(" Cursor now at character: '{}'", cursor_char);
}
}
}
Err(e) => {
println!(" Cursor tracking failed: {}", e);
}
}
sleep(Duration::from_secs(3));
println!();
println!("Demonstration complete!");
writer
.write_contracted_en_us_g2(
"Contraction demo complete - thank you for watching!",
CursorPosition::Off,
)
.unwrap_or_else(|_| {
let _ = writer.write_text("Contraction demo complete - thank you for watching!");
});
println!();
println!("Key points demonstrated:");
println!("- BrlAPI requires CLIENT-SIDE contraction processing");
println!("- liblouis provides the contraction engine (same as Orca uses)");
println!("- Contractions significantly reduce braille text length");
println!("- Cursor positions are properly mapped through contractions");
println!("- Multiple contraction tables are supported");
println!("- Graceful fallback to uncontracted text when needed");
Ok(())
}