use std::env;
use std::fs::File;
use std::io::Write;
const CR: u8 = b'\r';
const LF: u8 = b'\n';
const SP: u8 = b' ';
const HT: u8 = b'\t';
const DEL: u8 = 127;
const NIL: u8 = 0;
const TCHAR: &[u8] = b"-_.!#$%&'*+^`|~";
fn format_char(b: u8) -> String {
use std::fmt::Write;
let mut str = String::new();
match b {
b'\r' => str.push_str("\\r"),
b'\n' => str.push_str("\\n"),
b'\t' => str.push_str("\\t"),
_ if is_rfc_vchar(b) => {
str.push(b as char);
}
_ => {
write!(str, "\\{:#04x}", b).expect("Writing to strings is infallible");
}
};
str
}
fn generate_table(name: &str, predicate: fn(u8) -> bool) -> String {
use std::fmt::Write;
let mut t = String::new();
write!(t, "pub static {}: [bool; 256] = [\r\n ", name).ok();
for i in 0..=255_u8 {
write!(t, " {} /* {} */,", (predicate)(i), format_char(i)).ok();
if i % 4 == 3 {
write!(t, "\r\n ").ok();
}
}
write!(t, "];\r\n").ok();
t
}
fn is_valid_uri_char(b: u8) -> bool {
b > SP && b != DEL
}
fn is_rfc_tchar(b: u8) -> bool {
b.is_ascii_alphanumeric() || TCHAR.contains(&b)
}
fn is_rfc_vchar(b: u8) -> bool {
b >= SP && b < DEL
}
fn is_rfc_obs_text(b: u8) -> bool {
b >= 0x80
}
fn is_rfc_whitespace(b: u8) -> bool {
b == SP || b == HT
}
fn is_bad_http_character(b: u8) -> bool {
b == CR || b == LF || b == NIL
}
type TableGenerator = fn(u8) -> bool;
fn generate_lookup_tables() {
use std::fmt::Write;
let tables: &[(&str, TableGenerator)] = &[
("TCHAR_TABLE", |b| is_rfc_tchar(b)),
("VCHAR", |b| is_rfc_vchar(b)),
("BAD_CHARACTERS", |b| is_bad_http_character(b)),
("RFC_WHITE_SPACE", |b| is_rfc_whitespace(b)),
("VALID_URI_CHARS", |b| is_valid_uri_char(b)),
("HEADER_VALUE_CHARS", |b| {
is_rfc_vchar(b) || is_rfc_obs_text(b) || is_rfc_whitespace(b)
}),
];
let mut char_tables = String::new();
for table in tables {
write!(char_tables, "{}", generate_table(table.0, table.1))
.expect("Writing to strings is infallible");
}
let out_dir = env::var("OUT_DIR").expect("OUT_DIR variable is not set");
let mut file =
File::create(format!("{}/char_tables.rs", out_dir)).expect("Cannot open file for writing");
file.write_all(char_tables.as_bytes())
.expect("Cannot write to file");
}
fn main() {
generate_lookup_tables();
}