diff --git a/src/main.rs b/src/main.rs index 9590299..d6ae724 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use std::{ }; use eyre::{bail, Context, OptionExt, Result}; -use parse::ParsedTargetInfoFile; +use parse::{ParsedTargetInfoFile, Tier, TriStateBool}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; /// Information about a target obtained from `target_info.toml``. @@ -16,7 +16,7 @@ struct TargetDocs { name: String, maintainers: Vec, sections: Vec<(String, String)>, - tier: String, + tier: Option, // TODO: Make this mandatory. metadata: Option, } @@ -24,8 +24,8 @@ struct TargetDocs { /// Metadata for the table struct TargetMetadata { notes: String, - std: String, - host: String, + std: TriStateBool, + host: TriStateBool, } const SECTIONS: &[&str] = &[ @@ -182,8 +182,7 @@ fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> Ta TargetDocs { name: target.to_owned(), maintainers, - // tier: tier.expect(&format!("no tier found for target {target}")), - tier: tier.unwrap_or("UNKNOWN".to_owned()), + tier, sections, metadata, } diff --git a/src/parse.rs b/src/parse.rs index 989c4dd..50e61eb 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,34 +1,53 @@ //! Suboptimal half-markdown parser that's just good-enough for this. use eyre::{bail, OptionExt, Result, WrapErr}; +use serde::Deserialize; use std::{fs::DirEntry, path::Path}; +#[derive(Debug, PartialEq, Clone, Deserialize)] +pub enum Tier { + #[serde(rename = "1")] + One, + #[serde(rename = "2")] + Two, + #[serde(rename = "3")] + Three, +} + #[derive(Debug)] pub struct ParsedTargetInfoFile { pub pattern: String, - pub tier: Option, + pub tier: Option, pub maintainers: Vec, pub sections: Vec<(String, String)>, pub metadata: Vec, } -#[derive(serde::Deserialize)] +#[derive(Deserialize)] #[serde(deny_unknown_fields)] struct Frontmatter { - tier: Option, + tier: Option, #[serde(default)] maintainers: Vec, #[serde(default)] metadata: Vec, } -#[derive(Debug, Clone, serde::Deserialize)] +#[derive(Debug, Clone, Deserialize)] #[serde(deny_unknown_fields)] pub struct ParsedTargetMetadata { pub pattern: String, pub notes: String, - pub std: String, - pub host: String, + pub std: TriStateBool, + pub host: TriStateBool, +} + +#[derive(Debug, PartialEq, Clone, Copy, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TriStateBool { + True, + False, + Unknown, } pub fn load_target_infos(directory: &Path) -> Result> { @@ -112,6 +131,8 @@ fn parse_file(name: &str, content: &str) -> Result { #[cfg(test)] mod tests { + use crate::parse::Tier; + #[test] fn no_frontmatter() { let name = "archlinux-unknown-linux-gnu.md"; // arch linux is an arch, right? @@ -171,7 +192,7 @@ But it should be possible. assert_eq!(info.maintainers, vec!["who maintains the cat?"]); assert_eq!(info.pattern, name); - assert_eq!(info.tier, Some("1".to_owned())); + assert_eq!(info.tier, Some(Tier::One)); assert_eq!( info.sections, vec![ diff --git a/src/render.rs b/src/render.rs index 6b58a37..c760a80 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,11 +1,32 @@ -use eyre::{bail, Context, OptionExt, Result}; +use eyre::{Context, OptionExt, Result}; use std::{fs, path::Path}; -use crate::{is_in_rust_lang_rust, RustcTargetInfo, TargetDocs}; +use crate::{ + is_in_rust_lang_rust, + parse::{Tier, TriStateBool}, + RustcTargetInfo, TargetDocs, +}; + +impl TargetDocs { + fn has_host_tools(&self) -> bool { + self.metadata + .as_ref() + .map_or(false, |meta| meta.host == TriStateBool::True) + } +} /// Renders a single target markdown file from the information obtained. pub fn render_target_md(target: &TargetDocs, rustc_info: &RustcTargetInfo) -> String { - let mut doc = format!("# {}\n\n**Tier: {}**\n\n", target.name, target.tier); + let mut doc = format!( + "# {}\n\n**Tier: {}**\n\n", + target.name, + match target.tier { + Some(Tier::One) => "1", + Some(Tier::Two) => "2", + Some(Tier::Three) => "3", + None => "UNKNOWN", + } + ); let mut section = |name: &str, content: &str| { doc.push_str("## "); @@ -123,7 +144,7 @@ pub fn render_static(src_output: &Path, targets: &[(TargetDocs, RustcTargetInfo) But as you might notice, all targets are actually present with a stub :3. ", ) - .unwrap(); + .wrap_err("writing front page information about experiment")?; } // TODO: Render the nice table showing off all targets and their tier. @@ -131,20 +152,50 @@ pub fn render_static(src_output: &Path, targets: &[(TargetDocs, RustcTargetInfo) 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")?; + // 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")?; + let tier2_table = render_table( + targets + .into_iter() + .filter(|target| target.0.tier == Some(Tier::Two) && !target.0.has_host_tools()), + )?; + let tier3_table = render_table_with_host( + targets + .into_iter() + .filter(|target| target.0.tier == Some(Tier::Three)), + )?; - let platform_support_main_new = - replace_section(&platform_support_main_old, "TIER3", &tier3_table) - .wrap_err("replacing platform support.md")?; + let content = platform_support_main_old; + 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, platform_support_main_new) - .wrap_err("writing platform-support.md")?; + fs::write(platform_support_main, content).wrap_err("writing platform-support.md")?; Ok(()) } +fn render_table_tri_state_bool(bool: TriStateBool) -> &'static str { + match bool { + TriStateBool::True => "✓", + TriStateBool::False => " ", + TriStateBool::Unknown => "?", + } +} + fn render_table_with_host<'a>( targets: impl IntoIterator, ) -> Result { @@ -152,20 +203,13 @@ fn render_table_with_host<'a>( 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 std = meta + .map(|meta| render_table_tri_state_bool(meta.std)) + .unwrap_or("?"); + let host = meta + .map(|meta| render_table_tri_state_bool(meta.host)) + .unwrap_or("?"); + let notes = meta.map(|meta| meta.notes.as_str()).unwrap_or("unknown"); rows.push(format!( "[`{0}`](platform-support/targets/{0}.md) | {std} | {host} | {notes}", @@ -175,3 +219,23 @@ fn render_table_with_host<'a>( Ok(rows.join("\n")) } + +fn render_table<'a>( + targets: impl IntoIterator, +) -> Result { + let mut rows = Vec::new(); + + for (target, _) in targets { + let meta = target.metadata.as_ref(); + 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"); + rows.push(format!( + "[`{0}`](platform-support/targets/{0}.md) | {std} | {notes}", + target.name + )); + } + + Ok(rows.join("\n")) +}