use std::env;
use std::fmt::Write as _;
use std::fs;
use std::path::PathBuf;
fn parameters(specification: &str) -> String {
if specification == "-" {
return "&[]".to_owned();
}
let values = specification
.split(',')
.map(|value| match value {
"n" => "ParameterType::Number",
"b" => "ParameterType::Bytes",
_ => panic!("unknown parameter type {value:?}"),
})
.collect::<Vec<_>>()
.join(", ");
format!("&[{values}]")
}
fn main() {
println!("cargo:rerun-if-changed=capabilities.tsv");
let input = fs::read_to_string("capabilities.tsv").expect("read capabilities.tsv");
let mut groups = [Vec::new(), Vec::new(), Vec::new()];
for (line_no, line) in input.lines().enumerate() {
if line.is_empty() || line.starts_with('#') {
continue;
}
let fields: Vec<_> = line.split('\t').collect();
assert_eq!(fields.len(), 6, "capabilities.tsv:{}", line_no + 1);
let group = match fields[0] {
"bool" => 0,
"num" => 1,
"str" => 2,
_ => panic!("unknown capability kind on line {}", line_no + 1),
};
groups[group].push((fields[1], fields[2], fields[3], fields[4], fields[5]));
}
let mut output = String::from("// @generated by build.rs from capabilities.tsv\n");
for (group, type_name) in groups.iter().zip(["BooleanCap", "NumericCap", "StringCap"]) {
writeln!(
output,
"/// Generated fixed-index capability identifier.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]\npub struct {type_name}(u16);"
)
.unwrap();
writeln!(output, "impl {type_name} {{").unwrap();
writeln!(
output,
" /// Number of slots in this binary section.\n pub const COUNT: usize = {};",
group.len()
)
.unwrap();
for (index, (long, short, _, _, _)) in group.iter().enumerate() {
writeln!(
output,
" /// `{short}` (`{long}`).\n pub const {}: Self = Self({index});",
long.to_uppercase(),
)
.unwrap();
}
writeln!(
output,
" /// All identifiers in binary-slot order.\n pub const ALL: &'static [Self] = &["
)
.unwrap();
for (long, _, _, _, _) in group {
writeln!(output, " Self::{},", long.to_uppercase()).unwrap();
}
writeln!(output, " ];").unwrap();
writeln!(
output,
" pub(crate) const fn index(self) -> usize {{ self.0 as usize }}"
)
.unwrap();
writeln!(output, " /// Returns names, parameter types, and version metadata.\n pub fn metadata(self) -> &'static CapabilityMetadata {{ &{}_METADATA[self.index()] }}", type_name.to_uppercase()).unwrap();
writeln!(
output,
" /// Returns the terminfo short name.\n pub fn short_name(self) -> &'static str {{ self.metadata().short }}"
)
.unwrap();
writeln!(
output,
" /// Returns the descriptive terminfo name.\n pub fn long_name(self) -> &'static str {{ self.metadata().long }}"
)
.unwrap();
writeln!(
output,
" /// Returns the historical termcap code when defined.\n pub fn termcap_name(self) -> Option<&'static str> {{ self.metadata().termcap }}"
)
.unwrap();
writeln!(output, " pub(crate) fn from_index(index: usize) -> Option<Self> {{ Self::ALL.get(index).copied() }}").unwrap();
writeln!(output, " pub(crate) fn find_any(name: &str) -> Option<Self> {{ {}_METADATA.iter().position(|m| m.matches_any(name)).and_then(Self::from_index) }}", type_name.to_uppercase()).unwrap();
writeln!(output, " /// Finds the first exact name in one namespace.\n pub fn lookup(namespace: NameNamespace, name: &str) -> Option<Self> {{ {}_METADATA.iter().position(|m| m.name(namespace) == Some(name)).and_then(Self::from_index) }}", type_name.to_uppercase()).unwrap();
writeln!(output, "}}\n").unwrap();
writeln!(
output,
"/// Metadata in compiled binary-slot order.\npub static {}_METADATA: [CapabilityMetadata; {}] = [",
type_name.to_uppercase(),
group.len()
)
.unwrap();
for (long, short, termcap, parameter_specification, version) in group {
let termcap = if *termcap == "-" {
"None".to_owned()
} else {
format!("Some({termcap:?})")
};
let parameters = parameters(parameter_specification);
let introduced = match *version {
"system-v" => "CapabilityVersion::SystemV",
_ => panic!("unknown capability version {version:?}"),
};
writeln!(
output,
" CapabilityMetadata {{ long: {long:?}, short: {short:?}, termcap: {termcap}, parameters: {parameters}, introduced: {introduced} }},"
)
.unwrap();
}
writeln!(output, "];\n").unwrap();
}
let out = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR"));
fs::write(out.join("caps_generated.rs"), output).expect("write generated capability table");
}