diff --git a/src/main.rs b/src/main.rs index af85c4e..1c0c288 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,12 +11,14 @@ use eyre::{bail, Context, OptionExt, Result}; use parse::ParsedTargetInfoFile; use serde::Deserialize; -/// Information about a target obtained from the target_info markdown file. -struct TargetDocs { +/// Information about a target obtained from the markdown and rustc. +struct TargetInfo { name: String, maintainers: Vec, sections: Vec<(String, String)>, footnotes: Vec, + target_cfgs: Vec<(String, String)>, + metadata: RustcTargetMetadata, } /// All the sections that we want every doc page to have. @@ -56,29 +58,48 @@ fn main() -> Result<()> { .wrap_err("failed loading target_info")? .into_iter() .map(|info| { - let footnotes_used = - info.footnotes.iter().map(|(target, _)| (target.clone(), false)).collect(); - TargetPatternEntry { info, used: false, footnotes_used } + let footnotes_used = info + .footnotes + .keys() + .map(|target| (target.clone(), false)) + .collect(); + TargetPatternEntry { + info, + used: false, + footnotes_used, + } }) .collect::>(); eprintln!("Collecting rustc information"); - let rustc_infos = - targets.iter().map(|target| rustc_target_info(&rustc, target)).collect::>(); + let rustc_infos = targets + .iter() + .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) + .map(|(md, rustc)| TargetInfo { + name: md.name, + maintainers: md.maintainers, + sections: md.sections, + footnotes: md.footnotes, + target_cfgs: rustc.target_cfgs, + metadata: rustc.metadata, + }) .collect::>(); eprintln!("Rendering targets check_only={check_only}"); - let targets_dir = Path::new(output_src).join("platform-support").join("targets"); + let targets_dir = Path::new(output_src) + .join("platform-support") + .join("targets"); if !check_only { std::fs::create_dir_all(&targets_dir).wrap_err("creating platform-support/targets dir")?; } - for (info, rustc_info) in &targets { - let doc = render::render_target_md(info, rustc_info); + for info in &targets { + let doc = render::render_target_md(info); if !check_only { std::fs::write(targets_dir.join(format!("{}.md", info.name)), doc) @@ -88,7 +109,10 @@ fn main() -> Result<()> { for target_pattern in info_patterns { if !target_pattern.used { - bail!("target pattern `{}` was never used", target_pattern.info.pattern); + bail!( + "target pattern `{}` was never used", + target_pattern.info.pattern + ); } for footnote_target in target_pattern.info.footnotes.keys() { @@ -115,7 +139,15 @@ struct TargetPatternEntry { footnotes_used: HashMap, } -fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> TargetDocs { +/// Information about a target obtained from the target_info markdown file. +struct TargetInfoMd { + name: String, + maintainers: Vec, + sections: Vec<(String, String)>, + footnotes: Vec, +} + +fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> TargetInfoMd { let mut maintainers = Vec::new(); let mut sections = Vec::new(); @@ -128,7 +160,6 @@ fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> Ta maintainers.extend_from_slice(&target_pattern.maintainers); - for (section_name, content) in &target_pattern.sections { if sections.iter().any(|(name, _)| name == section_name) { panic!( @@ -139,7 +170,9 @@ fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> Ta } if let Some(target_footnotes) = target_pattern.footnotes.get(target) { - target_pattern_entry.footnotes_used.insert(target.to_owned(), true); + target_pattern_entry + .footnotes_used + .insert(target.to_owned(), true); if !footnotes.is_empty() { panic!("target {target} is assigned metadata from more than one pattern"); @@ -149,7 +182,12 @@ fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> Ta } } - TargetDocs { name: target.to_owned(), maintainers, sections, footnotes } + TargetInfoMd { + name: target.to_owned(), + maintainers, + sections, + footnotes, + } } /// Information about a target obtained from rustc. @@ -173,7 +211,7 @@ fn rustc_target_info(rustc: &Path, target: &str) -> RustcTargetInfo { .lines() .filter_map(|line| { if line.starts_with("target_") { - let Some((key, value)) = line.split_once("=") else { + let Some((key, value)) = line.split_once('=') else { // For example `unix` return None; }; @@ -191,12 +229,21 @@ fn rustc_target_info(rustc: &Path, target: &str) -> RustcTargetInfo { let json_spec = rustc_stdout( rustc, - &["-Zunstable-options", "--print", "target-spec-json", "--target", target], + &[ + "-Zunstable-options", + "--print", + "target-spec-json", + "--target", + target, + ], ); let spec = serde_json::from_str::(&json_spec) .expect("parsing --print target-spec-json for metadata"); - RustcTargetInfo { target_cfgs, metadata: spec.metadata } + RustcTargetInfo { + target_cfgs, + metadata: spec.metadata, + } } fn rustc_stdout(rustc: &Path, args: &[&str]) -> String { diff --git a/src/parse.rs b/src/parse.rs index e8b93b5..dd03f28 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -70,7 +70,9 @@ fn load_single_target_info(entry: &DirEntry) -> Result { fn parse_file(name: &str, content: &str) -> Result { let mut frontmatter_splitter = content.split("---\n"); - let frontmatter = frontmatter_splitter.nth(1).ok_or_eyre("missing frontmatter")?; + let frontmatter = frontmatter_splitter + .nth(1) + .ok_or_eyre("missing frontmatter")?; let frontmatter_line_count = frontmatter.lines().count() + 2; // 2 from --- @@ -86,7 +88,7 @@ fn parse_file(name: &str, content: &str) -> Result { let number = frontmatter_line_count + idx + 1; // 1 because "line numbers" are off by 1 if line.starts_with("```") { in_codeblock ^= true; // toggle - } else if line.starts_with("#") { + } else if line.starts_with('#') { if in_codeblock { match sections.last_mut() { Some((_, content)) => { @@ -121,7 +123,9 @@ fn parse_file(name: &str, content: &str) -> Result { } } - sections.iter_mut().for_each(|section| section.1 = section.1.trim().to_owned()); + sections + .iter_mut() + .for_each(|section| section.1 = section.1.trim().to_owned()); Ok(ParsedTargetInfoFile { pattern: name.to_owned(), diff --git a/src/parse/tests.rs b/src/parse/tests.rs index cdaaa1f..6eb3593 100644 --- a/src/parse/tests.rs +++ b/src/parse/tests.rs @@ -1,5 +1,3 @@ -use crate::parse::Tier; - #[test] fn no_frontmatter() { let name = "archlinux-unknown-linux-gnu.md"; // arch linux is an arch, right? @@ -38,7 +36,6 @@ fn parse_correctly() { let name = "cat-unknown-linux-gnu.md"; let content = r#" --- -tier: "1" # first-class cats maintainers: ["who maintains the cat?"] --- ## Requirements @@ -59,7 +56,6 @@ But it should be possible. assert_eq!(info.maintainers, vec!["who maintains the cat?"]); assert_eq!(info.pattern, name); - assert_eq!(info.tier, Some(Tier::One)); assert_eq!( info.sections, vec![ diff --git a/src/render.rs b/src/render.rs index 9674b8d..a28ffdb 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,29 +1,27 @@ use eyre::{Context, OptionExt, Result}; use std::{fs, path::Path}; -use crate::{RustcTargetInfo, TargetDocs}; +use crate::TargetInfo; /// Renders a single target markdown file from the information obtained. -pub fn render_target_md(target: &TargetDocs, rustc_info: &RustcTargetInfo) -> String { - let render_header_option_bool = |bool| { - match bool { - Some(true) => "Yes", - Some(false) => "No", - None => "?", - } +pub fn render_target_md(target: &TargetInfo) -> String { + let render_header_option_bool = |bool| match bool { + Some(true) => "Yes", + Some(false) => "No", + None => "?", }; let mut doc = format!( "# {}\n\n**Tier: {}**\n\n**std: {}**\n\n**host tools: {}**\n\n", target.name, - match rustc_info.metadata.tier { + match target.metadata.tier { Some(1) => "1", Some(2) => "2", Some(3) => "3", _ => "UNKNOWN", }, - render_header_option_bool(rustc_info.metadata.std), - render_header_option_bool(rustc_info.metadata.host_tools), + render_header_option_bool(target.metadata.std), + render_header_option_bool(target.metadata.host_tools), ); let mut section = |name: &str, content: &str| { @@ -43,7 +41,7 @@ pub fn render_target_md(target: &TargetDocs, rustc_info: &RustcTargetInfo) -> St .maintainers .iter() .map(|maintainer| { - let maintainer = if maintainer.starts_with('@') && !maintainer.contains(" ") { + let maintainer = if maintainer.starts_with('@') && !maintainer.contains(' ') { format!( "[@{0}](https://github.com/{0})", maintainer.strip_prefix("@").unwrap() @@ -62,16 +60,19 @@ pub fn render_target_md(target: &TargetDocs, rustc_info: &RustcTargetInfo) -> St section("Maintainers", &maintainers_content); for section_name in crate::SECTIONS { - let value = target.sections.iter().find(|(name, _)| name == section_name); + let value = target + .sections + .iter() + .find(|(name, _)| name == section_name); let section_content = match value { Some((_, value)) => value.clone(), None => "Unknown.".to_owned(), }; - section(§ion_name, §ion_content); + section(section_name, §ion_content); } - let cfg_text = rustc_info + let cfg_text = target .target_cfgs .iter() .map(|(key, value)| format!("- `{key}` = `{value}`")) @@ -105,17 +106,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( - check_only: bool, - src_output: &Path, - targets: &[(TargetDocs, RustcTargetInfo)], -) -> Result<()> { +pub fn render_static(check_only: bool, src_output: &Path, targets: &[TargetInfo]) -> 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}](platform-support/targets/{0}.md)", target.name)) + .map(|target| format!("- [{0}](platform-support/targets/{0}.md)", target.name)) .collect::>() .join("\n"); @@ -140,9 +137,12 @@ pub fn render_static( let summary = src_output.join("SUMMARY.md"); let summary_old = fs::read_to_string(&summary).wrap_err("reading SUMMARY.md")?; // indent the list - let summary_new = - replace_section(&summary_old, "TARGET_LIST", &target_list.replace("- ", " - ")) - .wrap_err("replacig SUMMARY.md")?; + let summary_new = replace_section( + &summary_old, + "TARGET_LIST", + &target_list.replace("- ", " - "), + ) + .wrap_err("replacig SUMMARY.md")?; if !check_only { fs::write(summary, summary_new).wrap_err("writing SUMAMRY.md")?; } @@ -150,10 +150,13 @@ pub fn render_static( Ok(()) } -fn render_platform_support_tables( - content: &str, - targets: &[(TargetDocs, RustcTargetInfo)], -) -> Result { +impl TargetInfo { + fn has_host_tools(&self) -> bool { + self.metadata.host_tools.unwrap_or(false) + } +} + +fn render_platform_support_tables(content: &str, targets: &[TargetInfo]) -> Result { let replace_table = |content, name, tier_table| -> Result { let section_string = render_table(targets, tier_table)?; replace_section(content, name, §ion_string).wrap_err("replacing platform support.md") @@ -163,7 +166,7 @@ fn render_platform_support_tables( content, "TIER1HOST", TierTable { - filter: |target| target.1.metadata.tier == Some(1), + filter: |target| target.metadata.tier == Some(1), include_host: false, include_std: false, }, @@ -172,9 +175,7 @@ fn render_platform_support_tables( &content, "TIER2HOST", TierTable { - filter: |target| { - target.1.metadata.tier == Some(2) && target.1.metadata.host_tools.unwrap_or(false) - }, + filter: |target| target.metadata.tier == Some(2) && target.has_host_tools(), include_host: false, include_std: false, }, @@ -183,9 +184,7 @@ fn render_platform_support_tables( &content, "TIER2", TierTable { - filter: |target| { - target.1.metadata.tier == Some(2) && !target.1.metadata.host_tools.unwrap_or(false) - }, + filter: |target| target.metadata.tier == Some(2) && !target.has_host_tools(), include_host: false, include_std: true, }, @@ -194,7 +193,7 @@ fn render_platform_support_tables( &content, "TIER3", TierTable { - filter: |target| target.1.metadata.tier == Some(3), + filter: |target| target.metadata.tier == Some(3), include_host: true, include_std: true, }, @@ -212,18 +211,18 @@ fn render_table_option_bool(bool: Option) -> &'static str { } struct TierTable { - filter: fn(&(TargetDocs, RustcTargetInfo)) -> bool, + filter: fn(&TargetInfo) -> bool, include_std: bool, include_host: bool, } -fn render_table(targets: &[(TargetDocs, RustcTargetInfo)], table: TierTable) -> Result { +fn render_table(targets: &[TargetInfo], table: TierTable) -> Result { let mut rows = Vec::new(); - let targets = targets.into_iter().filter(|target| (table.filter)(&target)); + let targets = targets.iter().filter(|target| (table.filter)(target)); - for (target, rustc_info) in targets { - let meta = &rustc_info.metadata; + for target in targets { + let meta = &target.metadata; let mut notes = meta.description.as_deref().unwrap_or("unknown").to_owned();