section.rs
63 lignes · 2251 octets
//! Remplacement idempotent d'une section délimitée par des marqueurs HTML. //! Permet de régénérer les blocs de sécurité lors d'un re-run du wizard sans //! écraser le reste du document (charte, tableau, prose ajoutée par le dev). /// Bloc encadré : `<!-- id -->\n{body}\n<!-- /id -->`. pub fn block(id: &str, body: &str) -> String { format!("<!-- {id} -->\n{}\n<!-- /{id} -->", body.trim_end()) } /// Insère (à la fin) ou remplace en place la section `id` par `body`. pub fn upsert(content: &str, id: &str, body: &str) -> String { let block = block(id, body); let open = format!("<!-- {id} -->"); let close = format!("<!-- /{id} -->"); if let (Some(start), Some(close_at)) = (content.find(&open), content.find(&close)) { if start < close_at { let end = close_at + close.len(); let mut out = String::with_capacity(content.len() + block.len()); out.push_str(&content[..start]); out.push_str(&block); out.push_str(&content[end..]); return out; } } let mut out = content.to_string(); if !out.ends_with('\n') { out.push('\n'); } out.push('\n'); out.push_str(&block); out.push('\n'); out } #[cfg(test)] mod tests { use super::*; #[test] fn block_encadre_le_corps() { let b = block("demo:x", "corps\n"); assert_eq!(b, "<!-- demo:x -->\ncorps\n<!-- /demo:x -->"); } #[test] fn upsert_ajoute_a_la_fin_si_absent() { let out = upsert("# Titre\n", "demo:x", "corps"); assert!(out.starts_with("# Titre\n")); assert!(out.contains("<!-- demo:x -->\ncorps\n<!-- /demo:x -->")); } #[test] fn upsert_remplace_en_place_et_est_idempotent() { let base = format!("A\n\n{}\n\nB\n", block("demo:x", "v1")); let once = upsert(&base, "demo:x", "v2"); assert!(once.contains("A\n"), "préfixe préservé"); assert!(once.contains("B\n"), "suffixe préservé"); assert!(once.contains("v2")); assert!(!once.contains("v1"), "ancien corps remplacé"); // idempotence : ré-appliquer avec le même corps ne change rien. assert_eq!(upsert(&once, "demo:x", "v2"), once); } }
GitRust