rust-switcher 1.0.9

Windows keyboard layout switcher and text conversion utility
Documentation
use std::sync::{Mutex, MutexGuard, OnceLock};

use crate::input::ring_buffer::{self, InputRun, LayoutTag, RunKind, RunOrigin};

fn test_lock() -> MutexGuard<'static, ()> {
    static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
    LOCK.get_or_init(|| Mutex::new(()))
        .lock()
        .unwrap_or_else(|e| e.into_inner())
}

#[test]
fn run_journal_merges_contiguous_same_metadata() {
    let _guard = test_lock();
    ring_buffer::invalidate();
    ring_buffer::push_run(InputRun {
        text: "ab".to_string(),
        layout: LayoutTag::En,
        origin: RunOrigin::Physical,
        kind: RunKind::Text,
    });
    ring_buffer::push_run(InputRun {
        text: "cd".to_string(),
        layout: LayoutTag::En,
        origin: RunOrigin::Physical,
        kind: RunKind::Text,
    });

    let runs = ring_buffer::runs_snapshot();
    assert_eq!(runs.len(), 1);
    assert_eq!(runs[0].text, "abcd");
}

#[test]
fn run_journal_splits_on_layout_change() {
    let _guard = test_lock();
    ring_buffer::invalidate();
    ring_buffer::push_run(InputRun {
        text: "ABC".to_string(),
        layout: LayoutTag::En,
        origin: RunOrigin::Physical,
        kind: RunKind::Text,
    });
    ring_buffer::push_run(InputRun {
        text: ",".to_string(),
        layout: LayoutTag::Ru,
        origin: RunOrigin::Physical,
        kind: RunKind::Text,
    });

    let runs = ring_buffer::runs_snapshot();
    assert_eq!(runs.len(), 2);
    assert_eq!(runs[0].layout, LayoutTag::En);
    assert_eq!(runs[1].layout, LayoutTag::Ru);
}

#[test]
fn take_last_layout_run_with_suffix_basic() {
    let _guard = test_lock();
    ring_buffer::invalidate();
    ring_buffer::push_run(InputRun {
        text: "hello".to_string(),
        layout: LayoutTag::En,
        origin: RunOrigin::Physical,
        kind: RunKind::Text,
    });
    ring_buffer::push_run(InputRun {
        text: "   ".to_string(),
        layout: LayoutTag::En,
        origin: RunOrigin::Physical,
        kind: RunKind::Whitespace,
    });

    let (run, suffix) = ring_buffer::take_last_layout_run_with_suffix().expect("payload");
    assert_eq!(run.text, "hello");
    assert_eq!(suffix.len(), 1);
    assert_eq!(suffix[0].text, "   ");
}

#[test]
fn take_last_layout_run_with_suffix_returns_none_for_whitespace_only() {
    let _guard = test_lock();
    ring_buffer::invalidate();
    ring_buffer::push_text("  \t\n");
    assert!(ring_buffer::take_last_layout_run_with_suffix().is_none());
}

#[test]
fn legacy_push_text_segments_internally() {
    let _guard = test_lock();
    ring_buffer::invalidate();
    ring_buffer::push_text("hello world   ");

    let (run, suffix) = ring_buffer::take_last_layout_run_with_suffix().expect("payload");
    assert_eq!(run.text, "world");
    assert_eq!(
        suffix.iter().map(|r| r.text.as_str()).collect::<String>(),
        "   "
    );
}

#[test]
fn last_char_triggers_autoconvert_still_works_across_runs() {
    let _guard = test_lock();
    ring_buffer::invalidate();
    ring_buffer::push_run(InputRun {
        text: "abc".to_string(),
        layout: LayoutTag::En,
        origin: RunOrigin::Physical,
        kind: RunKind::Text,
    });
    ring_buffer::push_run(InputRun {
        text: ".".to_string(),
        layout: LayoutTag::En,
        origin: RunOrigin::Physical,
        kind: RunKind::Text,
    });
    assert!(ring_buffer::last_char_triggers_autoconvert());

    ring_buffer::push_run(InputRun {
        text: " ".to_string(),
        layout: LayoutTag::En,
        origin: RunOrigin::Physical,
        kind: RunKind::Whitespace,
    });
    assert!(ring_buffer::last_char_triggers_autoconvert());
}

#[test]
fn backspace_removes_from_last_run_and_drops_empty_run() {
    let _guard = test_lock();
    ring_buffer::invalidate();
    ring_buffer::push_text("a");
    ring_buffer::test_backspace();
    assert!(ring_buffer::take_last_layout_run_with_suffix().is_none());
}

#[test]
fn foreground_invalidation_state_reset_via_invalidate() {
    let _guard = test_lock();
    ring_buffer::invalidate();
    ring_buffer::push_text("abc");
    ring_buffer::mark_last_token_autoconverted();
    assert!(ring_buffer::last_token_autoconverted());

    ring_buffer::invalidate();
    assert!(!ring_buffer::last_token_autoconverted());
    assert!(ring_buffer::take_last_layout_run_with_suffix().is_none());
}