fix: restaurer build.rs (renommé par erreur en .bak)
This commit is contained in:
@@ -0,0 +1,308 @@
|
||||
//! Build script — génère les pages HTML à partir du Markdown
|
||||
//!
|
||||
//! Exécuté à chaque `cargo build`. Garantit que :
|
||||
//! 1. changelog.html est généré depuis CHANGELOG.md
|
||||
//! 2. help.html est généré depuis ROADMAP.md
|
||||
//! 3. Le badge version dans index.html suit la version Cargo.toml
|
||||
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
let frontend_dir = manifest_dir.join("src/frontend");
|
||||
|
||||
// === changelog.html depuis CHANGELOG.md ===
|
||||
let changelog_tmpl = frontend_dir.join("changelog.tmpl.html");
|
||||
let changelog_md = manifest_dir.join("CHANGELOG.md");
|
||||
let changelog_out = frontend_dir.join("changelog.html");
|
||||
|
||||
if changelog_tmpl.exists() && changelog_md.exists() {
|
||||
let md = fs::read_to_string(&changelog_md).unwrap_or_default();
|
||||
let html = simple_md_to_html(&md);
|
||||
let tmpl = fs::read_to_string(&changelog_tmpl).unwrap_or_default();
|
||||
let page = tmpl
|
||||
.replace("{{VERSION}}", version)
|
||||
.replace("{{CHANGELOG_CONTENT}}", &html);
|
||||
fs::write(&changelog_out, &page).ok();
|
||||
println!("cargo:rerun-if-changed=CHANGELOG.md");
|
||||
println!("cargo:rerun-if-changed=src/frontend/changelog.tmpl.html");
|
||||
}
|
||||
|
||||
// === help.html depuis ROADMAP.md ===
|
||||
let help_tmpl = frontend_dir.join("help.tmpl.html");
|
||||
let roadmap_md = manifest_dir.join("ROADMAP.md");
|
||||
let help_out = frontend_dir.join("help.html");
|
||||
|
||||
if help_tmpl.exists() && roadmap_md.exists() {
|
||||
let md = fs::read_to_string(&roadmap_md).unwrap_or_default();
|
||||
let html = simple_md_to_html(&md);
|
||||
let tmpl = fs::read_to_string(&help_tmpl).unwrap_or_default();
|
||||
let page = tmpl
|
||||
.replace("{{VERSION}}", version)
|
||||
.replace("{{HELP_CONTENT}}", &html);
|
||||
fs::write(&help_out, &page).ok();
|
||||
println!("cargo:rerun-if-changed=ROADMAP.md");
|
||||
println!("cargo:rerun-if-changed=src/frontend/help.tmpl.html");
|
||||
}
|
||||
|
||||
// === Injection version dans index.html ===
|
||||
let index_path = frontend_dir.join("index.html");
|
||||
if index_path.exists() {
|
||||
let mut content = fs::read_to_string(&index_path).unwrap_or_default();
|
||||
// Remplacer tous les badges de version vX.Y.Z par la version courante
|
||||
content = replace_all_versions(&content, version);
|
||||
fs::write(&index_path, &content).ok();
|
||||
println!("cargo:rerun-if-changed=src/frontend/index.html");
|
||||
}
|
||||
|
||||
println!("cargo:info=✅ Pages HTML générées pour imagod-web v{version}");
|
||||
}
|
||||
|
||||
/// Mini convertisseur Markdown → HTML (zéro dépendance)
|
||||
fn simple_md_to_html(md: &str) -> String {
|
||||
let mut html = String::new();
|
||||
let mut in_code_block = false;
|
||||
let mut code_content = String::new();
|
||||
|
||||
for line in md.lines() {
|
||||
if line.starts_with("```") {
|
||||
if in_code_block {
|
||||
html.push_str("<pre><code>");
|
||||
html.push_str(&html_escape(&code_content));
|
||||
html.push_str("</code></pre>\n");
|
||||
code_content.clear();
|
||||
in_code_block = false;
|
||||
} else {
|
||||
in_code_block = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if in_code_block {
|
||||
if !code_content.is_empty() {
|
||||
code_content.push('\n');
|
||||
}
|
||||
code_content.push_str(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
let trimmed = line.trim();
|
||||
|
||||
if trimmed.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if trimmed.starts_with("### ") {
|
||||
html.push_str(&format!("<h3>{}</h3>\n", html_escape(&trimmed[4..])));
|
||||
} else if trimmed.starts_with("## ") {
|
||||
html.push_str(&format!("<h2>{}</h2>\n", html_escape(&trimmed[3..])));
|
||||
} else if trimmed.starts_with("# ") {
|
||||
html.push_str(&format!("<h1>{}</h1>\n", html_escape(&trimmed[2..])));
|
||||
} else if trimmed == "---" {
|
||||
html.push_str("<hr>\n");
|
||||
} else if trimmed.starts_with('|') && trimmed.ends_with('|') {
|
||||
render_table(&mut html, trimmed);
|
||||
} else if trimmed.starts_with("- [x]") || trimmed.starts_with("- [X]") {
|
||||
html.push_str(&format!(
|
||||
"<li><input type=\"checkbox\" checked disabled> {}</li>\n",
|
||||
process_inline(&trimmed[5..].trim())
|
||||
));
|
||||
} else if trimmed.starts_with("- [ ]") {
|
||||
html.push_str(&format!(
|
||||
"<li><input type=\"checkbox\" disabled> {}</li>\n",
|
||||
process_inline(&trimmed[5..].trim())
|
||||
));
|
||||
} else if trimmed.starts_with("- ") || trimmed.starts_with("* ") {
|
||||
html.push_str(&format!("<li>{}</li>\n", process_inline(&trimmed[2..])));
|
||||
} else {
|
||||
html.push_str(&format!("<p>{}</p>\n", process_inline(trimmed)));
|
||||
}
|
||||
}
|
||||
|
||||
if in_code_block && !code_content.is_empty() {
|
||||
html.push_str("<pre><code>");
|
||||
html.push_str(&html_escape(&code_content));
|
||||
html.push_str("</code></pre>\n");
|
||||
}
|
||||
|
||||
html
|
||||
}
|
||||
|
||||
fn render_table(html: &mut String, line: &str) {
|
||||
let cells: Vec<&str> = line.split('|').collect();
|
||||
let is_sep = cells.iter().any(|c| {
|
||||
let t = c.trim();
|
||||
t.contains("---") || t.contains(":--") || t.is_empty() && !t.contains(char::is_alphanumeric)
|
||||
});
|
||||
|
||||
if is_sep {
|
||||
return;
|
||||
}
|
||||
|
||||
let content_cells: Vec<&str> = cells.iter().filter(|c| !c.trim().is_empty()).copied().collect();
|
||||
if content_cells.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
html.push_str("<tr>");
|
||||
for cell in &content_cells {
|
||||
html.push_str(&format!("<td>{}</td>", process_inline(cell.trim())));
|
||||
}
|
||||
html.push_str("</tr>\n");
|
||||
}
|
||||
|
||||
fn process_inline(text: &str) -> String {
|
||||
// Échapper HTML d'abord
|
||||
let text = html_escape(text);
|
||||
// Puis appliquer les formats
|
||||
let text = replace_pattern(&text, "**", "strong");
|
||||
let text = replace_pattern(&text, "__", "strong");
|
||||
let text = replace_pattern(&text, "*", "em");
|
||||
let text = replace_pattern(&text, "_", "em");
|
||||
let text = replace_pattern(&text, "~~", "del");
|
||||
let text = replace_pattern(&text, "`", "code");
|
||||
// Liens [texte](url)
|
||||
replace_links(&text)
|
||||
}
|
||||
|
||||
fn replace_pattern(text: &str, marker: &str, tag: &str) -> String {
|
||||
let mut result = String::new();
|
||||
let mut chars = text.chars().peekable();
|
||||
let marker_len = marker.chars().count();
|
||||
let marker_chars: Vec<char> = marker.chars().collect();
|
||||
|
||||
while let Some(&c) = chars.peek() {
|
||||
// Vérifier si on est au début d'un marqueur
|
||||
if c == marker_chars[0] {
|
||||
let mut candidate = String::new();
|
||||
let mut clone = chars.clone();
|
||||
for _ in 0..marker_len {
|
||||
if let Some(&mc) = clone.peek() {
|
||||
candidate.push(mc);
|
||||
clone.next();
|
||||
}
|
||||
}
|
||||
if candidate == marker {
|
||||
// Avancer après le marqueur d'ouverture
|
||||
for _ in 0..marker_len {
|
||||
chars.next();
|
||||
}
|
||||
// Chercher le marqueur de fermeture
|
||||
let mut inner = String::new();
|
||||
let mut found = false;
|
||||
while let Some(&c2) = chars.peek() {
|
||||
if c2 == marker_chars[0] {
|
||||
let mut close_candidate = String::new();
|
||||
let mut close_clone = chars.clone();
|
||||
for _ in 0..marker_len {
|
||||
if let Some(&mc) = close_clone.peek() {
|
||||
close_candidate.push(mc);
|
||||
close_clone.next();
|
||||
}
|
||||
}
|
||||
if close_candidate == marker {
|
||||
for _ in 0..marker_len {
|
||||
chars.next();
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
inner.push(chars.next().unwrap());
|
||||
}
|
||||
if found {
|
||||
result.push_str(&format!("<{tag}>{inner}</{tag}>"));
|
||||
} else {
|
||||
result.push_str(&candidate);
|
||||
result.push_str(&inner);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
result.push(chars.next().unwrap());
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn replace_links(text: &str) -> String {
|
||||
let mut result = String::new();
|
||||
let chars: Vec<char> = text.chars().collect();
|
||||
let mut i = 0;
|
||||
while i < chars.len() {
|
||||
if chars[i] == '[' {
|
||||
let mut j = i + 1;
|
||||
let mut link_text = String::new();
|
||||
while j < chars.len() && chars[j] != ']' {
|
||||
link_text.push(chars[j]);
|
||||
j += 1;
|
||||
}
|
||||
if j + 2 < chars.len() && chars[j] == ']' && chars[j + 1] == '(' {
|
||||
let mut k = j + 2;
|
||||
let mut url = String::new();
|
||||
while k < chars.len() && chars[k] != ')' {
|
||||
url.push(chars[k]);
|
||||
k += 1;
|
||||
}
|
||||
if k < chars.len() && chars[k] == ')' {
|
||||
result.push_str(&format!("<a href=\"{url}\">{link_text}</a>"));
|
||||
i = k + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Pas un lien valide, remettre le [
|
||||
result.push('[');
|
||||
result.push_str(&link_text);
|
||||
i = j;
|
||||
} else {
|
||||
result.push(chars[i]);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn html_escape(text: &str) -> String {
|
||||
text.replace('&', "&")
|
||||
.replace('<', "<")
|
||||
.replace('>', ">")
|
||||
.replace('"', """)
|
||||
}
|
||||
|
||||
/// Remplace toutes les occurrences de `vX.Y.Z` dans le HTML par la version courante
|
||||
fn replace_all_versions(html: &str, version: &str) -> String {
|
||||
let mut result = String::new();
|
||||
let chars: Vec<char> = html.chars().collect();
|
||||
let mut i = 0;
|
||||
|
||||
while i < chars.len() {
|
||||
if chars[i] == 'v'
|
||||
&& i + 1 < chars.len()
|
||||
&& chars[i + 1].is_ascii_digit()
|
||||
{
|
||||
// Essayer de parser une version vX.Y.Z ou vX.Y
|
||||
let mut j = i + 1;
|
||||
let mut dots = 0;
|
||||
while j < chars.len() {
|
||||
if chars[j].is_ascii_digit() {
|
||||
j += 1;
|
||||
} else if chars[j] == '.' && dots < 2 && j + 1 < chars.len() && chars[j + 1].is_ascii_digit() {
|
||||
dots += 1;
|
||||
j += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if dots >= 1 {
|
||||
// C'est une version — remplacer
|
||||
result.push_str(&format!("v{version}"));
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
result.push(chars[i]);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
Reference in New Issue
Block a user