wslc 0.1.1

Safe Rust wrapper for Microsoft WSL Containers
Documentation
use std::ffi::{CStr, CString, OsStr};
use std::path::Path;

use crate::{Error, Result};

pub(crate) fn cstring(value: &str, field: &str) -> Result<CString> {
    CString::new(value).map_err(|_| Error::Nul(field.to_owned()))
}

pub(crate) fn cstrings<'a>(
    values: impl IntoIterator<Item = &'a str>,
    field: &str,
) -> Result<Vec<CString>> {
    values
        .into_iter()
        .map(|value| cstring(value, field))
        .collect()
}

#[cfg(windows)]
pub(crate) fn wide_os(value: &OsStr) -> Vec<u16> {
    use std::os::windows::ffi::OsStrExt;

    value.encode_wide().chain(std::iter::once(0)).collect()
}

#[cfg(not(windows))]
pub(crate) fn wide_os(value: &OsStr) -> Vec<u16> {
    value
        .to_string_lossy()
        .encode_utf16()
        .chain(std::iter::once(0))
        .collect()
}

pub(crate) fn wide_path(value: &Path) -> Vec<u16> {
    wide_os(value.as_os_str())
}

pub(crate) fn wide_str(value: &str) -> Vec<u16> {
    value.encode_utf16().chain(std::iter::once(0)).collect()
}

pub(crate) unsafe fn utf16_ptr_to_string(ptr: *const u16) -> String {
    if ptr.is_null() {
        return String::new();
    }

    let mut len = 0usize;
    while unsafe { *ptr.add(len) } != 0 {
        len += 1;
    }

    let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
    String::from_utf16_lossy(slice)
}

pub(crate) unsafe fn c_ptr_to_string(ptr: *const std::ffi::c_char) -> Result<String> {
    if ptr.is_null() {
        return Ok(String::new());
    }

    let bytes = unsafe { CStr::from_ptr(ptr) }.to_bytes().to_vec();
    Ok(String::from_utf8(bytes)?)
}

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

    #[test]
    fn cstring_rejects_interior_nul() {
        let err = cstring("a\0b", "field").unwrap_err();
        assert!(matches!(err, Error::Nul(field) if field == "field"));
    }
}