use std::fs;
use std::path::Path;
use super::{GeneratorError, GeneratorModule, GeneratorResult};
use crate::parser::vk_types::FeatureDefinition;
pub struct FeatureGenerator;
impl Default for FeatureGenerator {
fn default() -> Self {
Self::new()
}
}
impl FeatureGenerator {
pub fn new() -> Self {
Self
}
fn generate_feature(&self, feature: &FeatureDefinition) -> String {
let mut code = String::new();
if let Some(comment) = &feature.comment {
code.push_str(&format!("/// {}\n", comment));
} else {
code.push_str(&format!("/// Vulkan API feature: {}\n", feature.name));
}
if feature.name.starts_with("VK_VERSION_") {
let version_parts = self.parse_version_number(&feature.number);
if let Some((major, minor)) = version_parts {
code.push_str(&format!(
"pub const {}: u32 = vk_make_api_version(0, {}, {}, 0);\n",
feature.name, major, minor
));
} else {
code.push_str(&format!(
"pub const {}: u32 = {};\n",
feature.name, feature.number
));
}
} else {
code.push_str(&format!(
"pub const {}: &str = \"{}\";\n",
feature.name, feature.number
));
}
if let Some(deprecated) = &feature.deprecated {
code.push_str(&format!("#[deprecated = \"{}\"]\n", deprecated));
}
code.push('\n');
code
}
fn parse_version_number(&self, version: &str) -> Option<(u32, u32)> {
let parts: Vec<&str> = version.split('.').collect();
if parts.len() >= 2 {
if let (Ok(major), Ok(minor)) = (parts[0].parse::<u32>(), parts[1].parse::<u32>()) {
return Some((major, minor));
}
}
None
}
fn generate_version_helpers(&self) -> String {
r#"
/// Helper function to create API version from components
pub const fn vk_make_api_version(variant: u32, major: u32, minor: u32, patch: u32) -> u32 {
(variant << 29) | (major << 22) | (minor << 12) | patch
}
/// Extract major version from API version
pub const fn vk_api_version_major(version: u32) -> u32 {
(version >> 22) & 0x7F
}
/// Extract minor version from API version
pub const fn vk_api_version_minor(version: u32) -> u32 {
(version >> 12) & 0x3FF
}
/// Extract patch version from API version
pub const fn vk_api_version_patch(version: u32) -> u32 {
version & 0xFFF
}
/// Extract variant from API version
pub const fn vk_api_version_variant(version: u32) -> u32 {
version >> 29
}
"#
.to_string()
}
}
impl GeneratorModule for FeatureGenerator {
fn name(&self) -> &str {
"FeatureGenerator"
}
fn input_files(&self) -> Vec<String> {
vec!["features.json".to_string()]
}
fn output_file(&self) -> String {
"features.rs".to_string()
}
fn dependencies(&self) -> Vec<String> {
vec![] }
fn generate(&self, input_dir: &Path, output_dir: &Path) -> GeneratorResult<()> {
let input_path = input_dir.join("features.json");
let input_content = fs::read_to_string(input_path).map_err(GeneratorError::Io)?;
let features: Vec<FeatureDefinition> =
serde_json::from_str(&input_content).map_err(GeneratorError::Json)?;
let mut generated_code = String::new();
generated_code.push_str("#[allow(non_camel_case_types)]\n");
generated_code.push_str("#[allow(dead_code)]\n\n");
generated_code.push_str(&self.generate_version_helpers());
for feature in &features {
generated_code.push_str(&self.generate_feature(feature));
}
let output_path = output_dir.join(self.output_file());
fs::write(output_path, generated_code).map_err(GeneratorError::Io)?;
crate::codegen::logging::log_info(&format!(
"FeatureGenerator: Generated {} feature definitions",
features.len()
));
Ok(())
}
}