linuxfb 0.3.1

Interface to the Linux Framebuffer API
Documentation
use std::fs::File;
use std::io::{BufReader, BufRead};

pub fn devices() -> std::io::Result<impl Iterator<Item = Device>> {
    Ok(parse_devices(BufReader::new(File::open("/proc/devices")?)))
}

#[derive(Clone)]
#[derive(Copy)]
#[derive(Debug)]
#[derive(PartialEq, Eq)]
pub enum DeviceKind {
    Character,
    Block,
}

#[derive(Debug)]
#[derive(PartialEq, Eq)]
pub struct Device {
    pub kind: DeviceKind,
    pub major: u32,
    pub driver: String,
}

impl Device {
    pub fn parse(kind: DeviceKind, line: String) -> Option<Device> {
        let mut major = String::new();
        let mut driver = String::new();
        let mut major_done = false;
        for c in line.chars() {
            if major_done {
                if c.is_ascii_alphanumeric() {
                    driver.push(c);
                }
            } else {
                if c.is_digit(10) {
                    major.push(c);
                } else if major.len() > 0 {
                    major_done = true;
                }
            }
        }
        if major.len() > 0 && driver.len() > 0 {
            match major.parse::<u32>() {
                Ok(major_number) => Some(Device {
                    kind: kind,
                    major: major_number,
                    driver: driver,
                }),
                Err(_) => None
            }
        } else {
            None
        }
    }
}

fn parse_device_line(current_kind: &mut DeviceKind, line: String) -> Option<Option<Device>> {
    if line == "Character devices:" {
        *current_kind = DeviceKind::Character;
        Some(None)
    } else if line == "Block devices:" {
        *current_kind = DeviceKind::Block;
        Some(None)
    } else {
        Some(Device::parse(*current_kind, line))
    }
}

pub fn parse_devices(input: impl BufRead) -> impl Iterator<Item = Device> {
    input
        .lines()
        .map(|result| result.unwrap())
        .scan(DeviceKind::Character, parse_device_line)
        .flat_map(|x| x)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_devices() {
        let input = "
Character devices:
  1 mem
  4 tty
249 rtc

Block devices:
  7 loop
  9 md
";
        let mut devices = parse_devices(std::io::Cursor::new(input));
        assert_eq!(devices.next(), Some(Device { kind: DeviceKind::Character, major: 1, driver: String::from("mem") }));
        assert_eq!(devices.next(), Some(Device { kind: DeviceKind::Character, major: 4, driver: String::from("tty") }));
        assert_eq!(devices.next(), Some(Device { kind: DeviceKind::Character, major: 249, driver: String::from("rtc") }));
        assert_eq!(devices.next(), Some(Device { kind: DeviceKind::Block, major: 7, driver: String::from("loop") }));
        assert_eq!(devices.next(), Some(Device { kind: DeviceKind::Block, major: 9, driver: String::from("md") }));
        assert_eq!(devices.next(), None);
    }
}