use std::{
env::{self, var},
fs::OpenOptions,
io::Write,
path::PathBuf,
};
fn main() {
println!("cargo:rerun-if-changed=build.rs");
let tarpaulin = var("CARGO_FEATURE_TARPAULIN").is_ok();
if tarpaulin {
use_feature("tarpaulin");
} else {
println!("cargo:rerun-if-env-changed=CARGO_TARPAULIN");
println!("cargo:rerun-if-env-changed=CARGO_CFG_TARPAULIN");
if env::var("CARGO_TARPAULIN").is_ok() || env::var("CARGO_CFG_TARPAULIN").is_ok() {
use_feature("tarpaulin");
}
}
generate_from_str_60("minute");
generate_to_str_60("minute");
generate_from_str_60("second");
generate_to_str_60("second");
generate_hour_from_str();
generate_hour_to_str();
generate_millis_from_str();
generate_millis_to_str();
generate_html5_entities();
println!("cargo:rerun-if-env-changed=CARGO_FEATURE_TARPAULIN");
}
fn use_feature(feature: &str) {
println!("cargo:rustc-cfg={}", feature);
}
fn generate_from_str_60(name: &str) {
let mut out = String::new();
for i in 0..10 {
out.push_str(&format!(
" \"{}\" => return ::core::result::Result::Err(Self::Err::NotPadded),\n",
i
));
}
for i in 0..60 {
out.push_str(&format!(" \"{:02}\" => {},\n", i, i));
}
let output = format!(
r###"
// @generated by build.rs - do not edit by hand
macro_rules! {name}_from_str {{
($value:expr) => {{{{
::core::result::Result::Ok(Self(match $value {{
{} _ => {{
let val = <::core::primitive::u8 as ::core::str::FromStr>::from_str($value)?;
return ::core::result::Result::Err(Self::Err::Overflow(val));
}},
}}))
}}}}
}}
pub(crate) use {name}_from_str;
"###,
out
);
let path = PathBuf::from("generated").join(format!("{}_from_str.rs", name));
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)
.expect("failed to open generated file");
file
.write_all(output.as_bytes())
.expect("failed to write generated file");
}
fn generate_to_str_60(name: &str) {
let mut out = String::new();
for i in 0..60 {
out.push_str(&format!(" {} => \"{:02}\",\n", i, i));
}
let output = format!(
r###"
// @generated by build.rs - do not edit by hand
macro_rules! {name}_to_str {{
($value:expr) => {{{{
match $value {{
{} _ => ::core::panic!("{} value must be between 00-59"),
}}
}}}}
}}
pub(crate) use {name}_to_str;
"###,
out, name
);
let path = PathBuf::from("generated").join(format!("{}_to_str.rs", name));
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)
.expect("failed to open generated file");
file
.write_all(output.as_bytes())
.expect("failed to write generated file");
}
fn generate_millis_from_str() {
let mut out = String::new();
for i in 0..10 {
out.push_str(&format!(
" \"{}\" => return ::core::result::Result::Err(Self::Err::NotPadded),\n",
i
));
}
for i in 0..100 {
out.push_str(&format!(
" \"{:02}\" => return ::core::result::Result::Err(Self::Err::NotPadded),\n",
i
));
}
for i in 0..1000 {
out.push_str(&format!(" \"{:03}\" => {},\n", i, i));
}
let output = format!(
r###"
// @generated by build.rs - do not edit by hand
macro_rules! millisecond_from_str {{
($value:expr) => {{{{
::core::result::Result::Ok(Self(match $value {{
{} _ => {{
let val = <::core::primitive::u16 as ::core::str::FromStr>::from_str($value)?;
return ::core::result::Result::Err(Self::Err::Overflow(val));
}},
}}))
}}}}
}}
pub(crate) use millisecond_from_str;
"###,
out
);
let path = PathBuf::from("generated").join("millisecond_from_str.rs");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)
.expect("failed to open generated file");
file
.write_all(output.as_bytes())
.expect("failed to write generated file");
}
fn generate_millis_to_str() {
let mut out = String::new();
for i in 0..1000 {
out.push_str(&format!(" {} => \"{:03}\",\n", i, i));
}
let output = format!(
r###"
// @generated by build.rs - do not edit by hand
macro_rules! millisecond_to_str {{
($value:expr) => {{{{
match $value {{
{} _ => ::core::panic!("Millisecond value must be between 000-999"),
}}
}}}}
}}
pub(crate) use millisecond_to_str;
"###,
out,
);
let path = PathBuf::from("generated").join("millisecond_to_str.rs");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)
.expect("failed to open generated file");
file
.write_all(output.as_bytes())
.expect("failed to write generated file");
}
fn generate_hour_from_str() {
let mut out = String::new();
for i in 0..10 {
out.push_str(&format!(
" \"{}\" => return ::core::result::Result::Err(Self::Err::NotPadded),\n",
i
));
}
for i in 0..100 {
out.push_str(&format!(" \"{i:02}\" => {i},\n"));
}
for i in 100..1000 {
out.push_str(&format!(" \"{i}\" => {i},\n"));
}
let output = format!(
r###"
// @generated by build.rs - do not edit by hand
macro_rules! hour_from_str {{
($value:expr) => {{{{
::core::result::Result::Ok(Self(match $value {{
{} _ => {{
let val = <::core::primitive::u16 as ::core::str::FromStr>::from_str($value)?;
return ::core::result::Result::Err(Self::Err::Overflow(val));
}},
}}))
}}}}
}}
pub(crate) use hour_from_str;
"###,
out
);
let path = PathBuf::from("generated").join("hour_from_str.rs");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)
.expect("failed to open generated file");
file
.write_all(output.as_bytes())
.expect("failed to write generated file");
}
fn generate_hour_to_str() {
let mut out = String::new();
for i in 0..1000 {
out.push_str(&format!(" {} => \"{:02}\",\n", i, i));
}
let output = format!(
r###"
// @generated by build.rs - do not edit by hand
macro_rules! hour_to_str {{
($value:expr) => {{{{
match $value {{
{} _ => ::core::panic!("Hour value must be between 00-999"),
}}
}}}}
}}
pub(crate) use hour_to_str;
"###,
out,
);
let path = PathBuf::from("generated").join("hour_to_str.rs");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)
.expect("failed to open generated file");
file
.write_all(output.as_bytes())
.expect("failed to write generated file");
}
fn generate_html5_entities() {
println!("cargo:rerun-if-changed=assets/html5_entities.json");
let json = std::fs::read_to_string("assets/html5_entities.json")
.expect("failed to read html5_entities.json");
let entries: Vec<(String, Vec<u32>)> =
serde_json::from_str(&json).expect("failed to parse html5_entities.json");
let dest = PathBuf::from("generated").join("html5_entities.rs");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(dest)
.expect("failed to create html5_entities.rs");
writeln!(
file,
"// @generated by build.rs from assets/html5_entities.json — do not edit"
)
.unwrap();
writeln!(file).unwrap();
let values: Vec<(String, String)> = entries
.iter()
.map(|(name, cps)| {
let escaped: String = cps
.iter()
.filter_map(|&cp| char::from_u32(cp))
.map(|c| format!("\\u{{{:04X}}}", c as u32))
.collect();
(name.clone(), format!("\"{}\"", escaped))
})
.collect();
let mut builder = phf_codegen::Map::new();
for (name, value) in &values {
builder.entry(name.as_str(), value.as_str());
}
writeln!(
file,
"pub(crate) static HTML5_ENTITIES: phf::Map<&'static str, &'static str> = {};",
builder.build()
)
.unwrap();
}