Documentation
use std::os::unix::ffi::OsStrExt;
use std::fs;
use std::io;
use std::str;
use std::collections::HashSet;

pub fn physical_count() -> io::Result<u64> {
    cpuinfo().or_else(|_| topology())
}

fn cpuinfo() -> io::Result<u64> {
    let content = fs::read_to_string("/proc/cpuinfo")?;

    for line in content.lines() {
        if line.starts_with("cpu cores") {
            let num = &line[line.len()-1 ..];

            if let Ok(num) = num.parse::<u64>() {
                return Ok(num)
            }
        }
    }

    Ok(0)
}

fn topology() -> io::Result<u64> {
    let mut acc = HashSet::<u64>::new();

    let dirs = fs::read_dir("/sys/devices/system/cpu/")?;

    for dir in dirs {
        let dir = dir?;
        let path = dir.path();

        if let Some(file_name) = path.file_name() {
            if file_name.as_bytes().starts_with(b"cpu") {
                let core_id = unsafe { str::from_utf8_unchecked(&file_name.as_bytes()[3..]) };

                if let Ok(_) = core_id.parse::<u64>() {

                    let path = path.join("topology/core_id");

                    let content = fs::read_to_string(path)?;

                    if let Ok(cpu_id) = content.trim().parse::<u64>() {
                        acc.insert(cpu_id);
                    }
                } 
            }
        }

    }

    Ok(acc.len() as u64)
}