#![allow(clippy::tabs_in_doc_comments)]
use std::ffi::OsStr;
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use getargs::Argument;
mod utf8_bs;
#[cfg(test)]
mod test;
#[repr(transparent)]
pub struct OsArgument(pub OsStr);
impl<'a> From<&'a OsStr> for &'a OsArgument {
fn from(from: &'a OsStr) -> Self {
unsafe { std::mem::transmute(from) }
}
}
impl<'a> From<&'a OsArgument> for &'a OsStr {
fn from(from: &'a OsArgument) -> Self {
unsafe { std::mem::transmute(from) }
}
}
impl OsArgument {
fn as_bytes(&self) -> &[u8] {
#[cfg(windows)]
unsafe { std::mem::transmute(&self.0) }
#[cfg(not(windows))]
std::os::unix::ffi::OsStrExt::as_bytes(&self.0)
}
fn from_bytes(bytes: &[u8]) -> &Self {
#[cfg(windows)]
unsafe { std::mem::transmute(bytes) }
#[cfg(not(windows))]
<&Self as From<&OsStr>>::from(std::os::unix::ffi::OsStrExt::from_bytes(bytes))
}
}
impl Deref for OsArgument {
type Target = OsStr;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl DerefMut for OsArgument {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
impl PartialEq for OsArgument {
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
impl Eq for OsArgument {}
impl Debug for OsArgument {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) }
}
impl Hash for OsArgument {
fn hash<H: Hasher>(&self, state: &mut H) { self.0.hash(state) }
}
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum ShortOpt {
Codepoint(u32),
Byte(u8)
}
impl From<char> for ShortOpt {
fn from(codepoint: char) -> Self {
Self::Codepoint(codepoint as u32)
}
}
impl From<u32> for ShortOpt {
fn from(codepoint: u32) -> Self {
Self::Codepoint(codepoint)
}
}
impl From<u8> for ShortOpt {
fn from(byte: u8) -> Self {
Self::Byte(byte)
}
}
impl Argument for &'_ OsArgument {
type ShortOpt = ShortOpt;
#[inline]
fn ends_opts(self) -> bool {
self.as_bytes() == b"--"
}
#[inline]
fn parse_long_opt(self) -> Option<(Self, Option<Self>)> {
self.as_bytes().parse_long_opt().map(|(name, value)| (OsArgument::from_bytes(name), value.map(OsArgument::from_bytes)))
}
#[inline]
fn parse_short_cluster(self) -> Option<Self> {
self.as_bytes().parse_short_cluster().map(OsArgument::from_bytes)
}
#[cfg_attr(not(windows), inline)] fn consume_short_opt(self) -> (Self::ShortOpt, Option<Self>) {
#[cfg(windows)] {
let mut iter = self.as_bytes().iter();
let codepoint = unsafe { utf8_bs::next_code_point(&mut iter).unwrap_unchecked() };
(ShortOpt::Codepoint(codepoint), Some(iter.as_slice()).filter(|&slice| !slice.is_empty()).map(OsArgument::from_bytes))
}
#[cfg(not(windows))] {
let bytes = self.as_bytes();
let first = unsafe { *bytes.get_unchecked(0) };
let encoded_length = utf8_bs::utf8_char_width(first);
let (codepoint, rest) = if let Some(Ok(Some(char))) = bytes.get(0..encoded_length).map(|slice| std::str::from_utf8(slice).map(|str| str.chars().next())) {
(ShortOpt::Codepoint(char as u32), unsafe { bytes.get_unchecked(encoded_length..) })
} else {
(ShortOpt::Byte(first), unsafe { bytes.get_unchecked(1..) })
};
(codepoint, Some(OsArgument::from_bytes(rest)).filter(|s| !s.is_empty()))
}
}
#[inline]
fn consume_short_val(self) -> Self {
self
}
}
#[macro_export]
macro_rules! os {
($string:literal) => { <&$crate::OsArgument as From<&::std::ffi::OsStr>>::from(unsafe { std::mem::transmute(str::as_bytes($string as &str)) }) }
}
#[macro_export]
macro_rules! osb {
($bytes:literal) => { <&$crate::OsArgument as From<&::std::ffi::OsStr>>::from(std::mem::transmute($bytes as &[u8])) }
}