168 lines
5.3 KiB
Rust
168 lines
5.3 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
use std::env;
|
|
use std::fs::{self, File};
|
|
use std::io::{Error, Write};
|
|
use std::path::Path;
|
|
use std::vec::Vec;
|
|
|
|
use yaml_rust::{Yaml, YamlLoader};
|
|
|
|
fn main() -> Result<(), Error> {
|
|
generate_annotations()?;
|
|
Ok(())
|
|
}
|
|
|
|
struct Annotation {
|
|
name: String,
|
|
type_string: String,
|
|
altname: Option<String>,
|
|
skip_if: Option<String>,
|
|
}
|
|
|
|
impl Annotation {
|
|
fn new(key: &Yaml, value: &Yaml) -> Result<Annotation, Error> {
|
|
let name = key.as_str().ok_or(Error::other("Not a string"))?;
|
|
let raw_type = value["type"].as_str().ok_or(Error::other("Missing type"))?;
|
|
let type_string = Annotation::type_str_to_value(raw_type)?;
|
|
let altname = value["altname"].as_str().map(str::to_owned);
|
|
// We don't care about the contents of the `ping` field for the time being
|
|
let skip_if = value["skip_if"].as_str().map(str::to_owned);
|
|
|
|
Ok(Annotation {
|
|
name: name.to_owned(),
|
|
type_string,
|
|
altname,
|
|
skip_if,
|
|
})
|
|
}
|
|
|
|
fn type_str_to_value(raw_type: &str) -> Result<String, Error> {
|
|
match raw_type {
|
|
"string" => Ok("String"),
|
|
"boolean" => Ok("Boolean"),
|
|
"u32" => Ok("U32"),
|
|
"u64" => Ok("U64"),
|
|
"usize" => Ok("USize"),
|
|
"object" => Ok("Object"),
|
|
_ => Err(Error::other("Invalid type")),
|
|
}
|
|
.map(str::to_owned)
|
|
}
|
|
}
|
|
|
|
fn read_annotations(doc: &Yaml) -> Result<Vec<Annotation>, Error> {
|
|
let raw_annotations = doc.as_hash().ok_or(Error::other(
|
|
"Invalid data in YAML file, expected a hash".to_string(),
|
|
))?;
|
|
|
|
let mut annotations = Vec::<Annotation>::new();
|
|
for annotation in raw_annotations {
|
|
let annotation = Annotation::new(annotation.0, annotation.1)?;
|
|
annotations.push(annotation);
|
|
}
|
|
|
|
annotations.sort_by(|a, b| {
|
|
let a_lower = a.name.to_ascii_lowercase();
|
|
let b_lower = b.name.to_ascii_lowercase();
|
|
a_lower.cmp(&b_lower)
|
|
});
|
|
|
|
Ok(annotations)
|
|
}
|
|
|
|
fn generate_annotation_enum(annotations: &[Annotation]) -> Result<String, Error> {
|
|
let mut annotations_enum = String::new();
|
|
|
|
for (index, value) in annotations.iter().enumerate() {
|
|
let entry = format!(" {} = {index:},\n", value.name);
|
|
annotations_enum.push_str(&entry);
|
|
}
|
|
|
|
let count = format!(" Count = {},", annotations.len());
|
|
annotations_enum.push_str(&count);
|
|
|
|
Ok(annotations_enum)
|
|
}
|
|
|
|
fn generate_annotation_types(annotations: &[Annotation]) -> Result<String, Error> {
|
|
let mut types_array = String::new();
|
|
for value in annotations.iter() {
|
|
let entry = format!(" CrashAnnotationType::{},\n", value.type_string);
|
|
types_array.push_str(&entry);
|
|
}
|
|
|
|
// Pop the last newline
|
|
types_array.pop();
|
|
|
|
Ok(types_array)
|
|
}
|
|
|
|
fn generate_annotation_names(annotations: &[Annotation]) -> Result<String, Error> {
|
|
let mut names_array = String::new();
|
|
for value in annotations.iter() {
|
|
let name = value.altname.as_ref().unwrap_or(&value.name);
|
|
let entry = format!(" \"{}\",\n", name);
|
|
names_array.push_str(&entry);
|
|
}
|
|
|
|
// Pop the last newline
|
|
names_array.pop();
|
|
|
|
Ok(names_array)
|
|
}
|
|
|
|
fn generate_annotation_skiplist(annotations: &[Annotation]) -> Result<String, Error> {
|
|
let mut skiplist = String::new();
|
|
for annotation in annotations.iter() {
|
|
if let Some(skip_if) = &annotation.skip_if {
|
|
let entry = format!(
|
|
" CrashAnnotationSkipValue {{ annotation: CrashAnnotation::{}, value: b\"{}\" }},\n",
|
|
&annotation.name, skip_if
|
|
);
|
|
skiplist.push_str(&entry);
|
|
}
|
|
}
|
|
|
|
// Pop the last newline
|
|
skiplist.pop();
|
|
|
|
Ok(skiplist)
|
|
}
|
|
|
|
// Generate Rust code to manipulate crash annotations
|
|
fn generate_annotations() -> Result<(), Error> {
|
|
const CRASH_ANNOTATIONS_YAML: &str = "../CrashAnnotations.yaml";
|
|
const CRASH_ANNOTATIONS_TEMPLATE: &str = "../crash_annotations.rs.in";
|
|
|
|
let out_dir = env::var("OUT_DIR").unwrap();
|
|
let annotations_path = Path::new(&out_dir).join("crash_annotations.rs");
|
|
let mut annotations_file = File::create(annotations_path)?;
|
|
|
|
let template = fs::read_to_string(CRASH_ANNOTATIONS_TEMPLATE)?;
|
|
let yaml_str = fs::read_to_string(CRASH_ANNOTATIONS_YAML)?;
|
|
let yaml_doc = YamlLoader::load_from_str(&yaml_str)
|
|
.map_err(|e| Error::other(format!("Failed to parse YAML file: {}", e)))?;
|
|
|
|
let doc = &yaml_doc[0];
|
|
let annotations = read_annotations(doc)?;
|
|
|
|
let annotations_enum = generate_annotation_enum(&annotations)?;
|
|
let annotations_types = generate_annotation_types(&annotations)?;
|
|
let annotations_names = generate_annotation_names(&annotations)?;
|
|
let skiplist = generate_annotation_skiplist(&annotations)?;
|
|
|
|
let compiled_template = template
|
|
.replace("${enum}", &annotations_enum)
|
|
.replace("${types}", &annotations_types)
|
|
.replace("${names}", &annotations_names)
|
|
.replace("${skiplist}", &skiplist);
|
|
write!(&mut annotations_file, "{}", compiled_template)?;
|
|
|
|
println!("cargo:rerun-if-changed={CRASH_ANNOTATIONS_YAML}");
|
|
println!("cargo:rerun-if-changed={CRASH_ANNOTATIONS_TEMPLATE}");
|
|
|
|
Ok(())
|
|
}
|