pcsc-mon 0.1.1

Monitor PC/SC smart card readers with hotplug and card event support
Documentation
// cargo run --bin pcsc_diag
use pcsc::{Context, ReaderState, Scope, State, Error};
use std::{ffi::CString, time::Duration, thread};

fn main() {
    // make all panics and logs visible
    std::panic::set_hook(Box::new(|info| eprintln!("[PANIC] {info}")));
    eprintln!("[diag] starting…");

    // 1) establish context
    let ctx = match Context::establish(Scope::User) {
        Ok(c) => {
            eprintln!("[diag] Context established.");
            c
        }
        Err(e) => {
            eprintln!("[diag] Context establish FAILED: {e:?}");
            return;
        }
    };

    // 2) persistent reader name buffer (must outlive the iterator)
    let mut readers_buf = [0u8; 2048];

    // 3) main loop (prove we enter it)
    let mut tick: u64 = 0;
    loop {
        tick += 1;
        if tick % 10 == 0 {
            eprintln!("[diag] heartbeat tick={tick}");
        }

        // 3a) list readers
        match ctx.list_readers(&mut readers_buf) {
            Ok(names) => {
                let readers: Vec<_> = names.map(|n| n.to_string_lossy().into_owned()).collect();
                if readers.is_empty() {
                    eprintln!("[diag] No readers attached. Sleeping…");
                    thread::sleep(Duration::from_millis(500));
                    continue;
                }
                eprintln!("[diag] Readers: {:?}", readers);

                // 3b) build ReaderState structs from names (UNAWARE on first pass)
                let mut states: Vec<ReaderState> = readers.iter()
                    .map(|name| {
                        let c = CString::new(name.as_str()).expect("CString::new");
                        ReaderState::new(c, State::UNAWARE)
                    })
                    .collect();

                // 3c) wait for status change (or timeout)
                match ctx.get_status_change(Some(Duration::from_secs(1)), &mut states) {
                    Ok(_) => {
                        for (i, st) in states.iter().enumerate() {
                            let ev = st.event_state();
                            let cur = st.current_state();
                            eprintln!("[diag] {} : event={:?} current={:?}",
                                readers[i], ev, cur);

                            // example: connect when PRESENT & not INUSE
                            if ev.contains(State::PRESENT) && !ev.contains(State::INUSE) {
                                match ctx.connect(st.name(), pcsc::ShareMode::Shared, pcsc::Protocols::ANY) {
                                    Ok(_card) => eprintln!("[diag] Connected to {}", readers[i]),
                                    Err(e) => eprintln!("[diag] connect error on {}: {:?}", readers[i], e),
                                }
                            }
                        }
                    }
                    Err(e) if e == Error::Timeout
                           || e == Error::Cancelled
                           || e == Error::NoService => {
                        // benign under some conditions; keep looping
                        // Note: cppvsdbg often shows first-chance RPC exceptions here.
                    }
                    Err(e) => {
                        eprintln!("[diag] get_status_change ERROR: {e:?} (continuing)");
                        thread::sleep(Duration::from_millis(200));
                    }
                }
            }
            Err(e) if e == Error::NoReadersAvailable => {
                eprintln!("[diag] NoReadersAvailable (normal if nothing is plugged). Sleeping…");
                thread::sleep(Duration::from_millis(500));
            }
            Err(e) => {
                eprintln!("[diag] list_readers ERROR: {e:?} (continuing)");
                thread::sleep(Duration::from_millis(500));
            }
        }
    }
}