mnemo/lifecycle/
update.rs1use std::io::{self, IsTerminal, Write};
6
7use anyhow::Result;
8use serde::Serialize;
9
10use super::github::fetch_latest_release;
11use super::{current_version, normalize_tag, target_triple, update_available};
12
13#[derive(Debug, Clone, Serialize)]
15pub struct UpdateReport {
16 pub current_version: String,
17 pub latest_version: String,
18 pub update_available: bool,
19 pub asset_target: String,
20}
21
22impl UpdateReport {
23 pub fn new(current: &str, latest: &str) -> Self {
25 Self {
26 current_version: normalize_tag(current),
27 latest_version: normalize_tag(latest),
28 update_available: update_available(current, latest),
29 asset_target: target_triple().to_string(),
30 }
31 }
32}
33
34fn interpret_yes(answer: &str) -> bool {
38 let a = answer.trim().to_lowercase();
39 a == "o" || a == "oui" || a == "y" || a == "yes"
40}
41
42fn prompt_install_now() -> Result<bool> {
47 if !io::stdin().is_terminal() || !io::stdout().is_terminal() {
48 return Ok(false);
49 }
50 print!("Installer maintenant avec `mnemo upgrade` ? [o/N] ");
51 io::stdout().flush()?;
52 let mut answer = String::new();
53 io::stdin().read_line(&mut answer)?;
54 Ok(interpret_yes(&answer))
55}
56
57pub fn run(json: bool, upgrade: bool, assume_yes: bool, require_signature: bool) -> Result<()> {
65 let current = current_version();
66 let latest = fetch_latest_release()?.tag_name;
67 let report = UpdateReport::new(¤t, &latest);
68
69 if json {
70 println!("{}", serde_json::to_string_pretty(&report)?);
73 return Ok(());
74 }
75
76 println!("Version installée : {}", report.current_version);
77 println!("Dernière version : {}", report.latest_version);
78
79 if !report.update_available {
80 println!("mnemo est à jour ✓");
81 return Ok(());
82 }
83
84 println!("Mise à jour disponible ✓");
85
86 if upgrade {
91 return super::upgrade::run(false, assume_yes, None, None, require_signature);
92 }
93
94 if prompt_install_now()? {
99 return super::upgrade::run(false, true, None, None, require_signature);
100 }
101
102 println!(" Lancez `mnemo upgrade` pour l'installer.");
105 Ok(())
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn rapport_mise_a_jour_disponible() {
114 let r = UpdateReport::new("v0.4.0", "v0.5.0");
115 assert_eq!(r.current_version, "v0.4.0");
116 assert_eq!(r.latest_version, "v0.5.0");
117 assert!(r.update_available);
118 assert!(r.asset_target.contains("linux-musl"));
119 }
120
121 #[test]
122 fn rapport_a_jour() {
123 let r = UpdateReport::new("v0.5.0", "v0.5.0");
124 assert!(!r.update_available);
125 }
126
127 #[test]
128 fn rapport_serialise_json() {
129 let r = UpdateReport::new("0.4.0", "0.5.0");
130 let json = serde_json::to_value(&r).unwrap();
131 assert_eq!(json["current_version"], "v0.4.0");
132 assert_eq!(json["latest_version"], "v0.5.0");
133 assert_eq!(json["update_available"], true);
134 assert!(json["asset_target"].is_string());
135 }
136
137 #[test]
138 fn interpret_yes_accepte_les_reponses_affirmatives() {
139 for ok in ["o", "O", "oui", "OUI", "y", "Y", "yes", "Yes", " oui "] {
140 assert!(interpret_yes(ok), "« {ok} » devrait valoir oui");
141 }
142 }
143
144 #[test]
145 fn interpret_yes_refuse_le_reste_et_la_ligne_vide() {
146 for no in ["", " ", "\n", "n", "non", "no", "nope", "x", "1"] {
147 assert!(!interpret_yes(no), "« {no} » devrait valoir non");
148 }
149 }
150}