target tables

This commit is contained in:
nora 2024-02-08 17:50:24 +01:00
parent 09ee1f7a33
commit 8590f9b343
3 changed files with 172 additions and 47 deletions

View file

@ -8,7 +8,7 @@ use std::{
};
use eyre::{bail, Context, OptionExt, Result};
use parse::{ParsedTargetInfoFile, Tier, TriStateBool};
use parse::{Footnote, ParsedTargetInfoFile, Tier, TriStateBool};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
/// Information about a target obtained from `target_info.toml``.
@ -26,12 +26,14 @@ struct TargetMetadata {
notes: String,
std: TriStateBool,
host: TriStateBool,
footnotes: Vec<Footnote>,
}
const SECTIONS: &[&str] = &[
"Overview",
"Requirements",
"Testing",
"Building",
"Building the target",
"Cross compilation",
"Building Rust programs",
];
@ -59,12 +61,12 @@ fn main() -> Result<()> {
match std::fs::create_dir("targets/src") {
Ok(()) => {}
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {}
e @ _ => e.unwrap(),
e @ _ => e.wrap_err("failed creating src dir")?,
}
}
let mut info_patterns = parse::load_target_infos(Path::new(input_dir))
.unwrap()
.wrap_err("failed loading target_info")?
.into_iter()
.map(|info| {
let metadata_used = vec![false; info.metadata.len()];
@ -173,6 +175,7 @@ fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> Ta
notes: metadata_pattern.notes.clone(),
host: metadata_pattern.host.clone(),
std: metadata_pattern.std.clone(),
footnotes: metadata_pattern.footnotes.clone(),
});
}
}

View file

