dirs-cli 0.1.0

A tiny low-level command line application that provides platform-specific standard locations of directories for config, cache and other data on Linux, Windows, macOS and Redox by leveraging the mechanisms defined by the XDG base/user directory specifications on Linux, the Known Folder API on Windows, and the Standard Directory guidelines on macOS.
extern crate windows_sys as windows;

use self::windows::Win32::Foundation::HANDLE;
use self::windows::Win32::Storage::FileSystem;
use self::windows::Win32::System::Console;

use std::ffi::c_void;
use std::io::{Error, Result};
use std::ptr::null_mut;

pub(crate) fn stdout() -> HANDLE {
     unsafe { Console::GetStdHandle(Console::STD_OUTPUT_HANDLE) }
}

pub(crate) fn write(handle: &HANDLE, buf: &[u8]) -> Result<usize> {
    if buf.len() == 0 {
        Ok(0)
    } else if is_console(handle) {
        write_to_console(handle, buf)
    } else {
        write_to_file(handle, buf)
    }
}

fn is_console(handle: &HANDLE) -> bool {
    let mut mode = 0;
    unsafe { Console::GetConsoleMode(*handle, &mut mode) != 0 }
}

fn write_to_console(handle: &HANDLE, buf: &[u8]) -> Result<usize> {
    let mut bytes_written = 0;
    unsafe {
        if Console::WriteConsoleA(
            *handle,
            buf.as_ptr() as *const c_void,
            buf.len() as u32,
            &mut bytes_written,
            null_mut(),
        ) == 0 {
            Err(Error::last_os_error())
        } else {
            assert_eq!(bytes_written, buf.len() as u32);
            Ok(bytes_written as usize)
        }
    }
}

fn write_to_file(handle: &HANDLE, buf: &[u8]) -> Result<usize> {
    let buf = match String::from_utf8(buf.to_vec()) {
        Ok(string) => string,
        Err(e) => return Err(Error::new(std::io::ErrorKind::InvalidInput, e)),
    };

    let mut bytes_written = 0;

    unsafe {
        if FileSystem::WriteFile(
            *handle,
            buf.as_ptr(),
            buf.len() as u32,
            &mut bytes_written,
            null_mut(),
        ) == 0 {
            return Err(Error::last_os_error());
        }
    }
    return Ok(buf.len());
}