icsneoc2 0.1002002.0-rc.4

High-level Rust interface for Intrepid Control Systems vehicle network adapters
Documentation
use icsneoc2::{Device, DiskFormatFlags, DiskLayout, OpenOptions};
use std::io::{self, BufRead, Write};

unsafe extern "C" fn format_progress(
    sectors_formatted: u64,
    total_sectors: u64,
    user_data: *mut std::ffi::c_void,
) -> u8 {
    let pct = if total_sectors > 0 {
        100 * sectors_formatted / total_sectors
    } else {
        0
    };
    if !user_data.is_null() {
        let device = unsafe { &*(user_data as *const Device) };
        let name = device
            .description()
            .unwrap_or_else(|_| "unknown".to_string());
        print!(
            "\r  [{name}] Progress: {} / {} sectors  ({}%)",
            sectors_formatted, total_sectors, pct
        );
    } else {
        print!(
            "\r  Progress: {} / {} sectors  ({}%)",
            sectors_formatted, total_sectors, pct
        );
    }
    let _ = io::stdout().flush();
    0 // Continue
}

fn main() -> anyhow::Result<()> {
    // Open first device with minimal options (don't go online for formatting)
    println!("Opening first available device...");
    let options = OpenOptions::NONE;
    let device = Device::open_first(0, options)?;
    let desc = device.description()?;
    println!("\tOpened device: {desc}");

    // Check disk formatting support
    let supported = device.supports_disk_formatting()?;
    if !supported {
        println!("\terror: {desc} does not support disk formatting");
        return Ok(());
    }

    let disk_count = device.disk_count()?;
    println!("\tDisk count: {disk_count}");

    // Query disk details
    print!("\tQuerying disk details... ");
    let details = device.disk_details()?;
    println!("OK");

    // Display current state
    let layout = details.layout()?;
    let layout_name = match layout {
        DiskLayout::Raid0 => "RAID0",
        DiskLayout::Spanned => "Spanned",
        _ => "Unknown",
    };
    println!("\t  Layout        : {layout_name}");

    let detail_count = details.count()?;
    let mut any_present = false;

    for i in 0..detail_count {
        let flags = details.flags(i)?;
        println!("\t  Disk [{i}]:");
        println!(
            "\t    Present     : {}",
            if flags.contains(DiskFormatFlags::PRESENT) {
                "yes"
            } else {
                "no"
            }
        );
        println!(
            "\t    Initialized : {}",
            if flags.contains(DiskFormatFlags::INITIALIZED) {
                "yes"
            } else {
                "no"
            }
        );
        println!(
            "\t    Formatted   : {}",
            if flags.contains(DiskFormatFlags::FORMATTED) {
                "yes"
            } else {
                "no"
            }
        );
        if flags.contains(DiskFormatFlags::PRESENT) {
            let sz = details.size(i)?;
            let mb = (sz.sectors * sz.bytes_per_sector) / (1024 * 1024);
            println!(
                "\t    Size        : {} MB ({} sectors x {} bytes)",
                mb, sz.sectors, sz.bytes_per_sector
            );
        }
    }

    // Build format config: mark present disks for formatting
    for i in 0..detail_count {
        let flags = details.flags(i)?;
        if flags.contains(DiskFormatFlags::PRESENT) {
            details.set_flags(i, flags | DiskFormatFlags::FORMATTED)?;
            any_present = true;
        }
    }
    details.set_full_format(false)?; // Quick format

    if !any_present {
        println!("\n\terror: no disks are present in the device");
        return Ok(());
    }

    // Confirm
    println!("\n\tThis will format the disk(s) in {desc}.");
    print!("\tAll existing data will be lost. Continue? [y/N]: ");
    io::stdout().flush().unwrap();

    let mut input = String::new();
    io::stdin().lock().read_line(&mut input).unwrap();
    let input = input.trim();
    if !input.eq_ignore_ascii_case("y") {
        println!("\tAborted.");
        return Ok(());
    }

    // Format
    println!("\n\tStarting format...");
    unsafe {
        let user_data = &device as *const Device as *mut std::ffi::c_void;
        device.format_disk(&details, Some(format_progress), user_data)?;
    }
    println!("\n\tFormat complete!");

    // Verify
    print!("\n\tVerifying disk state after format... ");
    let post_details = device.disk_details()?;
    println!("OK");

    let post_count = post_details.count()?;
    for i in 0..post_count {
        let flags = post_details.flags(i)?;
        println!(
            "\t  Disk [{i}] formatted: {}",
            if flags.contains(DiskFormatFlags::FORMATTED) {
                "yes"
            } else {
                "no"
            }
        );
    }

    println!("\tClosing device: {desc}...");
    Ok(())
}