use core::ffi::*;
use displaydoc::Display;
use proc_macro2::TokenStream;
use quote::quote;
use thiserror::Error;
#[non_exhaustive]
pub enum HsType {
CInt,
CChar,
CSChar,
CUChar,
CShort,
CUShort,
CUInt,
CLong,
CULong,
CLLong,
CULLong,
CBool,
CString,
CDouble,
CFloat,
Empty,
Ptr(Box<HsType>),
IO(Box<HsType>),
}
impl std::fmt::Display for HsType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
HsType::CBool => "CBool".to_string(),
HsType::CChar => "CChar".to_string(),
HsType::CDouble => "CDouble".to_string(),
HsType::CFloat => "CFloat".to_string(),
HsType::CInt => "CInt".to_string(),
HsType::CLLong => "CLLong".to_string(),
HsType::CLong => "CLong".to_string(),
HsType::CSChar => "CSChar".to_string(),
HsType::CShort => "CShort".to_string(),
HsType::CString => "CString".to_string(),
HsType::CUChar => "CUChar".to_string(),
HsType::CUInt => "CUInt".to_string(),
HsType::CULLong => "CULLong".to_string(),
HsType::CULong => "CULong".to_string(),
HsType::CUShort => "CUShort".to_string(),
HsType::Empty => "()".to_string(),
HsType::Ptr(x) => format!("Ptr ({x})"),
HsType::IO(x) => format!("IO ({x})"),
}
)
}
}
#[derive(Debug, Display, Error)]
pub enum Error {
UnsupportedHsType(String),
UnmatchedParenthesis,
}
impl std::str::FromStr for HsType {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if s == "()" {
Ok(HsType::Empty)
} else if !s.is_empty() && &s[..1] == "(" {
Ok(s[1..]
.strip_suffix(')')
.ok_or(Error::UnmatchedParenthesis)?
.parse()?)
} else if s.len() >= 2 && &s[..2] == "IO" {
Ok(HsType::IO(Box::new(s[2..].parse()?)))
} else if s.len() >= 3 && &s[..3] == "Ptr" {
Ok(HsType::Ptr(Box::new(s[3..].parse()?)))
} else {
match s {
"CBool" => Ok(HsType::CBool),
"CChar" => Ok(HsType::CChar),
"CDouble" => Ok(HsType::CDouble),
"CFloat" => Ok(HsType::CFloat),
"CInt" => Ok(HsType::CInt),
"CLLong" => Ok(HsType::CLLong),
"CLong" => Ok(HsType::CLong),
"CSChar" => Ok(HsType::CSChar),
"CShort" => Ok(HsType::CShort),
"CString" => Ok(HsType::CString),
"CUChar" => Ok(HsType::CUChar),
"CUInt" => Ok(HsType::CUInt),
"CULLong" => Ok(HsType::CULLong),
"CULong" => Ok(HsType::CULong),
"CUShort" => Ok(HsType::CUShort),
ty => Err(Error::UnsupportedHsType(ty.to_string())),
}
}
}
}
impl HsType {
pub fn quote(&self) -> TokenStream {
match self {
HsType::CBool => quote! { bool },
HsType::CChar => quote! { core::ffi::c_char },
HsType::CDouble => quote! { core::ffi::c_double },
HsType::CFloat => quote! { core::ffi::c_float },
HsType::CInt => quote! { core::ffi::c_int },
HsType::CLLong => quote! { core::ffi::c_longlong },
HsType::CLong => quote! { core::ffi::c_long },
HsType::CSChar => quote! { core::ffi::c_schar },
HsType::CShort => quote! { core::ffi::c_short },
HsType::CString => HsType::Ptr(Box::new(HsType::CChar)).quote(),
HsType::CUChar => quote! { core::ffi::c_uchar },
HsType::CUInt => quote! { core::ffi::c_uint },
HsType::CULLong => quote! { core::ffi::c_ulonglong },
HsType::CULong => quote! { core::ffi::c_ulong },
HsType::CUShort => quote! { core::ffi::c_ushort },
HsType::Empty => quote! { () },
HsType::Ptr(x) => {
let ty = x.quote();
quote! { *const #ty }
}
HsType::IO(x) => x.quote(),
}
}
}
pub trait ReprHs {
fn into() -> HsType;
}
macro_rules! repr_hs {
($($ty:ty => $ident:ident,)*) => {$(
impl ReprHs for $ty {
fn into() -> HsType {
HsType::$ident
}
}
)*};
}
pub(crate) use repr_hs;
repr_hs! {
c_char => CChar,
c_double => CDouble,
c_float => CFloat,
c_int => CInt,
c_long => CLong,
c_short => CShort,
c_uchar => CUChar,
c_uint => CUInt,
c_ulong => CULong,
c_ushort => CUShort,
() => Empty,
}
impl<T> ReprHs for *const T
where
T: ReprHs,
{
fn into() -> HsType {
HsType::Ptr(Box::new(T::into()))
}
}