use crate::codegen::logging::{log_debug, log_info};
use crate::parser::vk_types::ConstantDefinition;
use std::fs;
use std::path::Path;
use super::{GeneratorError, GeneratorMetadata, GeneratorModule, GeneratorResult};
pub struct ConstantGenerator;
impl Default for ConstantGenerator {
fn default() -> Self {
Self::new()
}
}
impl ConstantGenerator {
pub fn new() -> Self {
Self
}
fn generate_constant(&self, constant: &ConstantDefinition) -> String {
let value = constant.value.as_deref().unwrap_or("0");
let rust_type = self.infer_type_from_value(value);
let rust_value = self.map_value(value, &rust_type);
let mut doc_comment = format!("/// Vulkan constant: {}", constant.name);
if let Some(ref comment) = constant.comment {
doc_comment.push_str(&format!("\n/// {}", comment));
}
format!(
"{}\npub const {}: {} = {};\n",
doc_comment, constant.name, rust_type, rust_value
)
}
fn infer_type_from_value(&self, value: &str) -> String {
let v = value.trim();
let mut v_unparen = v;
while v_unparen.starts_with('(') && v_unparen.ends_with(')') {
v_unparen = v_unparen.trim_start_matches('(').trim_end_matches(')');
v_unparen = v_unparen.trim();
}
let v_upper = v_unparen.to_uppercase();
if v_unparen.starts_with('"') && v_unparen.ends_with('"') {
return "&'static str".to_string();
}
let is_ull = v_upper.ends_with("ULL");
let is_u = !is_ull && v_upper.ends_with('U');
if is_ull {
return "u64".to_string();
}
if is_u {
return "u32".to_string();
}
if v_unparen.starts_with('~') {
return "u32".to_string();
}
if v_unparen.starts_with("0x") || v_unparen.starts_with("0X") {
"u32".to_string()
} else if v.contains('.') {
"f32".to_string()
} else if v_unparen.parse::<i64>().is_ok() {
if v_unparen.starts_with('-') {
"i32".to_string()
} else {
"u32".to_string()
}
} else {
"&'static str".to_string()
}
}
fn map_value(&self, value: &str, value_type: &str) -> String {
let v = value.trim();
let mut v_unparen = v;
while v_unparen.starts_with('(') && v_unparen.ends_with(')') {
v_unparen = v_unparen.trim_start_matches('(').trim_end_matches(')');
v_unparen = v_unparen.trim();
}
if v_unparen.starts_with("0x") || v_unparen.starts_with("0X") {
return v_unparen.to_string();
}
if v_unparen.starts_with('"') && v_unparen.ends_with('"') {
return v_unparen.to_string();
}
if (value_type.starts_with('f') || value_type == "float") && !value.contains('.') {
return format!("{}.0", value);
}
let v_upper_unparen = v_unparen.to_uppercase();
let is_ull = v_upper_unparen.ends_with("ULL");
let is_u = v_upper_unparen.ends_with('U') && !is_ull;
if v_unparen.starts_with('~') {
let mut replaced = v_unparen.replacen('~', "!", 1);
let mut rep_upper = replaced.to_uppercase();
if rep_upper.ends_with("ULL") {
replaced.truncate(replaced.len() - 3);
rep_upper.truncate(rep_upper.len() - 3);
} else if rep_upper.ends_with('U') {
replaced.truncate(replaced.len() - 1);
rep_upper.truncate(rep_upper.len() - 1);
}
if is_ull {
return format!("{}u64", replaced);
} else if is_u {
return format!("{}u32", replaced);
} else {
return replaced;
}
}
let mut rust_value = v_unparen.to_string();
if rust_value.to_uppercase().ends_with("ULL") {
rust_value = rust_value[..rust_value.len() - 3].to_string();
}
if rust_value.ends_with("U)") {
rust_value = rust_value.replace("U)", ")");
}
if rust_value.ends_with("U;") {
rust_value = rust_value.replace("U;", ";");
}
if rust_value.ends_with("F") || rust_value.ends_with("f") {
rust_value = rust_value.trim_end_matches(['F', 'f']).to_string() + "f32";
}
if rust_value.ends_with("L") {
rust_value = rust_value.replace("L", "");
}
if rust_value == "~0ULL" || rust_value == "(~0ULL)" {
rust_value = "u64::MAX".to_string();
} else if rust_value == "~0U" || rust_value == "(~0U)" {
rust_value = "!0u32".to_string();
} else if rust_value == "~0" || rust_value == "(~0)" {
rust_value = "!0".to_string();
} else if rust_value == "~1" || rust_value == "(~1)" {
rust_value = "!1".to_string();
} else if rust_value == "~2" || rust_value == "(~2)" {
rust_value = "!2".to_string();
}
rust_value
}
}
impl GeneratorModule for ConstantGenerator {
fn name(&self) -> &str {
"ConstantGenerator"
}
fn input_files(&self) -> Vec<String> {
vec!["constants.json".to_string()]
}
fn output_file(&self) -> String {
"constants.rs".to_string()
}
fn dependencies(&self) -> Vec<String> {
Vec::new() }
fn generate(&self, input_dir: &Path, output_dir: &Path) -> GeneratorResult<()> {
let input_path = input_dir.join("constants.json");
let input_content = fs::read_to_string(input_path).map_err(GeneratorError::Io)?;
let constants_array: Vec<ConstantDefinition> =
serde_json::from_str(&input_content).map_err(GeneratorError::Json)?;
let mut generated_code = String::new();
let mut seen_constants = std::collections::HashSet::new();
for constant in &constants_array {
if seen_constants.contains(&constant.name) {
continue;
}
seen_constants.insert(constant.name.clone());
generated_code.push_str(&self.generate_constant(constant));
generated_code.push('\n');
}
let output_path = output_dir.join(self.output_file());
fs::write(output_path, generated_code).map_err(GeneratorError::Io)?;
let metadata = self.collect_metadata(input_dir)?;
log_debug(&format!(
"ConstantGenerator uses {} external types",
metadata.used_types.len()
));
log_info(&format!("Generated {} constants", constants_array.len()));
Ok(())
}
fn metadata(&self) -> GeneratorMetadata {
GeneratorMetadata {
defined_types: Vec::new(),
used_types: Vec::new(),
has_forward_declarations: false,
priority: 10, }
}
fn collect_metadata(&self, input_dir: &Path) -> GeneratorResult<GeneratorMetadata> {
let mut defined_constants = Vec::new();
let _used_types = Vec::new();
let input_path = input_dir.join("constants.json");
let input_content = std::fs::read_to_string(input_path).map_err(GeneratorError::Io)?;
let constants_array: Vec<ConstantDefinition> =
serde_json::from_str(&input_content).map_err(GeneratorError::Json)?;
for constant in &constants_array {
defined_constants.push(constant.name.clone());
}
log_debug(&format!(
"ConstantGenerator found {} constants",
defined_constants.len()
));
Ok(GeneratorMetadata {
defined_types: Vec::new(),
used_types: _used_types,
has_forward_declarations: false,
priority: 10,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_constant_generation() {
let generator = ConstantGenerator::new();
let constant = ConstantDefinition {
name: "VK_API_VERSION_1_0".to_string(),
value: Some("0x100000".to_string()),
source_line: None,
alias: None,
comment: None,
api: None,
deprecated: None,
constant_type: "enum".to_string(),
raw_content: String::new(),
is_alias: false,
};
let generated = generator.generate_constant(&constant);
assert!(generated.contains("pub const VK_API_VERSION_1_0: u32 = 0x100000;"));
}
#[test]
fn test_value_mapping() {
let generator = ConstantGenerator::new();
assert_eq!(generator.map_value("0x1000", "uint32_t"), "0x1000");
assert_eq!(generator.map_value("42", "float"), "42.0");
assert_eq!(generator.map_value("123", "uint32_t"), "123");
}
}