async-hid 0.5.1

A async library for interacting with HID devices
Documentation
use std::path::Path;

use crate::ensure;

#[derive(Debug)]
pub enum Action<'a> {
    Add,
    Remove,
    Other(&'a str)
}

#[derive(Debug)]
pub struct UEvent<'a> {
    pub action: Action<'a>,
    pub subsystem: &'a str,
    pub dev_path: &'a Path
}

impl<'a> UEvent<'a> {
    pub fn parse(event: &'a [u8]) -> Result<UEvent<'a>, &'static str> {
        ensure!(event.len() >= 32, "Message is too short");
        let offset = if event[0..8].eq(b"libudev\0") {
            const UDEV_MONITOR_MAGIC: u32 = 0xfeedcafe;
            let magic = u32::from_be_bytes(event[8..12].try_into().unwrap());
            ensure!(magic == UDEV_MONITOR_MAGIC, "Invalid magic number");

            u32::from_ne_bytes(event[16..20].try_into().unwrap()) as usize
        } else {
            ensure!(event.contains(&b'@'), "Invalid kernel event");
            event
                .iter()
                .position(|&b| b == b'\0')
                .ok_or("Failed to find the start of the message")?
                + 1
        };

        Self::parse_internal(&event[offset..])
    }
    fn parse_internal(event: &'a [u8]) -> Result<UEvent<'a>, &'static str> {
        let mut action = None;
        let mut subsystem = None;
        let mut dev_path = None;

        for line in std::str::from_utf8(event)
            .map_err(|_| "Invalid utf-8")?
            .split('\0')
        {
            if let Some((key, value)) = line.split_once('=') {
                match key {
                    "ACTION" => {
                        action = Some(match value {
                            "add" => Action::Add,
                            "remove" => Action::Remove,
                            other => Action::Other(other)
                        })
                    }
                    "SUBSYSTEM" => subsystem = Some(value),
                    "DEVPATH" => dev_path = Some(Path::new(value)),
                    _ => {}
                }
            }
        }

        Ok(UEvent {
            action: action.ok_or("Event action not found")?,
            subsystem: subsystem.ok_or("Event subsystem not found")?,
            dev_path: dev_path.ok_or("Event device path not found")?
        })
    }
}