fabro/crates/arc-util/build.rs
Bryan Helmkamp 32e4f5eb96 Rename all crates from unprefixed to arc-* prefix
Rename crate directories, package names, binary names, path
dependencies, use statements, qualified paths, clap command names,
and string literals across the workspace.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-28 04:20:47 -05:00

169 lines
5.1 KiB
Rust

use serde::Deserialize;
use std::env;
use std::fs;
use std::path::Path;
#[derive(Deserialize)]
struct Config {
allowlist: Option<GlobalAllowlist>,
rules: Vec<Rule>,
}
#[derive(Deserialize)]
struct GlobalAllowlist {
regexes: Option<Vec<String>>,
stopwords: Option<Vec<String>>,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct Rule {
id: String,
regex: String,
#[serde(default)]
keywords: Vec<String>,
entropy: Option<f64>,
#[serde(default)]
allowlist: Option<RuleAllowlist>,
#[allow(dead_code)]
description: Option<String>,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct RuleAllowlist {
regexes: Option<Vec<String>>,
stopwords: Option<Vec<String>>,
regex_target: Option<String>,
}
fn escape_rust_string(s: &str) -> String {
let mut out = String::with_capacity(s.len() + 10);
for ch in s.chars() {
match ch {
'\\' => out.push_str("\\\\"),
'"' => out.push_str("\\\""),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
c => out.push(c),
}
}
out
}
fn main() {
println!("cargo:rerun-if-changed=data/gitleaks.toml");
let toml_path = Path::new("data/gitleaks.toml");
let toml_content = fs::read_to_string(toml_path).expect("failed to read gitleaks.toml");
let config: Config = toml::from_str(&toml_content).expect("failed to parse gitleaks.toml");
let out_dir = env::var("OUT_DIR").unwrap();
let out_path = Path::new(&out_dir).join("rules_generated.rs");
let mut code = String::new();
// Global allowlist regexes
code.push_str("pub const GLOBAL_ALLOWLIST_REGEXES: &[&str] = &[\n");
if let Some(ref al) = config.allowlist {
if let Some(ref regexes) = al.regexes {
for r in regexes {
code.push_str(&format!(" \"{}\",\n", escape_rust_string(r)));
}
}
}
code.push_str("];\n\n");
// Global allowlist stopwords
code.push_str("pub const GLOBAL_ALLOWLIST_STOPWORDS: &[&str] = &[\n");
if let Some(ref al) = config.allowlist {
if let Some(ref stopwords) = al.stopwords {
for sw in stopwords {
code.push_str(&format!(" \"{}\",\n", escape_rust_string(sw)));
}
}
}
code.push_str("];\n\n");
// Rule definitions
code.push_str("#[allow(dead_code)]\n");
code.push_str("pub struct RuleDef {\n");
code.push_str(" pub id: &'static str,\n");
code.push_str(" pub regex_pattern: &'static str,\n");
code.push_str(" pub keywords: &'static [&'static str],\n");
code.push_str(" pub entropy: Option<f64>,\n");
code.push_str(" pub allowlist_regexes: &'static [&'static str],\n");
code.push_str(" pub allowlist_stopwords: &'static [&'static str],\n");
code.push_str(" pub allowlist_regex_target: Option<&'static str>,\n");
code.push_str("}\n\n");
code.push_str("pub const RULES: &[RuleDef] = &[\n");
for rule in &config.rules {
code.push_str(" RuleDef {\n");
code.push_str(&format!(
" id: \"{}\",\n",
escape_rust_string(&rule.id)
));
code.push_str(&format!(
" regex_pattern: \"{}\",\n",
escape_rust_string(&rule.regex)
));
// Keywords
code.push_str(" keywords: &[");
for kw in &rule.keywords {
code.push_str(&format!("\"{}\", ", escape_rust_string(kw)));
}
code.push_str("],\n");
// Entropy
match rule.entropy {
Some(e) => code.push_str(&format!(" entropy: Some({:.1}),\n", e)),
None => code.push_str(" entropy: None,\n"),
}
// Allowlist regexes
code.push_str(" allowlist_regexes: &[");
if let Some(ref al) = rule.allowlist {
if let Some(ref regexes) = al.regexes {
for r in regexes {
code.push_str(&format!("\"{}\", ", escape_rust_string(r)));
}
}
}
code.push_str("],\n");
// Allowlist stopwords
code.push_str(" allowlist_stopwords: &[");
if let Some(ref al) = rule.allowlist {
if let Some(ref stopwords) = al.stopwords {
for sw in stopwords {
code.push_str(&format!("\"{}\", ", escape_rust_string(sw)));
}
}
}
code.push_str("],\n");
// Allowlist regex target
if let Some(ref al) = rule.allowlist {
if let Some(ref target) = al.regex_target {
code.push_str(&format!(
" allowlist_regex_target: Some(\"{}\"),\n",
escape_rust_string(target)
));
} else {
code.push_str(" allowlist_regex_target: None,\n");
}
} else {
code.push_str(" allowlist_regex_target: None,\n");
}
code.push_str(" },\n");
}
code.push_str("];\n");
fs::write(&out_path, code).expect("failed to write generated rules");
}