Skip to main content

mnemo/
version.rs

1//! Commande `mnemo version` : affiche des informations détaillées sur le build.
2
3use std::io::{self, Write};
4
5/// Architecture cible (déduite à la compilation).
6const TARGET_ARCH: &str = std::env::consts::ARCH;
7/// Système d'exploitation cible.
8const TARGET_OS: &str = std::env::consts::OS;
9
10/// Profil de compilation : `release` ou `debug`.
11fn build_profile() -> &'static str {
12    if cfg!(debug_assertions) {
13        "debug"
14    } else {
15        "release"
16    }
17}
18
19/// Chemin du binaire courant, si disponible.
20fn binary_path() -> Option<String> {
21    std::env::current_exe()
22        .ok()
23        .map(|p| p.display().to_string())
24}
25
26/// Affiche le rapport de version.
27///
28/// L'écriture passe par un stdout verrouillé : si la sortie est pipée vers
29/// `head`/`less` et que le tube est fermé en avance, le `BrokenPipe` remonte
30/// comme une erreur propre (interceptée dans `main`) au lieu de faire paniquer
31/// `println!`.
32pub fn run() -> io::Result<()> {
33    let stdout = io::stdout();
34    let mut out = stdout.lock();
35    write_report(&mut out)
36}
37
38/// Écrit le rapport de version sur un writer quelconque (testable).
39fn write_report<W: Write>(out: &mut W) -> io::Result<()> {
40    writeln!(out, "mnemo {}", env!("CARGO_PKG_VERSION"))?;
41    writeln!(out, "  cible   : {TARGET_OS}/{TARGET_ARCH}")?;
42    writeln!(out, "  profil  : {}", build_profile())?;
43    match binary_path() {
44        Some(p) => writeln!(out, "  binaire : {p}")?,
45        None => writeln!(out, "  binaire : (indisponible)")?,
46    }
47    Ok(())
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    /// Writer qui échoue systématiquement avec `BrokenPipe`, pour simuler une
55    /// sortie pipée vers un consommateur qui ferme le tube (`| head`).
56    struct BrokenPipeWriter;
57
58    impl Write for BrokenPipeWriter {
59        fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
60            Err(io::Error::new(io::ErrorKind::BrokenPipe, "broken pipe"))
61        }
62
63        fn flush(&mut self) -> io::Result<()> {
64            Ok(())
65        }
66    }
67
68    #[test]
69    fn write_report_ecrit_la_version() {
70        let mut buf: Vec<u8> = Vec::new();
71        write_report(&mut buf).expect("écriture en mémoire");
72        let rendu = String::from_utf8(buf).expect("utf8");
73        assert!(rendu.starts_with("mnemo "));
74        assert!(rendu.contains("cible"));
75    }
76
77    #[test]
78    fn write_report_remonte_broken_pipe_sans_paniquer() {
79        let mut writer = BrokenPipeWriter;
80        let err = write_report(&mut writer).expect_err("doit retourner une erreur");
81        assert_eq!(err.kind(), io::ErrorKind::BrokenPipe);
82    }
83}