use std::collections::HashMap;
use crate::cli::ApiRequest;
use crate::ir::{RawCommand, RawSpec};
use super::selection::{SelectedExt, SelectedFeature, api_profile_matches};
use super::types::{AliasPair, Command, Param};
pub(super) fn build_command(
index: u16,
raw: &RawCommand,
scope: &str,
protect: Option<String>,
pfn_prefix: &str,
name_prefix: &str,
) -> Command {
let short_name = raw
.name
.strip_prefix(name_prefix)
.unwrap_or(&raw.name)
.to_string();
let pfn_type = if pfn_prefix == "PFN_" {
format!("PFN_{}", raw.name)
} else {
let stem = raw.name.strip_prefix(name_prefix).unwrap_or(&raw.name);
format!("{}{}PROC", pfn_prefix, stem.to_uppercase())
};
let params: Vec<Param> = raw
.params
.iter()
.map(|p| Param {
type_raw: p.type_raw.clone(),
name: p.name.clone(),
})
.collect();
let params_str = if params.is_empty() {
"void".to_string()
} else {
params
.iter()
.map(|p| {
if p.name.is_empty() {
p.type_raw.clone()
} else if p.type_raw.trim_end().ends_with(']') {
p.type_raw.trim().to_string()
} else {
format!("{} {}", p.type_raw, p.name)
}
})
.collect::<Vec<_>>()
.join(", ")
};
Command {
index,
name: raw.name.clone(),
short_name,
pfn_type,
return_type: raw.return_type.clone(),
params_str,
params,
scope: scope.to_string(),
protect,
name_offset: 0, }
}
pub(super) fn build_command_protect_map<'a>(exts: &[SelectedExt<'a>]) -> HashMap<&'a str, String> {
let mut map = HashMap::new();
for ext in exts {
if let Some(protect) = ext.raw.protect.first() {
for require in &ext.raw.requires {
for cmd in &require.commands {
map.entry(cmd.as_str()).or_insert_with(|| protect.clone());
}
}
}
}
map
}
pub(super) fn optimize_command_order(
core_cmds: &[String],
ext_cmds: &[String],
selected_features: &[SelectedFeature<'_>],
selected_exts: &[SelectedExt<'_>],
requests: &[ApiRequest],
) -> (Vec<String>, Vec<String>) {
let num_features = selected_features.len();
let mut consumers: HashMap<&str, Vec<u32>> = HashMap::new();
for (fi, feat) in selected_features.iter().enumerate() {
let profile = requests
.iter()
.find(|r| r.name == feat.api)
.and_then(|r| r.profile.as_deref());
for require in &feat.raw.requires {
if !api_profile_matches(
require.api.as_deref(),
require.profile.as_deref(),
&feat.api,
profile,
) {
continue;
}
for cmd in &require.commands {
consumers.entry(cmd.as_str()).or_default().push(fi as u32);
}
}
}
for (ei, ext) in selected_exts.iter().enumerate() {
for require in &ext.raw.requires {
for cmd in &require.commands {
consumers
.entry(cmd.as_str())
.or_default()
.push((num_features + ei) as u32);
}
}
}
for list in consumers.values_mut() {
list.sort_unstable();
list.dedup();
}
let sort_by_consumers = |names: &[String]| -> Vec<String> {
let mut sorted = names.to_vec();
sorted.sort_by(|a, b| {
let ca = consumers.get(a.as_str()).map(Vec::as_slice).unwrap_or(&[]);
let cb = consumers.get(b.as_str()).map(Vec::as_slice).unwrap_or(&[]);
ca.cmp(cb).then_with(|| a.cmp(b)) });
sorted
};
(sort_by_consumers(core_cmds), sort_by_consumers(ext_cmds))
}
pub(super) fn build_alias_pairs(raw: &RawSpec, commands: &[Command]) -> Vec<AliasPair> {
let idx: HashMap<&str, u16> = commands
.iter()
.map(|c| (c.name.as_str(), c.index))
.collect();
let mut groups: HashMap<String, Vec<String>> = HashMap::new();
for (name, cmd) in &raw.commands {
if let Some(ref alias) = cmd.alias {
if !idx.contains_key(name.as_str()) || !idx.contains_key(alias.as_str()) {
continue;
}
let (canonical, secondary) = if alias.len() < name.len()
|| (alias.len() == name.len() && alias.as_str() < name.as_str())
{
(alias.clone(), name.clone())
} else {
(name.clone(), alias.clone())
};
groups.entry(canonical).or_default().push(secondary);
}
}
let mut pairs: Vec<AliasPair> = Vec::new();
for (canonical, secondaries) in groups {
let Some(&ci) = idx.get(canonical.as_str()) else {
continue;
};
for secondary in secondaries {
let Some(&si) = idx.get(secondary.as_str()) else {
continue;
};
pairs.push(AliasPair {
canonical: ci,
secondary: si,
});
}
}
pairs.sort_by_key(|p| (p.canonical, p.secondary));
pairs
}