libusbk 0.2.0

FFI bindings for libusbK.
Documentation
use std::ffi::c_void;
use std::sync::Mutex;

use libusbk_sys::{
    HotK_Free, HotK_Init, KHOT_HANDLE, KHOT_PARAMS, KLST_DEVINFO_HANDLE, KLST_SYNC_FLAG,
};
use once_cell::sync::OnceCell;

use crate::device::{self, Device};
use crate::error::{try_unsafe, Result};

static DATA: OnceCell<Mutex<Option<Data>>> = OnceCell::new();

unsafe impl Send for Data {}

struct Data {
    callback: Box<dyn Hotplug>,
    vid: Option<i32>,
    pid: Option<i32>,
}

pub trait Hotplug: Send {
    fn device_arrived(&mut self, device: Device);
    fn device_left(&mut self, device: Device);
}

#[derive(Debug, Copy, Clone)]
pub struct HotplugBuilder {
    vendor_id: Option<i32>,
    product_id: Option<i32>,
    params: KHOT_PARAMS,
}

impl HotplugBuilder {
    pub fn new() -> Self {
        HotplugBuilder::default()
    }

    pub fn vendor_id(&mut self, vendor_id: i32) -> &mut Self {
        self.vendor_id = Some(vendor_id);
        self
    }

    pub fn product_id(&mut self, product_id: i32) -> &mut Self {
        self.product_id = Some(product_id);
        self
    }

    pub fn register(self, callback: Box<dyn Hotplug>) -> crate::Result<Registration> {
        let v = DATA.get_or_init(|| Mutex::new(None));
        let mut item = v.lock().unwrap();
        *item = Some(Data {
            callback,
            vid: self.vendor_id,
            pid: self.product_id,
        });
        Ok(Registration {
            params: self.params,
            handle: 0 as *mut c_void,
        })
    }
}

impl Default for HotplugBuilder {
    fn default() -> Self {
        Self {
            vendor_id: None,
            product_id: None,
            params: libusbk_sys::_KHOT_PARAMS::default(),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum NotificationType {
    Arrival,
    Removal,
}

impl From<KLST_SYNC_FLAG> for NotificationType {
    fn from(flag: KLST_SYNC_FLAG) -> Self {
        const ADDED: i32 = 2;

        match flag {
            ADDED => NotificationType::Arrival,
            _ => NotificationType::Removal,
        }
    }
}

pub struct Registration {
    params: KHOT_PARAMS,
    handle: KHOT_HANDLE,
}

impl Registration {
    unsafe extern "C" fn on_hotplug(
        _handle: KHOT_HANDLE,
        device_info: KLST_DEVINFO_HANDLE,
        sync_flag: KLST_SYNC_FLAG,
    ) {
        let mut lock = DATA.get().unwrap().lock().unwrap();
        let data_s = lock.as_mut().unwrap();
        let callback = data_s.callback.as_mut();

        let vid = data_s.vid;
        let pid = data_s.pid;

        let device = device::Device(device_info);

        if let Some(vid) = vid {
            if vid != device.vendor_id() as i32 {
                return;
            }
        }

        if let Some(pid) = pid {
            if pid != device.product_id() as i32 {
                return;
            }
        }

        match sync_flag.into() {
            NotificationType::Arrival => callback.device_arrived(device),
            NotificationType::Removal => callback.device_left(device),
        }
    }

    pub fn init(&mut self) -> Result<()> {
        self.params.OnHotPlug = Some(Self::on_hotplug);

        try_unsafe!(HotK_Init(&mut self.handle, &mut self.params));
        Ok(())
    }
}

impl Drop for Registration {
    fn drop(&mut self) {
        unsafe { HotK_Free(self.handle) };
    }
}

pub fn has_hotplug() -> bool {
    true
}