use once_cell::sync::Lazy; use regex::{Captures, Regex}; static C_PTR_RE: Lazy = Lazy::new(|| Regex::new(r"(const )?([a-zA-Z0-9_]+)\s?\*(.*)").unwrap()); static REST_C_PTR_RE: Lazy = Lazy::new(|| Regex::new(r"(const)?\s?\*").unwrap()); pub fn c_ptr_to_rust_ptr(c_ptr: &str) -> String { C_PTR_RE .replace(c_ptr, |captures: &Captures| { let const_or_mut = captures.get(1).map_or_else(|| "mut", |_| "const"); let type_name = captures.get(2).unwrap().as_str(); let rest = captures.get(3).unwrap(); let rest_ptr_matches = REST_C_PTR_RE.captures_iter(rest.as_str()); let rest_ptrs = rest_ptr_matches .map(|capts| { let const_or_mut = capts.get(1).map_or_else(|| "mut", |_| "const"); format!("*{const_or_mut} ") }) .collect::>(); format!("{}*{const_or_mut} {type_name}", rest_ptrs.join(" ")) }) .to_string() } #[cfg(test)] mod tests { use pretty_assertions::assert_str_eq; use super::*; #[test] fn c_ptr_to_rust_ptr_works() { assert_str_eq!(c_ptr_to_rust_ptr("void *"), "*mut void"); assert_str_eq!(c_ptr_to_rust_ptr("const void*"), "*const void"); assert_str_eq!(c_ptr_to_rust_ptr("GLint *"), "*mut GLint"); assert_str_eq!(c_ptr_to_rust_ptr("const GLint*"), "*const GLint"); assert_str_eq!(c_ptr_to_rust_ptr("const GLint *"), "*const GLint"); assert_str_eq!(c_ptr_to_rust_ptr("const GLint **"), "*mut *const GLint"); assert_str_eq!(c_ptr_to_rust_ptr("const GLint * *"), "*mut *const GLint"); assert_str_eq!(c_ptr_to_rust_ptr("GLint **"), "*mut *mut GLint"); assert_str_eq!(c_ptr_to_rust_ptr("GLuint * const*"), "*const *mut GLuint"); assert_str_eq!( c_ptr_to_rust_ptr("const GLuint * const*"), "*const *const GLuint" ); assert_str_eq!(c_ptr_to_rust_ptr("GLchar"), "GLchar"); } }