mirror of
https://github.com/Noratrieb/target-tier-docs-experiment.git
synced 2026-01-14 16:35:09 +01:00
target tables
This commit is contained in:
parent
09ee1f7a33
commit
8590f9b343
3 changed files with 172 additions and 47 deletions
11
src/main.rs
11
src/main.rs
|
|
@ -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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
47
src/parse.rs
47
src/parse.rs
|
|
@ -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() {
|
||||
|
|
|
|||
145
src/render.rs
145
src/render.rs
|
|
@ -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
|
||||
));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue