use std::env;
use std::fs;
use std::path::Path;
fn main() {
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let out_dir = env::var("OUT_DIR").unwrap();
println!("cargo::rerun-if-changed=toolkit");
println!("cargo::rerun-if-changed=skills");
println!("cargo::rerun-if-changed=ui");
println!("cargo::rerun-if-changed=README_TELEMETRY.md");
generate_toolkit(&manifest_dir, &out_dir);
generate_setup_from_readme(&manifest_dir, &out_dir);
generate_skills(&manifest_dir, &out_dir);
}
fn resolve_path(path: &Path) -> String {
fs::canonicalize(path)
.unwrap_or_else(|_| path.to_path_buf())
.to_string_lossy()
.to_string()
}
fn generate_toolkit(manifest_dir: &str, out_dir: &str) {
let toolkit_dir = Path::new(manifest_dir).join("toolkit");
let dest = Path::new(out_dir).join("toolkit_files.rs");
let mut entries: Vec<(String, String)> = Vec::new();
if toolkit_dir.is_dir() {
for entry in fs::read_dir(&toolkit_dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.is_file() || path.is_symlink() {
let name = entry.file_name().to_string_lossy().to_string();
entries.push((name, resolve_path(&path)));
}
}
}
entries.sort_by(|a, b| a.0.cmp(&b.0));
let mut code = String::from("pub const TOOLKIT_FILES: &[(&str, &str)] = &[\n");
for (name, path) in &entries {
code.push_str(&format!(" ({:?}, include_str!({:?})),\n", name, path));
}
code.push_str("];\n");
fs::write(dest, code).unwrap();
}
fn generate_skills(manifest_dir: &str, out_dir: &str) {
let skills_dir = Path::new(manifest_dir).join("skills");
let dest = Path::new(out_dir).join("skill_files.rs");
let mut code = String::new();
let mut segments: Vec<(String, String, String)> = Vec::new();
if skills_dir.is_dir() {
for entry in fs::read_dir(&skills_dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
let name = entry.file_name().to_string_lossy().to_string();
if !name.ends_with(".md") {
continue;
}
let canonical = resolve_path(&path);
if name == "header.md" {
code.push_str(&format!(
"pub const HEADER: &str = include_str!({:?});\n",
canonical
));
continue;
}
let stem = name.trim_end_matches(".md").to_string();
let title = extract_title(&path).unwrap_or_else(|| stem.clone());
segments.push((stem, title, canonical));
}
}
if !code.contains("HEADER") {
code.push_str("pub const HEADER: &str = \"\";\n");
}
segments.sort_by(|a, b| a.0.cmp(&b.0));
let setup_path = Path::new(out_dir).join("setup_skill.md");
segments.push((
"setup".to_string(),
"Instrumenting your app with dial9".to_string(),
resolve_path(&setup_path),
));
segments.sort_by(|a, b| a.0.cmp(&b.0));
code.push_str("pub const SKILL_FILES: &[(&str, &str, &str)] = &[\n");
for (name, title, path) in &segments {
code.push_str(&format!(
" ({:?}, {:?}, include_str!({:?})),\n",
name, title, path
));
}
code.push_str("];\n");
fs::write(dest, code).unwrap();
}
fn extract_title(path: &Path) -> Option<String> {
let content = fs::read_to_string(path).ok()?;
content
.lines()
.find(|l| l.starts_with("# "))
.map(|l| l.trim_start_matches("# ").to_string())
}
const SETUP_SECTIONS: &[&str] = &[
"Prerequisites",
"Quick start",
"The root future is not instrumented",
"Tracing span events (opt-in)",
"Wake event tracking",
];
fn generate_setup_from_readme(manifest_dir: &str, out_dir: &str) {
let readme_path = Path::new(manifest_dir).join("README_TELEMETRY.md");
let readme = fs::read_to_string(&readme_path)
.unwrap_or_else(|e| panic!("failed to read {}: {e}", readme_path.display()));
let mut output = String::from("# Instrumenting your app with dial9\n\n");
output.push_str("*This content is extracted from the [dial9-tokio-telemetry README](https://github.com/dial9-rs/dial9-tokio-telemetry).*\n\n");
for &heading in SETUP_SECTIONS {
let section = extract_section(&readme, heading)
.unwrap_or_else(|| panic!("README section '## {heading}' not found; was it renamed?"));
output.push_str(§ion);
output.push('\n');
}
let dest = Path::new(out_dir).join("setup_skill.md");
fs::write(&dest, &output).unwrap();
}
fn extract_section(markdown: &str, heading: &str) -> Option<String> {
let target = format!("## {heading}");
let lines: Vec<&str> = markdown.lines().collect();
let start = lines.iter().position(|l| l.trim() == target)?;
let end = lines[start + 1..]
.iter()
.position(|l| l.starts_with("## "))
.map(|i| start + 1 + i)
.unwrap_or(lines.len());
Some(lines[start..end].join("\n"))
}