1use cfg_if::cfg_if;
2use core::ffi::*;
3use displaydoc::Display;
4use proc_macro2::TokenStream;
5use quote::quote;
6use thiserror::Error;
7
8#[non_exhaustive]
15pub enum HsType {
16 CInt,
18 CChar,
20 CSChar,
22 CUChar,
24 CShort,
26 CUShort,
28 CUInt,
30 CLong,
32 CULong,
34 CLLong,
36 CULLong,
38 CBool,
40 CString,
42 CDouble,
44 CFloat,
46 Empty,
48 Ptr(Box<HsType>),
50 IO(Box<HsType>),
52 FunPtr(Vec<HsType>),
54}
55
56impl std::fmt::Display for HsType {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 write!(
59 f,
60 "{}",
61 match self {
62 HsType::CBool => "CBool".to_string(),
63 HsType::CChar => "CChar".to_string(),
64 HsType::CDouble => "CDouble".to_string(),
65 HsType::CFloat => "CFloat".to_string(),
66 HsType::CInt => "CInt".to_string(),
67 HsType::CLLong => "CLLong".to_string(),
68 HsType::CLong => "CLong".to_string(),
69 HsType::CSChar => "CSChar".to_string(),
70 HsType::CShort => "CShort".to_string(),
71 HsType::CString => "CString".to_string(),
72 HsType::CUChar => "CUChar".to_string(),
73 HsType::CUInt => "CUInt".to_string(),
74 HsType::CULLong => "CULLong".to_string(),
75 HsType::CULong => "CULong".to_string(),
76 HsType::CUShort => "CUShort".to_string(),
77 HsType::Empty => "()".to_string(),
78 HsType::Ptr(x) => format!("Ptr ({x})"),
79 HsType::IO(x) => format!("IO ({x})"),
80 HsType::FunPtr(types) => {
81 let args: Vec<String> = types.iter().map(|arg| format!("{arg}")).collect();
82 format!("FunPtr({})", args.join(" -> "))
83 }
84 }
85 )
86 }
87}
88
89#[derive(Debug, Display, Error)]
90pub enum Error {
91 UnsupportedHsType(String),
98 UnmatchedParenthesis,
100 FunPtrWithoutTypeArgument,
102}
103
104pub struct ArrowIter<'a> {
105 remaining: &'a str,
106}
107
108impl<'a> Iterator for ArrowIter<'a> {
109 type Item = &'a str;
110
111 fn next(&mut self) -> Option<Self::Item> {
112 let ArrowIter { remaining } = self;
113
114 let mut open = 0;
115 let mut offset = 0;
116
117 if remaining.trim().is_empty() {
118 return None;
119 }
120
121 let mut matched: &str = "";
122
123 for c in remaining.chars() {
124 if c == '(' {
125 open += 1;
126 } else if c == ')' {
127 open -= 1;
128 } else if open == 0 && remaining[offset..].starts_with("->") {
129 matched = &remaining[..offset];
130 offset += "->".len();
131 break;
132 }
133
134 offset += c.len_utf8();
135 matched = &remaining[..offset];
136 }
137
138 *remaining = &remaining[offset..];
139 Some(matched)
140 }
141}
142
143impl<'a> From<&'a str> for ArrowIter<'a> {
144 fn from(value: &'a str) -> Self {
145 Self { remaining: value }
146 }
147}
148
149impl std::str::FromStr for HsType {
150 type Err = Error;
151
152 fn from_str(s: &str) -> Result<Self, Self::Err> {
153 let s = s.trim();
154 if s == "()" {
155 Ok(HsType::Empty)
156 } else if !s.is_empty() && &s[..1] == "(" {
157 Ok(s[1..]
158 .strip_suffix(')')
159 .ok_or(Error::UnmatchedParenthesis)?
160 .parse()?)
161 } else if s.len() >= 2 && &s[..2] == "IO" {
162 Ok(HsType::IO(Box::new(s[2..].parse()?)))
163 } else if s.len() >= 3 && &s[..3] == "Ptr" {
164 Ok(HsType::Ptr(Box::new(s[3..].parse()?)))
165 } else if s.len() >= 6 && &s[..6] == "FunPtr" {
166 let mut s = s[6..].trim();
167
168 if let Some('(') = s.chars().next() {
169 s = s[1..]
170 .strip_suffix(')')
171 .ok_or(Error::UnmatchedParenthesis)?;
172 }
173
174 let types: Vec<_> = ArrowIter { remaining: s }
175 .map(|s| s.parse::<Self>())
176 .collect::<Result<_, _>>()?;
177
178 if types.is_empty() {
179 return Err(Error::FunPtrWithoutTypeArgument);
180 }
181
182 Ok(HsType::FunPtr(types))
183 } else {
184 match s {
185 "CBool" => Ok(HsType::CBool),
186 "CChar" => Ok(HsType::CChar),
187 "CDouble" => Ok(HsType::CDouble),
188 "CFloat" => Ok(HsType::CFloat),
189 "CInt" => Ok(HsType::CInt),
190 "CLLong" => Ok(HsType::CLLong),
191 "CLong" => Ok(HsType::CLong),
192 "CSChar" => Ok(HsType::CSChar),
193 "CShort" => Ok(HsType::CShort),
194 "CString" => Ok(HsType::CString),
195 "CUChar" => Ok(HsType::CUChar),
196 "CUInt" => Ok(HsType::CUInt),
197 "CULLong" => Ok(HsType::CULLong),
198 "CULong" => Ok(HsType::CULong),
199 "CUShort" => Ok(HsType::CUShort),
200 ty => Err(Error::UnsupportedHsType(ty.to_string())),
201 }
202 }
203 }
204}
205
206impl HsType {
207 pub fn quote(&self) -> TokenStream {
215 match self {
216 HsType::CBool => quote! { bool },
218 HsType::CChar => quote! { core::ffi::c_char },
219 HsType::CDouble => quote! { core::ffi::c_double },
220 HsType::CFloat => quote! { core::ffi::c_float },
221 HsType::CInt => quote! { core::ffi::c_int },
222 HsType::CLLong => quote! { core::ffi::c_longlong },
223 HsType::CLong => quote! { core::ffi::c_long },
224 HsType::CSChar => quote! { core::ffi::c_schar },
225 HsType::CShort => quote! { core::ffi::c_short },
226 HsType::CString => HsType::Ptr(Box::new(HsType::CChar)).quote(),
227 HsType::CUChar => quote! { core::ffi::c_uchar },
228 HsType::CUInt => quote! { core::ffi::c_uint },
229 HsType::CULLong => quote! { core::ffi::c_ulonglong },
230 HsType::CULong => quote! { core::ffi::c_ulong },
231 HsType::CUShort => quote! { core::ffi::c_ushort },
232 HsType::Empty => quote! { () },
233 HsType::Ptr(x) => {
234 let ty = x.quote();
235 quote! { *const #ty }
236 }
237 HsType::IO(x) => x.quote(),
238 HsType::FunPtr(types) => {
239 let ret = types.last().unwrap().quote();
240 let args: Vec<_> = types[..types.len() - 1]
241 .iter()
242 .map(|arg| arg.quote())
243 .collect();
244 quote!(unsafe extern "C" fn(#(#args),*) -> #ret)
245 }
246 }
247 }
248}
249
250pub trait ReprHs {
256 fn into() -> HsType;
257}
258
259macro_rules! repr_hs {
260 ($($ty:ty => $ident:ident,)*) => {$(
261 impl ReprHs for $ty {
262 fn into() -> HsType {
263 HsType::$ident
264 }
265 }
266 )*};
267}
268pub(crate) use repr_hs;
269
270repr_hs! {
271 c_char => CChar,
272 c_double => CDouble,
273 c_float => CFloat,
274 c_int => CInt,
275 c_short => CShort,
276 c_uchar => CUChar,
277 c_uint => CUInt,
278 c_ushort => CUShort,
279 () => Empty,
280}
281
282cfg_if! {
283 if #[cfg(all(target_pointer_width = "64", not(windows)))] {
284 repr_hs! {
285 c_long => CLong,
286 c_ulong => CULong,
287 }
288 } else {
289 repr_hs! {
290 c_longlong => CLLong,
291 c_ulonglong => CULLong,
292 }
293 }
294}
295
296impl<T> ReprHs for *const T
297where
298 T: ReprHs,
299{
300 fn into() -> HsType {
301 HsType::Ptr(Box::new(T::into()))
302 }
303}
304
305impl<T> ReprHs for *mut T
306where
307 T: ReprHs,
308{
309 fn into() -> HsType {
310 HsType::Ptr(Box::new(T::into()))
311 }
312}
313
314impl<T> ReprHs for Vec<T>
317where
318 T: ReprHs,
319{
320 fn into() -> HsType {
321 HsType::Ptr(Box::new(T::into()))
322 }
323}
324
325impl<T, const N: usize> ReprHs for &[T; N]
326where
327 T: ReprHs,
328{
329 fn into() -> HsType {
330 HsType::Ptr(Box::new(T::into()))
331 }
332}
333
334use std::ffi::CString;
337
338repr_hs! {
339 CString => CString,
340 &CStr => CString,
341 String => CString,
342 &str => CString,
343}