use std::borrow::Borrow;
use std::ffi::{CStr, CString};
use std::fmt::{self, Debug, Display};
use std::mem;
use std::ops::Deref;
use std::str::Utf8Error;
#[allow(private_bounds)]
pub trait IntoCStr: private::IntoCStrImpl {}
#[allow(private_bounds)]
pub trait IntoCStrArray<const N: usize>: private::IntoCStrArrayImpl<N> {}
pub(crate) mod private {
use std::ffi::CStr;
use std::ops::Deref;
pub(crate) trait IntoCStrImpl {
type CSTR: Deref<Target = CStr>;
fn into_cstr(self) -> Self::CSTR;
}
pub(crate) trait IntoCStrArrayImpl<const N: usize> {
type CSTRS: AsCStrArray<N>;
fn into_cstrs(self) -> Self::CSTRS;
}
pub(crate) trait AsCStrArray<const N: usize> {
fn as_cstr_array(&self) -> [&CStr; N];
}
}
impl IntoCStr for &str {}
impl IntoCStr for String {}
impl IntoCStr for &CStr {}
impl IntoCStr for CString {}
impl IntoCStr for &HexStr {}
impl IntoCStr for HexString {}
impl<'a> private::IntoCStrImpl for &'a str {
type CSTR = CString;
fn into_cstr(self) -> Self::CSTR {
CString::new(self).unwrap()
}
}
impl private::IntoCStrImpl for String {
type CSTR = CString;
fn into_cstr(self) -> Self::CSTR {
CString::new(self).unwrap()
}
}
impl<'a> private::IntoCStrImpl for &'a CStr {
type CSTR = &'a CStr;
fn into_cstr(self) -> Self::CSTR {
self
}
}
impl private::IntoCStrImpl for CString {
type CSTR = CString;
fn into_cstr(self) -> Self::CSTR {
self
}
}
impl<'a> private::IntoCStrImpl for &'a HexStr {
type CSTR = &'a CStr;
fn into_cstr(self) -> Self::CSTR {
self.as_ref()
}
}
impl private::IntoCStrImpl for HexString {
type CSTR = CString;
fn into_cstr(self) -> Self::CSTR {
self.into_cstring()
}
}
impl<S, const N: usize> IntoCStrArray<N> for [S; N] where S: IntoCStr {}
impl IntoCStrArray<0> for () {}
impl<A> IntoCStrArray<1> for (A,) where A: IntoCStr {}
impl<A, B> IntoCStrArray<2> for (A, B)
where
A: IntoCStr,
B: IntoCStr,
{
}
impl<A, B, C> IntoCStrArray<3> for (A, B, C)
where
A: IntoCStr,
B: IntoCStr,
C: IntoCStr,
{
}
impl<A, B, C, D> IntoCStrArray<4> for (A, B, C, D)
where
A: IntoCStr,
B: IntoCStr,
C: IntoCStr,
D: IntoCStr,
{
}
impl<S: IntoCStr, const N: usize> private::IntoCStrArrayImpl<N> for [S; N] {
type CSTRS = [S::CSTR; N];
fn into_cstrs(self) -> Self::CSTRS {
self.map(private::IntoCStrImpl::into_cstr)
}
}
impl private::IntoCStrArrayImpl<0> for () {
type CSTRS = ();
fn into_cstrs(self) -> Self::CSTRS {}
}
impl<A: IntoCStr> private::IntoCStrArrayImpl<1> for (A,) {
type CSTRS = (A::CSTR,);
fn into_cstrs(self) -> Self::CSTRS {
(self.0.into_cstr(),)
}
}
impl<A: IntoCStr, B: IntoCStr> private::IntoCStrArrayImpl<2> for (A, B) {
type CSTRS = (A::CSTR, B::CSTR);
fn into_cstrs(self) -> Self::CSTRS {
(self.0.into_cstr(), self.1.into_cstr())
}
}
impl<A: IntoCStr, B: IntoCStr, C: IntoCStr> private::IntoCStrArrayImpl<3> for (A, B, C) {
type CSTRS = (A::CSTR, B::CSTR, C::CSTR);
fn into_cstrs(self) -> Self::CSTRS {
(self.0.into_cstr(), self.1.into_cstr(), self.2.into_cstr())
}
}
impl<A: IntoCStr, B: IntoCStr, C: IntoCStr, D: IntoCStr> private::IntoCStrArrayImpl<4>
for (A, B, C, D)
{
type CSTRS = (A::CSTR, B::CSTR, C::CSTR, D::CSTR);
fn into_cstrs(self) -> Self::CSTRS {
(
self.0.into_cstr(),
self.1.into_cstr(),
self.2.into_cstr(),
self.3.into_cstr(),
)
}
}
impl<S: Deref<Target = CStr>, const N: usize> private::AsCStrArray<N> for [S; N] {
fn as_cstr_array(&self) -> [&CStr; N] {
self.each_ref().map(Deref::deref)
}
}
impl private::AsCStrArray<0> for () {
fn as_cstr_array(&self) -> [&CStr; 0] {
[]
}
}
impl<A: Deref<Target = CStr>> private::AsCStrArray<1> for (A,) {
fn as_cstr_array(&self) -> [&CStr; 1] {
[self.0.deref()]
}
}
impl<A: Deref<Target = CStr>, B: Deref<Target = CStr>> private::AsCStrArray<2> for (A, B) {
fn as_cstr_array(&self) -> [&CStr; 2] {
[self.0.deref(), self.1.deref()]
}
}
impl<A: Deref<Target = CStr>, B: Deref<Target = CStr>, C: Deref<Target = CStr>>
private::AsCStrArray<3> for (A, B, C)
{
fn as_cstr_array(&self) -> [&CStr; 3] {
[self.0.deref(), self.1.deref(), self.2.deref()]
}
}
impl<
A: Deref<Target = CStr>,
B: Deref<Target = CStr>,
C: Deref<Target = CStr>,
D: Deref<Target = CStr>,
> private::AsCStrArray<4> for (A, B, C, D)
{
fn as_cstr_array(&self) -> [&CStr; 4] {
[
self.0.deref(),
self.1.deref(),
self.2.deref(),
self.3.deref(),
]
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct HexStr {
inner: str,
}
impl HexStr {
pub(crate) const EMPTY: &'static HexStr = match HexStr::from_cstr(c"") {
Ok(hex) => hex,
Err(_) => unreachable!(),
};
pub(crate) unsafe fn from_null_terminated_str(str: &str) -> &HexStr {
unsafe { mem::transmute(str) }
}
pub(crate) const fn from_cstr(cstr: &CStr) -> Result<&HexStr, Utf8Error> {
let bytes = cstr.to_bytes_with_nul();
let str: &str = match std::str::from_utf8(bytes) {
Ok(str) => str,
Err(err) => return Err(err),
};
let hex: &HexStr = unsafe { mem::transmute(str) };
Ok(hex)
}
pub fn as_str(&self) -> &str {
self.deref()
}
pub fn as_cstr(&self) -> &CStr {
self.as_ref()
}
}
impl Debug for HexStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let str: &str = self.as_ref();
Debug::fmt(str, f)
}
}
impl Display for HexStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let str: &str = self.as_ref();
Display::fmt(str, f)
}
}
impl Deref for HexStr {
type Target = str;
fn deref(&self) -> &Self::Target {
unsafe { self.inner.get_unchecked(0..self.inner.len() - 1) }
}
}
impl AsRef<str> for HexStr {
fn as_ref(&self) -> &str {
self.deref()
}
}
impl AsRef<CStr> for HexStr {
fn as_ref(&self) -> &CStr {
unsafe { CStr::from_bytes_with_nul_unchecked(self.inner.as_bytes()) }
}
}
impl ToOwned for HexStr {
type Owned = HexString;
fn to_owned(&self) -> Self::Owned {
unsafe { HexString::from_null_terminated_string(self.inner.to_owned()) }
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct HexString {
inner: String,
}
impl HexString {
pub(crate) unsafe fn from_null_terminated_string(string: String) -> HexString {
HexString { inner: string }
}
pub fn into_string(self) -> String {
let mut s = self.inner;
let null = s.pop();
debug_assert_eq!(null, Some('\0'));
s
}
pub fn into_cstring(self) -> CString {
unsafe { CString::from_vec_with_nul_unchecked(self.inner.into_bytes()) }
}
}
impl Debug for HexString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let str: &str = self.as_ref();
Debug::fmt(str, f)
}
}
impl Display for HexString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let str: &str = self.as_ref();
Display::fmt(str, f)
}
}
impl Deref for HexString {
type Target = HexStr;
fn deref(&self) -> &Self::Target {
unsafe { HexStr::from_null_terminated_str(&self.inner) }
}
}
impl Borrow<HexStr> for HexString {
fn borrow(&self) -> &HexStr {
self.deref()
}
}
#[cfg(test)]
mod tests {
use super::private::*;
use super::*;
#[test]
fn intocstr_str() {
let owner = "hello".into_cstr();
assert_eq!(owner.as_ref(), c"hello");
let owner = String::from("hello").into_cstr();
assert_eq!(owner.as_ref(), c"hello");
let owner = c"hello".into_cstr();
assert_eq!(owner.as_ref(), c"hello");
let owner = CString::from(c"hello").into_cstr();
assert_eq!(owner.as_ref(), c"hello");
}
#[test]
#[should_panic]
fn intocstr_str_invalid_no_null() {
"hel\0lo".into_cstr();
}
#[test]
#[should_panic]
fn intocstr_str_invalid_with_null() {
"hel\0lo\0".into_cstr();
}
#[test]
fn hexstr_empty_is_empty() {
assert_eq!(HexStr::EMPTY.as_str(), "");
assert_eq!(HexStr::EMPTY.as_cstr(), c"");
}
#[test]
fn hexstr_from_cstr() {
let hex = HexStr::from_cstr(c"hello").unwrap();
assert_eq!(hex.as_str(), "hello");
assert_eq!(hex.as_cstr(), c"hello");
}
#[test]
fn hexstr_from_cstr_invalid() {
assert!(HexStr::from_cstr(c"hello\xcf").is_err());
}
#[test]
fn hexstr_debug() {
let hex = HexStr::from_cstr(c"hello").unwrap();
assert_eq!(format!("{:?}", hex), "\"hello\"");
}
#[test]
fn hexstr_display() {
let hex = HexStr::from_cstr(c"hello").unwrap();
assert_eq!(format!("{}", hex), "hello");
}
#[test]
fn hexstr_deref() {
let hex = HexStr::from_cstr(c"hello").unwrap();
assert_eq!(hex.len(), "hello".len());
}
#[test]
fn hexstr_to_owned() {
let hex = HexStr::from_cstr(c"hello").unwrap();
let owned: HexString = hex.to_owned();
assert_eq!(owned.as_str(), "hello");
}
#[test]
fn hexstring_into_string() {
let hex: HexString = HexStr::from_cstr(c"hello").unwrap().to_owned();
assert_eq!(hex.into_string(), "hello");
}
#[test]
fn hexstring_into_cstring() {
let hex: HexString = HexStr::from_cstr(c"hello").unwrap().to_owned();
assert_eq!(hex.into_cstring().as_c_str(), c"hello");
}
#[test]
fn hexstring_debug() {
let hex: HexString = HexStr::from_cstr(c"hello").unwrap().to_owned();
assert_eq!(format!("{:?}", hex), "\"hello\"");
}
#[test]
fn hexstring_display() {
let hex: HexString = HexStr::from_cstr(c"hello").unwrap().to_owned();
assert_eq!(format!("{}", hex), "hello");
}
#[test]
fn hexstring_deref() {
let hex: HexString = HexStr::from_cstr(c"hello").unwrap().to_owned();
assert_eq!(hex.as_str(), "hello");
}
}