intermodal/bin/generate-bep-table.rs
Casey Rodarmor 66d44155f0
Add BEP support table to readme
Also adds `bin/bin/generate-bep-table.rs`, which generates and updates
the table from a collection of BEPs.

type: documentation
2020-04-07 19:00:48 -07:00

191 lines
3.7 KiB
Rust

use std::{
error::Error,
fmt::{self, Display, Formatter},
fs,
str::FromStr,
};
use glob::glob;
use regex::Regex;
const README: &str = "README.md";
struct Bep {
number: usize,
title: String,
status: Status,
}
enum Status {
Unknown,
NotApplicable,
Supported,
NotSupported,
}
impl FromStr for Status {
type Err = String;
fn from_str(text: &str) -> Result<Self, Self::Err> {
match text {
"x" => Ok(Self::NotSupported),
"+" => Ok(Self::Supported),
"-" => Ok(Self::NotApplicable),
"?" => Ok(Self::Unknown),
":x:" => Ok(Self::NotSupported),
":white_check_mark:" => Ok(Self::Supported),
":heavy_minus_sign:" => Ok(Self::NotApplicable),
":grey_question:" => Ok(Self::Unknown),
_ => Err(format!("invalid status: {}", text)),
}
}
}
impl Display for Status {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Unknown => write!(f, ":grey_question:"),
Self::NotApplicable => write!(f, ":heavy_minus_sign:"),
Self::Supported => write!(f, ":white_check_mark:"),
Self::NotSupported => write!(f, ":x:"),
}
}
}
fn main() -> Result<(), Box<dyn Error>> {
let title_re = Regex::new("(?m)^:Title: (?P<title>.*)$")?;
let mut beps = Vec::new();
for result in glob("tmp/bittorrent.org/beps/bep_*.rst")? {
let path = result?;
let number = path
.file_stem()
.unwrap()
.to_string_lossy()
.split('_')
.nth(1)
.unwrap()
.parse::<usize>()?;
if number == 1000 {
continue;
}
let rst = fs::read_to_string(path)?;
let title = title_re
.captures(&rst)
.unwrap()
.name("title")
.unwrap()
.as_str()
.trim()
.to_owned();
beps.push(Bep {
status: Status::Unknown,
number,
title,
});
}
beps.sort_by_key(|bep| bep.number);
let table_re = Regex::new(
r"(?mx)
^[|]\ BEP.*
(
\n
[|]
.*
)*
",
)?;
let readme = fs::read_to_string(README)?;
let parts = table_re.split(&readme).into_iter().collect::<Vec<&str>>();
assert_eq!(parts.len(), 2);
let before = parts[0];
let after = parts[1];
let original = table_re
.captures(&readme)
.unwrap()
.get(0)
.unwrap()
.as_str()
.trim();
let row_re = Regex::new(
r"(?x)
^
\|
\s*
\[
(?P<number>[0-9]+)
\]
.*
\s*
\|
(?P<status>.*)
\|
(?P<title>.*)
\|
$
",
)?;
let mut originals = Vec::new();
for row in original.lines().skip(2) {
let captures = row_re.captures(row).unwrap();
originals.push(Bep {
number: captures.name("number").unwrap().as_str().parse()?,
status: captures.name("status").unwrap().as_str().trim().parse()?,
title: captures.name("title").unwrap().as_str().to_owned(),
});
}
assert_eq!(originals.len(), beps.len());
let mut lines = Vec::new();
let width = beps.iter().map(|bep| bep.title.len()).max().unwrap_or(0);
lines.push(format!(
"| BEP | Status | {:width$} |",
"Title",
width = width
));
lines.push(format!(
"|:----------------------------------------------:|:------------------:|:{:-<width$}-|",
"",
width = width
));
for (bep, original) in beps.into_iter().zip(originals) {
assert_eq!(bep.number, original.number);
lines.push(format!(
"| [{:02}](http://bittorrent.org/beps/bep_{:04}.html) | {:18} | {:width$} |",
bep.number,
bep.number,
original.status.to_string(),
bep.title,
width = width
));
}
let table = lines.join("\n");
let readme = &[before.trim(), "", &table, "", after.trim(), ""].join("\n");
fs::write(README, readme)?;
Ok(())
}