/* 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, skip_if: Option, } impl Annotation { fn new(key: &Yaml, value: &Yaml) -> Result { 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 { 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, 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::::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 { 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 { 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 { 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 { 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(()) }