-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbuild.rs
More file actions
113 lines (95 loc) · 4.04 KB
/
build.rs
File metadata and controls
113 lines (95 loc) · 4.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use std::collections::BTreeMap;
use std::env;
use std::fs;
use std::path::Path;
use std::process::Command;
fn main() {
generate_version();
embed_themes();
}
/// Inject build-time version info as environment variables.
fn generate_version() {
let version = env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "0.0.0".to_string());
let commit = Command::new("git")
.args(["rev-parse", "--short", "HEAD"])
.output()
.ok()
.and_then(|o| {
if o.status.success() {
String::from_utf8(o.stdout)
.ok()
.map(|s| s.trim().to_string())
} else {
None
}
})
.unwrap_or_else(|| "unknown".to_string());
let build_time = chrono::Utc::now().to_rfc3339();
let channel = env::var("FORESTAGE_CHANNEL").unwrap_or_else(|_| "alpha".to_string());
let tag = env::var("FORESTAGE_TAG").unwrap_or_else(|_| format!("{version}-{commit}"));
let long_version = format!("{tag} ({commit}, {channel})");
println!("cargo:rustc-env=FORESTAGE_VERSION={version}");
println!("cargo:rustc-env=FORESTAGE_COMMIT={commit}");
println!("cargo:rustc-env=FORESTAGE_BUILD_TIME={build_time}");
println!("cargo:rustc-env=FORESTAGE_CHANNEL={channel}");
println!("cargo:rustc-env=FORESTAGE_TAG={tag}");
println!("cargo:rustc-env=FORESTAGE_LONG_VERSION={long_version}");
println!("cargo:rerun-if-changed=.git/HEAD");
println!("cargo:rerun-if-env-changed=FORESTAGE_CHANNEL");
println!("cargo:rerun-if-env-changed=FORESTAGE_TAG");
}
/// Embed all persona theme YAMLs at compile time using raw string literals.
///
/// Generates a Rust source file containing a function that returns a HashMap
/// of theme slug to YAML content. Uses `r#"..."#` raw strings to avoid
/// escaping issues with newlines, quotes, and backslashes in YAML content.
fn embed_themes() {
let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set");
let themes_dir = Path::new("personas/themes");
let mut themes: BTreeMap<String, String> = BTreeMap::new();
if themes_dir.is_dir() {
let mut entries: Vec<_> = fs::read_dir(themes_dir)
.expect("failed to read personas/themes/")
.filter_map(Result::ok)
.filter(|e| {
e.path()
.extension()
.is_some_and(|ext| ext == "yaml" || ext == "yml")
})
.collect();
entries.sort_by_key(fs::DirEntry::file_name);
for entry in entries {
let path = entry.path();
let slug = path
.file_stem()
.expect("no stem")
.to_string_lossy()
.to_string();
let content = fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display()));
themes.insert(slug, content);
// Rebuild when individual theme files change
println!("cargo:rerun-if-changed={}", path.display());
}
}
let mut code = String::from(
"/// Auto-generated by build.rs — do not edit.\n\
pub fn embedded_themes() -> std::collections::HashMap<&'static str, &'static str> {\n\
\x20 let mut m = std::collections::HashMap::new();\n",
);
for (slug, content) in &themes {
// Use raw string literals to avoid all escaping issues.
// r#"..."# handles newlines, quotes, backslashes natively.
// Only fails if content contains the sequence "# — theme YAML never does.
assert!(
!content.contains("\"#"),
"theme {slug} contains raw string delimiter sequence"
);
code.push_str(&format!(" m.insert(\"{slug}\", r#\"{content}\"#);\n"));
}
code.push_str(" m\n}\n");
let dest = Path::new(&out_dir).join("themes_embedded.rs");
fs::write(&dest, code).expect("failed to write themes_embedded.rs");
// Also rebuild if themes directory itself changes (files added/removed)
println!("cargo:rerun-if-changed=personas/themes");
}