@ -40,6 +40,15 @@ pub struct ParsedTargetMetadata {
pub notes: String,
pub std: TriStateBool,
pub host: TriStateBool,
#[serde(default)]
pub footnotes: Vec<Footnote>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Footnote {
pub name: String,
pub content: String,
}
#[derive(Debug, PartialEq, Clone, Copy, Deserialize)]
@ -84,25 +93,49 @@ fn parse_file(name: &str, content: &str) -> Result<ParsedTargetInfoFile> {
.nth(1)
.ok_or_eyre("missing frontmatter")?;
let frontmatter =
let frontmatter_line_count = frontmatter.lines().count() + 2; // 2 from ---
let mut frontmatter =
serde_yaml::from_str::<Frontmatter>(frontmatter).wrap_err("invalid frontmatter")?;
frontmatter.metadata.iter_mut().for_each(|meta| {
meta.footnotes.iter_mut().for_each(|footnote| {
footnote.content = footnote.content.replace("\r\n", " ").replace("\n", " ")
})
});
let frontmatter = frontmatter;
let body = frontmatter_splitter.next().ok_or_eyre("no body")?;
let mut sections = Vec::new();
let mut sections = Vec::<(String, String)>::new();
let mut in_codeblock = false;
for line in body.lines() {
if line.starts_with("#") {
if let Some(header) = line.strip_prefix("## ") {
for (idx, line) in body.lines().enumerate() {
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("#") {
if in_codeblock {
match sections.last_mut() {
Some((_, content)) => {
content.push_str(line);
content.push('\n');
}
None if line.trim().is_empty() => {}
None => {
bail!("line {number} with content not allowed before the first heading")
}
}
} else if let Some(header) = line.strip_prefix("## ") {
if !crate::SECTIONS.contains(&header) {
bail!(
"`{header}` is not an allowed section name, must be one of {:?}",
"on line {number}, `{header}` is not an allowed section name, must be one of {:?}",
super::SECTIONS
);
}
sections.push((header.to_owned(), String::new()));
} else {
bail!("the only allowed headings are `## `");
bail!("on line {number}, the only allowed headings are `## `: `{line}`");
}
} else {
match sections.last_mut() {

View file

@ -3,7 +3,7 @@ use std::{fs, path::Path};
use crate::{
is_in_rust_lang_rust,
parse::{Tier, TriStateBool},
parse::{Footnote, Tier, TriStateBool},
RustcTargetInfo, TargetDocs,
};
@ -147,45 +147,67 @@ pub fn render_static(src_output: &Path, targets: &[(TargetDocs, RustcTargetInfo)
.wrap_err("writing front page information about experiment")?;
}
// 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 platform_support_main_new =
render_platform_support_tables(&platform_support_main_old, targets)?;
fs::write(platform_support_main, platform_support_main_new)
.wrap_err("writing platform-support.md")?;
// needs footnotes...
//let tier1host_table = render_table(
// targets
// .into_iter()
// .filter(|target| target.0.tier == Some(Tier::One)),
//)?;
// Tier 2 without host doesn't exist right now
// they all support std, obviously
//let tier2host_table = render_table_without_std this needs a better scheme??(
// targets
// .into_iter()
// .filter(|target| target.0.tier == Some(Tier::Two) && target.0.has_host_tools()),
//)
//.wrap_err("rendering tier 2 table")?;
Ok(())
}
impl Footnote {
fn reference(&self) -> String {
format!("[^{}]", self.name)
}
}
fn render_platform_support_tables(
content: &str,
targets: &[(TargetDocs, RustcTargetInfo)],
) -> Result<String> {
let tier1host_table = render_table_awesome(
targets,
TierTable {
filter: |target| target.tier == Some(Tier::One),
include_host: false,
include_std: false,
},
)?;
let tier2host_table = render_table(
targets
.into_iter()
.filter(|target| target.0.tier == Some(Tier::Two) && target.0.has_host_tools()),
false,
false,
)?;
let tier2_table = render_table(
targets
.into_iter()
.filter(|target| target.0.tier == Some(Tier::Two) && !target.0.has_host_tools()),
true,
false,
)?;
let tier3_table = render_table_with_host(
let tier3_table = render_table(
targets
.into_iter()
.filter(|target| target.0.tier == Some(Tier::Three)),
true,
true,
)?;
let content = platform_support_main_old;
let content = replace_section(&content, "TIER1HOST", &tier1host_table)
.wrap_err("replacing platform support.md")?;
let content = replace_section(&content, "TIER2HOST", &tier2host_table)
.wrap_err("replacing platform support.md")?;
let content = replace_section(&content, "TIER2", &tier2_table)
.wrap_err("replacing platform support.md")?;
let content = replace_section(&content, "TIER3", &tier3_table)
.wrap_err("replacing platform support.md")?;
fs::write(platform_support_main, content).wrap_err("writing platform-support.md")?;
Ok(())
Ok(content)
}
fn render_table_tri_state_bool(bool: TriStateBool) -> &'static str {
@ -196,43 +218,110 @@ fn render_table_tri_state_bool(bool: TriStateBool) -> &'static str {
}
}
fn render_table_with_host<'a>(
targets: impl IntoIterator<Item = &'a (TargetDocs, RustcTargetInfo)>,
struct TierTable {
filter: fn(&TargetDocs) -> bool,
include_std: bool,
include_host: bool,
}
fn render_table_awesome<'a>(
targets: &[(TargetDocs, RustcTargetInfo)],
table: TierTable,
) -> Result<String> {
let mut rows = Vec::new();
let mut all_footnotes = Vec::new();
let targets = targets
.into_iter()
.filter(|target| (table.filter)(&target.0));
for (target, _) in targets {
let meta = target.metadata.as_ref();
let mut notes = meta
.map(|meta| meta.notes.as_str())
.unwrap_or("unknown")
.to_owned();
if meta.map_or(false, |meta| !meta.footnotes.is_empty()) {
let footnotes = &meta.unwrap().footnotes;
all_footnotes.extend(footnotes);
let footnotes_str = footnotes
.iter()
.map(|footnote| footnote.reference())
.collect::<Vec<_>>()
.join(" ");
notes = format!("{notes} {footnotes_str}");
}
let std = if table.include_std {
let std = meta
.map(|meta| render_table_tri_state_bool(meta.std))
.unwrap_or("?");
format!(" | {std}")
} else {
String::new()
};
let host = if table.include_host {
let host = meta
.map(|meta| render_table_tri_state_bool(meta.host))
.unwrap_or("?");
format!(" | {host}")
} else {
String::new()
};
let notes = meta.map(|meta| meta.notes.as_str()).unwrap_or("unknown");
rows.push(format!(
"[`{0}`](platform-support/targets/{0}.md) | {std} | {host} | {notes}",
"[`{0}`](platform-support/targets/{0}.md){std}{host} | {notes}",
target.name
));
}
Ok(rows.join("\n"))
let mut result = rows.join("\n");
for footnote in all_footnotes {
result.push_str("\n\n");
result.push_str(&footnote.reference());
result.push_str(": ");
result.push_str(&footnote.content);
}
Ok(result)
}
fn render_table<'a>(
targets: impl IntoIterator<Item = &'a (TargetDocs, RustcTargetInfo)>,
include_std: bool,
include_host: bool,
) -> Result<String> {
let mut rows = Vec::new();
for (target, _) in targets {
let meta = target.metadata.as_ref();
let notes = meta.map(|meta| meta.notes.as_str()).unwrap_or("unknown");
let std = if include_std {
let std = meta
.map(|meta| render_table_tri_state_bool(meta.std))
.unwrap_or("?");
let notes = meta.map(|meta| meta.notes.as_str()).unwrap_or("unknown");
format!(" | {std}")
} else {
String::new()
};
let host = if include_host {
let host = meta
.map(|meta| render_table_tri_state_bool(meta.host))
.unwrap_or("?");
format!(" | {host}")
} else {
String::new()
};
rows.push(format!(
"[`{0}`](platform-support/targets/{0}.md) | {std} | {notes}",
"[`{0}`](platform-support/targets/{0}.md){std}{host} | {notes}",
target.name
));
}