- crates/ld-engine : formule inertielle (parité avec la maquette, garde C), coquille / changement de fond, assistant d'édition à règles, composition d'un document sous vote permanent (adoptions, aperçus, rivales). - crates/trustin-access : qui voit, qui ouvre, qui gère les accès. - crates/ld-server : API axum + SQLite, connexion Ğ1 (client OAuth2 de connect.monnaie-libre.fr), invitations, entrée en vigueur automatique, signalements de contradiction, traductions éditoriales, publication git, taille de la toile lue sur la chaîne. - seeds/acte-engagement-certification.json : 33 articles, fr + en + es ; en vigueur : préambule, E1, E2 (seed a minima). - maquette/ : frontend, backend et docker v2 déplacés tels quels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
64 lines
1.7 KiB
Rust
64 lines
1.7 KiB
Rust
//! Small shared helpers: clock, randomness, hashing, language maps.
|
|
|
|
use rand::RngCore;
|
|
use sha2::{Digest, Sha256};
|
|
use std::collections::BTreeMap;
|
|
use time::OffsetDateTime;
|
|
use time::format_description::well_known::Rfc3339;
|
|
|
|
/// Languages of the interface and of the editorial translations (Yvv, 2026-10-21).
|
|
pub const LANGS: [&str; 3] = ["fr", "en", "es"];
|
|
|
|
pub fn now() -> String {
|
|
stamp(OffsetDateTime::now_utc())
|
|
}
|
|
|
|
pub fn stamp(at: OffsetDateTime) -> String {
|
|
at.replace_nanosecond(0).unwrap_or(at).format(&Rfc3339).unwrap_or_default()
|
|
}
|
|
|
|
pub fn in_days(days: i64) -> String {
|
|
stamp(OffsetDateTime::now_utc() + time::Duration::days(days))
|
|
}
|
|
|
|
pub fn in_seconds(secs: i64) -> String {
|
|
stamp(OffsetDateTime::now_utc() + time::Duration::seconds(secs))
|
|
}
|
|
|
|
/// 32 random bytes, hex — session tokens, invitation tokens, OAuth state.
|
|
pub fn token() -> String {
|
|
let mut bytes = [0u8; 32];
|
|
rand::rng().fill_bytes(&mut bytes);
|
|
hex::encode(bytes)
|
|
}
|
|
|
|
/// 16 random bytes, hex — row identifiers.
|
|
pub fn id() -> String {
|
|
let mut bytes = [0u8; 16];
|
|
rand::rng().fill_bytes(&mut bytes);
|
|
hex::encode(bytes)
|
|
}
|
|
|
|
pub fn sha256_hex(data: &str) -> String {
|
|
hex::encode(Sha256::digest(data.as_bytes()))
|
|
}
|
|
|
|
/// The hash a translation is pinned to: it goes stale when the source changes.
|
|
pub fn source_hash(title: &str, body: &str) -> String {
|
|
sha256_hex(&format!("{title}\n\u{1f}\n{body}"))
|
|
}
|
|
|
|
pub type LangMap = BTreeMap<String, String>;
|
|
|
|
pub fn lang_json(map: &LangMap) -> String {
|
|
serde_json::to_string(map).unwrap_or_else(|_| "{}".into())
|
|
}
|
|
|
|
pub fn parse_lang(raw: &str) -> LangMap {
|
|
serde_json::from_str(raw).unwrap_or_default()
|
|
}
|
|
|
|
pub fn is_lang(lang: &str) -> bool {
|
|
LANGS.contains(&lang)
|
|
}
|