use std::env;
use std::fs;
use std::path::PathBuf;
const NONSTANDARD_SCHEMES: &[&str] = &["doi", "gemini", "isbn", "javascript", "pmid"];
fn main() {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let csv_path = manifest_dir.join("src/parser/inlines/uri-schemes.csv");
let csv = fs::read_to_string(&csv_path).expect("read uri-schemes.csv");
let mut schemes = parse_iana_schemes(&csv);
schemes.extend(NONSTANDARD_SCHEMES.iter().map(|s| s.to_string()));
schemes.sort();
schemes.dedup();
let mut out = String::from("// @generated by build.rs from uri-schemes.csv — do not edit.\n");
out.push_str("const BARE_URI_SCHEMES: &[&str] = &[\n");
for scheme in &schemes {
out.push_str(" \"");
out.push_str(scheme);
out.push_str("\",\n");
}
out.push_str("];\n");
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
fs::write(out_dir.join("uri_schemes.rs"), out).expect("write generated scheme table");
println!("cargo:rerun-if-changed=src/parser/inlines/uri-schemes.csv");
println!("cargo:rerun-if-changed=build.rs");
}
fn parse_iana_schemes(csv: &str) -> Vec<String> {
let mut schemes = Vec::new();
let mut chars = csv.chars().peekable();
let mut header = true;
while chars.peek().is_some() {
let mut field = String::new();
let mut in_quotes = false;
while let Some(&c) = chars.peek() {
if in_quotes {
chars.next();
if c == '"' {
if chars.peek() == Some(&'"') {
chars.next();
field.push('"');
} else {
in_quotes = false;
}
} else {
field.push(c);
}
} else if c == '"' {
chars.next();
in_quotes = true;
} else if c == ',' || c == '\n' || c == '\r' {
break;
} else {
chars.next();
field.push(c);
}
}
let mut in_quotes = false;
while let Some(&c) = chars.peek() {
chars.next();
match c {
'"' if in_quotes && chars.peek() == Some(&'"') => {
chars.next();
}
'"' => in_quotes = !in_quotes,
'\n' if !in_quotes => break,
_ => {}
}
}
if header {
header = false;
continue;
}
if let Some(name) = field.split_whitespace().next() {
schemes.push(name.to_ascii_lowercase());
}
}
schemes
}