futucli/cmd/
version_check.rs1use std::time::Duration;
2
3use anyhow::Result;
4use serde_json::json;
5
6use crate::output::OutputFormat;
7
8pub use futu_core::update_check::{UpdateDecision, UpdateLevel};
9#[cfg(test)]
10pub use futu_core::update_check::{VersionManifest, decide_update};
11
12#[cfg(test)]
13const BUNDLED_VERSION_MANIFEST_JSON: &str = include_str!("../../../../docs-site/docs/version.json");
14
15pub async fn run_version(
16 check: bool,
17 url: Option<&str>,
18 timeout_ms: u64,
19 output: OutputFormat,
20) -> Result<()> {
21 let current_version = env!("CARGO_PKG_VERSION");
22 if !check {
23 return print_version_report(
24 output,
25 json!({
26 "current_version": current_version,
27 "update_check": "disabled",
28 "hint": "run `futucli version --check` to check https://futuapi.com/version.json",
29 }),
30 );
31 }
32
33 let decision = check_for_update(current_version, url, timeout_ms).await?;
34 print_version_report(
35 output,
36 json!({
37 "current_version": current_version,
38 "latest_version": decision.latest_version,
39 "level": decision.level,
40 "message": decision.message,
41 "action": decision.action,
42 "update_check_url": resolve_update_check_url(url),
43 }),
44 )
45}
46
47pub async fn check_for_update(
48 current_version: &str,
49 explicit_url: Option<&str>,
50 timeout_ms: u64,
51) -> Result<UpdateDecision> {
52 let url = resolve_update_check_url(explicit_url);
53 futu_core::update_check::check_for_update(
54 current_version,
55 &url,
56 Duration::from_millis(timeout_ms.max(1)),
57 )
58 .await
59}
60
61pub fn resolve_update_check_url(explicit_url: Option<&str>) -> String {
62 futu_core::update_check::resolve_update_check_url(explicit_url)
63}
64
65fn print_version_report(output: OutputFormat, value: serde_json::Value) -> Result<()> {
66 match output {
67 OutputFormat::Json => println!("{}", serde_json::to_string_pretty(&value)?),
68 OutputFormat::Jsonl => println!("{}", serde_json::to_string(&value)?),
69 OutputFormat::Table | OutputFormat::Markdown => {
70 println!("futucli version");
71 if let Some(current) = value.get("current_version").and_then(|v| v.as_str()) {
72 println!("current_version: {current}");
73 }
74 if let Some(latest) = value.get("latest_version").and_then(|v| v.as_str()) {
75 println!("latest_version: {latest}");
76 }
77 if let Some(level) = value.get("level").and_then(|v| v.as_str()) {
78 println!("level: {level}");
79 }
80 if let Some(message) = value.get("message").and_then(|v| v.as_str()) {
81 println!("message: {message}");
82 }
83 if let Some(action) = value.get("action").and_then(|v| v.as_str()) {
84 println!("next: {action}");
85 }
86 if let Some(hint) = value.get("hint").and_then(|v| v.as_str()) {
87 println!("hint: {hint}");
88 }
89 }
90 }
91 Ok(())
92}
93
94#[cfg(test)]
95pub(crate) fn bundled_version_manifest() -> Result<VersionManifest> {
96 serde_json::from_str(BUNDLED_VERSION_MANIFEST_JSON).map_err(anyhow::Error::from)
97}