pub mod assembler;
pub mod generator_modules;
pub mod logging;
pub mod provisional;
pub mod type_integration;
pub fn sanitize_doc_line(line: &str) -> String {
line.trim()
.replace('<', "<")
.replace('>', ">")
.replace('[', "[")
.replace(']', "]")
}
pub use assembler::AssemblerError;
pub fn camel_to_snake(name: &str) -> String {
let chars: Vec<char> = name.chars().collect();
let mut out = String::with_capacity(name.len() + name.len() / 3);
for i in 0..chars.len() {
let c = chars[i];
if c.is_ascii_uppercase() {
if i > 0 {
let prev = chars[i - 1];
let next = chars.get(i + 1).copied();
let boundary = prev.is_ascii_lowercase()
|| prev.is_ascii_digit()
|| (prev.is_ascii_uppercase()
&& matches!(next, Some(n) if n.is_ascii_lowercase()));
if boundary && !out.ends_with('_') {
out.push('_');
}
}
out.push(c.to_ascii_lowercase());
} else {
out.push(c);
}
}
out
}
#[cfg(test)]
mod camel_to_snake_tests {
use super::camel_to_snake;
#[test]
fn lowercase_passthrough() {
assert_eq!(camel_to_snake("foo"), "foo");
}
#[test]
fn single_boundary() {
assert_eq!(camel_to_snake("timelineSemaphore"), "timeline_semaphore");
}
#[test]
fn multiple_boundaries() {
assert_eq!(
camel_to_snake("shaderBufferFloat32Atomics"),
"shader_buffer_float32_atomics"
);
}
#[test]
fn acronym_group() {
assert_eq!(camel_to_snake("GPUList"), "gpu_list");
assert_eq!(camel_to_snake("VkKHRSwapchain"), "vk_khr_swapchain");
}
#[test]
fn digits_are_word_chars() {
assert_eq!(camel_to_snake("vulkan12"), "vulkan12");
assert_eq!(camel_to_snake("Vulkan12Features"), "vulkan12_features");
}
#[test]
fn preexisting_underscores() {
assert_eq!(camel_to_snake("VK_VERSION_1_0"), "vk_version_1_0");
}
}
#[derive(Debug, thiserror::Error)]
pub enum CodegenError {
#[error("Assembler error: {0}")]
Assembler(#[from] AssemblerError),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Invalid input: {message}")]
InvalidInput { message: String },
#[error("Generation failed: {message}")]
GenerationFailed { message: String },
}
pub type CodegenResult<T> = Result<T, CodegenError>;