diff --git a/src/main.rs b/src/main.rs index 14940ca..9590299 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,15 @@ struct TargetDocs { maintainers: Vec, sections: Vec<(String, String)>, tier: String, + // TODO: Make this mandatory. + metadata: Option, +} + +/// Metadata for the table +struct TargetMetadata { + notes: String, + std: String, + host: String, } const SECTIONS: &[&str] = &[ @@ -57,7 +66,14 @@ fn main() -> Result<()> { let mut info_patterns = parse::load_target_infos(Path::new(input_dir)) .unwrap() .into_iter() - .map(|info| TargetPatternEntry { info, used: false }) + .map(|info| { + let metadata_used = vec![false; info.metadata.len()]; + TargetPatternEntry { + info, + used: false, + metadata_used, + } + }) .collect::>(); eprintln!("Collecting rustc information"); @@ -66,16 +82,21 @@ fn main() -> Result<()> { .map(|target| rustc_target_info(&rustc, target)) .collect::>(); + let targets = targets + .into_iter() + .map(|target| target_doc_info(&mut info_patterns, target)) + .zip(rustc_infos) + .collect::>(); + eprintln!("Rendering targets"); - for (target, rustc_info) in std::iter::zip(&targets, rustc_infos) { - let info = target_info(&mut info_patterns, target); - let doc = render::render_target_md(&info, &rustc_info); + for (info, rustc_info) in &targets { + let doc = render::render_target_md(info, rustc_info); std::fs::write( Path::new(output_src) .join("platform-support") .join("targets") - .join(format!("{target}.md")), + .join(format!("{}.md", info.name)), doc, ) .wrap_err("writing target file")?; @@ -88,9 +109,21 @@ fn main() -> Result<()> { target_pattern.info.pattern ); } + + for (used, meta) in + std::iter::zip(target_pattern.metadata_used, target_pattern.info.metadata) + { + if !used { + bail!( + "in target pattern `{}`, the metadata pattern `{}` was never used", + target_pattern.info.pattern, + meta.pattern + ); + } + } } - render::render_static(&Path::new(output_src).join("platform-support"), &targets)?; + render::render_static(Path::new(output_src), &targets)?; eprintln!("Finished generating target docs"); Ok(()) @@ -99,18 +132,20 @@ fn main() -> Result<()> { struct TargetPatternEntry { info: ParsedTargetInfoFile, used: bool, + metadata_used: Vec, } -/// Gets the target information from `target_info.toml` by applying all patterns that match. -fn target_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> TargetDocs { +fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> TargetDocs { let mut tier = None; let mut maintainers = Vec::new(); let mut sections = Vec::new(); - for target_pattern in info_patterns { - if glob_match::glob_match(&target_pattern.info.pattern, target) { - target_pattern.used = true; - let target_pattern = &target_pattern.info; + let mut metadata = None; + + for target_pattern_entry in info_patterns { + if glob_match::glob_match(&target_pattern_entry.info.pattern, target) { + target_pattern_entry.used = true; + let target_pattern = &target_pattern_entry.info; maintainers.extend_from_slice(&target_pattern.maintainers); @@ -127,6 +162,20 @@ fn target_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> Target } sections.push((section_name.clone(), content.clone())); } + + for (i, metadata_pattern) in target_pattern.metadata.iter().enumerate() { + if glob_match::glob_match(&metadata_pattern.pattern, target) { + target_pattern_entry.metadata_used[i] = true; + if metadata.is_some() { + panic!("target {target} is assigned metadata from more than one pattern"); + } + metadata = Some(TargetMetadata { + notes: metadata_pattern.notes.clone(), + host: metadata_pattern.host.clone(), + std: metadata_pattern.std.clone(), + }); + } + } } } @@ -136,6 +185,7 @@ fn target_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> Target // tier: tier.expect(&format!("no tier found for target {target}")), tier: tier.unwrap_or("UNKNOWN".to_owned()), sections, + metadata, } } diff --git a/src/parse.rs b/src/parse.rs index 6e9171e..989c4dd 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -3,19 +3,32 @@ use eyre::{bail, OptionExt, Result, WrapErr}; use std::{fs::DirEntry, path::Path}; -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub struct ParsedTargetInfoFile { pub pattern: String, pub tier: Option, pub maintainers: Vec, pub sections: Vec<(String, String)>, + pub metadata: Vec, } #[derive(serde::Deserialize)] +#[serde(deny_unknown_fields)] struct Frontmatter { tier: Option, #[serde(default)] maintainers: Vec, + #[serde(default)] + metadata: Vec, +} + +#[derive(Debug, Clone, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ParsedTargetMetadata { + pub pattern: String, + pub notes: String, + pub std: String, + pub host: String, } pub fn load_target_infos(directory: &Path) -> Result> { @@ -93,6 +106,7 @@ fn parse_file(name: &str, content: &str) -> Result { maintainers: frontmatter.maintainers, tier: frontmatter.tier, sections, + metadata: frontmatter.metadata, }) } @@ -130,7 +144,7 @@ mod tests { assert!(super::parse_file(name, content).is_err()); } - + #[test] fn parse_correctly() { let name = "cat-unknown-linux-gnu.md"; diff --git a/src/render.rs b/src/render.rs index 049a763..6b58a37 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,4 +1,4 @@ -use eyre::{Context, OptionExt, Result}; +use eyre::{bail, Context, OptionExt, Result}; use std::{fs, path::Path}; use crate::{is_in_rust_lang_rust, RustcTargetInfo, TargetDocs}; @@ -91,13 +91,13 @@ fn replace_section(prev_content: &str, section_name: &str, replacement: &str) -> } /// Renders the non-target files like `SUMMARY.md` that depend on the target. -pub fn render_static(platform_support: &Path, targets: &[&str]) -> Result<()> { - let targets_file = platform_support.join("targets.md"); +pub fn render_static(src_output: &Path, targets: &[(TargetDocs, RustcTargetInfo)]) -> Result<()> { + let targets_file = src_output.join("platform-support").join("targets.md"); let old_targets = fs::read_to_string(&targets_file).wrap_err("reading summary file")?; let target_list = targets .iter() - .map(|target| format!("- [{0}](targets/{0}.md)", target)) + .map(|(target, _)| format!("- [{0}](targets/{0}.md)", target.name)) .collect::>() .join("\n"); @@ -127,6 +127,51 @@ pub fn render_static(platform_support: &Path, targets: &[&str]) -> Result<()> { } // TODO: Render the nice table showing off all targets and their tier. + let platform_support_main = src_output.join("platform-support.md"); + let platform_support_main_old = + fs::read_to_string(&platform_support_main).wrap_err("reading platform-support.md")?; + + let tier3_table = + render_table_with_host(targets.into_iter().filter(|target| target.0.tier == "3")) + .wrap_err("rendering tier 3 table")?; + + let platform_support_main_new = + replace_section(&platform_support_main_old, "TIER3", &tier3_table) + .wrap_err("replacing platform support.md")?; + + fs::write(platform_support_main, platform_support_main_new) + .wrap_err("writing platform-support.md")?; Ok(()) } + +fn render_table_with_host<'a>( + targets: impl IntoIterator, +) -> Result { + let mut rows = Vec::new(); + + for (target, _) in targets { + let meta = target.metadata.as_ref(); + let std = match meta.map(|meta| meta.std.as_str()) { + Some("true") => "✓", + Some("unknown") => "?", + Some("false") => " ", + None => "?", + _ => bail!("invalid value for std todo parse early"), + }; + let host = match meta.map(|meta| meta.host.as_str()) { + Some("true") => "✓", + Some("unknown") => "?", + Some("false") => " ", + None => "?", + _ => bail!("invalid value for host todo parse early"), + }; + let notes = meta.map(|meta| meta.notes.as_str()).unwrap_or("unknown"); + rows.push(format!( + "[`{0}`](platform-support/targets/{0}.md) | {std} | {host} | {notes}", + target.name + )); + } + + Ok(rows.join("\n")) +}