refactor(cli): cleanup info command (#7204)

Co-authored-by: martin frances <martinfrances107@hotmail.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
Amr Bashir 2023-10-17 19:09:59 +03:00 committed by GitHub
parent 550173aaf5
commit 99865d9e9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 364 additions and 507 deletions

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::{SectionItem, Status};
use super::SectionItem;
use crate::helpers::framework;
use std::{fs::read_to_string, path::PathBuf};
@ -14,15 +14,11 @@ pub fn items(app_dir: Option<&PathBuf>, tauri_dir: Option<PathBuf>) -> Vec<Secti
let config = config_guard.as_ref().unwrap();
let bundle_or_build = if config.tauri.bundle.active {
"bundle".to_string()
"bundle"
} else {
"build".to_string()
"build"
};
items.push(SectionItem::new(
move || Some((format!("build-type: {bundle_or_build}"), Status::Neutral)),
|| None,
false,
));
items.push(SectionItem::new().description(format!("build-type: {bundle_or_build}")));
let csp = config
.tauri
@ -31,42 +27,24 @@ pub fn items(app_dir: Option<&PathBuf>, tauri_dir: Option<PathBuf>) -> Vec<Secti
.clone()
.map(|c| c.to_string())
.unwrap_or_else(|| "unset".to_string());
items.push(SectionItem::new(
move || Some((format!("CSP: {csp}"), Status::Neutral)),
|| None,
false,
));
items.push(SectionItem::new().description(format!("CSP: {csp}")));
let dist_dir = config.build.dist_dir.to_string();
items.push(SectionItem::new(
move || Some((format!("distDir: {dist_dir}"), Status::Neutral)),
|| None,
false,
));
let dist_dir = &config.build.dist_dir;
items.push(SectionItem::new().description(format!("distDir: {dist_dir}")));
let dev_path = config.build.dev_path.to_string();
items.push(SectionItem::new(
move || Some((format!("devPath: {dev_path}"), Status::Neutral)),
|| None,
false,
));
let dev_path = &config.build.dev_path;
items.push(SectionItem::new().description(format!("devPath: {dev_path}")));
if let Some(app_dir) = app_dir {
if let Ok(package_json) = read_to_string(app_dir.join("package.json")) {
let (framework, bundler) = framework::infer_from_package_json(&package_json);
if let Some(framework) = framework {
items.push(SectionItem::new(
move || Some((format!("framework: {framework}"), Status::Neutral)),
|| None,
false,
));
items.push(SectionItem::new().description(format!("framework: {framework}")));
}
if let Some(bundler) = bundler {
items.push(SectionItem::new(
move || Some((format!("bundler: {bundler}"), Status::Neutral)),
|| None,
false,
));
items.push(SectionItem::new().description(format!("bundler: {bundler}")));
}
}
}

View File

@ -2,12 +2,11 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::{cross_command, VersionMetadata};
use super::{SectionItem, Status};
use super::{cross_command, ActionResult, SectionItem, VersionMetadata};
use colored::Colorize;
pub fn items(metadata: &VersionMetadata) -> (Vec<SectionItem>, Option<String>) {
let yarn_version = cross_command("yarn")
pub fn manager_version(package_manager: &str) -> Option<String> {
cross_command(package_manager)
.arg("-v")
.output()
.map(|o| {
@ -19,129 +18,58 @@ pub fn items(metadata: &VersionMetadata) -> (Vec<SectionItem>, Option<String>) {
}
})
.ok()
.unwrap_or_default();
let yarn_version_c = yarn_version.clone();
.unwrap_or_default()
}
pub fn items(metadata: &VersionMetadata) -> Vec<SectionItem> {
let node_target_ver = metadata.js_cli.node.replace(">= ", "");
(
vec![
SectionItem::new(
move || {
cross_command("node")
.arg("-v")
.output()
.map(|o| {
if o.status.success() {
let v = String::from_utf8_lossy(o.stdout.as_slice()).to_string();
let v = v
.split('\n')
.next()
.unwrap()
.strip_prefix('v')
.unwrap_or_default()
.trim();
Some((
format!("node: {}{}", v, {
let version = semver::Version::parse(v).unwrap();
let target_version = semver::Version::parse(node_target_ver.as_str()).unwrap();
if version < target_version {
format!(
" ({}, latest: {})",
"outdated".red(),
target_version.to_string().green()
)
} else {
"".into()
}
}),
Status::Neutral,
))
vec![
SectionItem::new().action(move || {
cross_command("node")
.arg("-v")
.output()
.map(|o| {
if o.status.success() {
let v = String::from_utf8_lossy(o.stdout.as_slice()).to_string();
let v = v
.split('\n')
.next()
.unwrap()
.strip_prefix('v')
.unwrap_or_default()
.trim();
ActionResult::Description(format!("node: {}{}", v, {
let version = semver::Version::parse(v).unwrap();
let target_version = semver::Version::parse(node_target_ver.as_str()).unwrap();
if version < target_version {
format!(
" ({}, latest: {})",
"outdated".red(),
target_version.to_string().green()
)
} else {
None
"".into()
}
})
.ok()
.unwrap_or_default()
},
|| None,
false,
),
SectionItem::new(
|| {
cross_command("pnpm")
.arg("-v")
.output()
.map(|o| {
if o.status.success() {
let v = String::from_utf8_lossy(o.stdout.as_slice()).to_string();
Some((
format!("pnpm: {}", v.split('\n').next().unwrap()),
Status::Neutral,
))
} else {
None
}
})
.ok()
.unwrap_or_default()
},
|| None,
false,
),
SectionItem::new(
|| {
cross_command("bun")
.arg("-v")
.output()
.map(|o| {
if o.status.success() {
let v = String::from_utf8_lossy(o.stdout.as_slice()).to_string();
Some((
format!("bun: {}", v.split('\n').next().unwrap()),
Status::Neutral,
))
} else {
None
}
})
.ok()
.unwrap_or_default()
},
|| None,
false,
),
SectionItem::new(
move || {
yarn_version_c
.as_ref()
.map(|v| (format!("yarn: {v}"), Status::Neutral))
},
|| None,
false,
),
SectionItem::new(
|| {
cross_command("npm")
.arg("-v")
.output()
.map(|o| {
if o.status.success() {
let v = String::from_utf8_lossy(o.stdout.as_slice()).to_string();
Some((
format!("npm: {}", v.split('\n').next().unwrap()),
Status::Neutral,
))
} else {
None
}
})
.ok()
.unwrap_or_default()
},
|| None,
false,
),
],
yarn_version,
)
}))
} else {
ActionResult::None
}
})
.ok()
.unwrap_or_default()
}),
SectionItem::new().action(|| {
manager_version("pnpm")
.map(|v| format!("pnpm: {}", v))
.into()
}),
SectionItem::new().action(|| {
manager_version("yarn")
.map(|v| format!("yarn: {}", v))
.into()
}),
SectionItem::new().action(|| manager_version("npm").map(|v| format!("npm: {}", v)).into()),
SectionItem::new().action(|| manager_version("bun").map(|v| format!("bun: {}", v)).into()),
]
}

View File

@ -7,95 +7,55 @@ use super::Status;
use colored::Colorize;
use std::process::Command;
fn component_version(component: &str) -> Option<(String, Status)> {
Command::new(component)
.arg("-V")
.output()
.map(|o| String::from_utf8_lossy(o.stdout.as_slice()).to_string())
.map(|v| {
format!(
"{component}: {}",
v.split('\n')
.next()
.unwrap()
.strip_prefix(&format!("{component} "))
.unwrap_or_default()
)
})
.map(|desc| (desc, Status::Success))
.ok()
}
pub fn items() -> Vec<SectionItem> {
vec![
SectionItem::new(
|| {
Some(
Command::new("rustc")
.arg("-V")
.output()
.map(|o| String::from_utf8_lossy(o.stdout.as_slice()).to_string())
.map(|v| {
SectionItem::new().action(|| {
component_version("rustc")
.unwrap_or_else(|| {
(
format!(
"rustc: {}",
v.split('\n')
.next()
.unwrap()
.strip_prefix("rustc ")
.unwrap_or_default()
)
})
.map(|desc| (desc, Status::Success))
.ok()
.unwrap_or_else(|| {
(
format!(
"rustc: {}\nMaybe you don't have rust installed! Visit {}",
"not installed!".red(),
"https://rustup.rs/".cyan()
),
Status::Error,
)
}),
)
},
|| None,
false,
),
SectionItem::new(
|| {
Some(
Command::new("cargo")
.arg("-V")
.output()
.map(|o| String::from_utf8_lossy(o.stdout.as_slice()).to_string())
.map(|v| {
"rustc: {}\nMaybe you don't have rust installed! Visit {}",
"not installed!".red(),
"https://rustup.rs/".cyan()
),
Status::Error,
)
}).into()
}),
SectionItem::new().action(|| {
component_version("cargo")
.unwrap_or_else(|| {
(
format!(
"Cargo: {}",
v.split('\n')
.next()
.unwrap()
.strip_prefix("cargo ")
.unwrap_or_default()
)
})
.map(|desc| (desc, Status::Success))
.ok()
.unwrap_or_else(|| {
(
format!(
"Cargo: {}\nMaybe you don't have rust installed! Visit {}",
"not installed!".red(),
"https://rustup.rs/".cyan()
),
Status::Error,
)
}),
)
},
|| None,
false,
),
SectionItem::new(
|| {
Some(
Command::new("rustup")
.arg("-V")
.output()
.map(|o| String::from_utf8_lossy(o.stdout.as_slice()).to_string())
.map(|v| {
format!(
"rustup: {}",
v.split('\n')
.next()
.unwrap()
.strip_prefix("rustup ")
.unwrap_or_default()
)
})
.map(|desc| (desc, Status::Success))
.ok()
"Cargo: {}\nMaybe you don't have rust installed! Visit {}",
"not installed!".red(),
"https://rustup.rs/".cyan()
),
Status::Error,
)
}).into()
}),
SectionItem::new().action(|| {
component_version("rustup")
.unwrap_or_else(|| {
(
format!(
@ -105,15 +65,9 @@ pub fn items() -> Vec<SectionItem> {
),
Status::Warning,
)
}),
)
},
|| None,
false,
),
SectionItem::new(
|| {
Some(
}).into()
}),
SectionItem::new().action(|| {
Command::new("rustup")
.args(["show", "active-toolchain"])
.output()
@ -135,11 +89,7 @@ pub fn items() -> Vec<SectionItem> {
),
Status::Warning,
)
}),
)
},
|| None,
false,
),
}).into()
}),
]
}

View File

@ -2,8 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::SectionItem;
use super::Status;
use super::{SectionItem, Status};
use colored::Colorize;
#[cfg(windows)]
use serde::Deserialize;
@ -177,74 +176,55 @@ fn is_xcode_command_line_tools_installed() -> bool {
pub fn items() -> Vec<SectionItem> {
vec![
SectionItem::new(
|| {
let os_info = os_info::get();
Some((
format!(
"OS: {} {} {:?}",
os_info.os_type(),
os_info.version(),
os_info.bitness()
),
Status::Neutral,
))
},
|| None,
false,
),
SectionItem::new().action(|| {
let os_info = os_info::get();
format!(
"OS: {} {} {:?}",
os_info.os_type(),
os_info.version(),
os_info.bitness()
).into()
}),
#[cfg(windows)]
SectionItem::new(
|| {
let error = || {
format!(
"Webview2: {}\nVisit {}",
"not installed!".red(),
"https://developer.microsoft.com/en-us/microsoft-edge/webview2/".cyan()
)
};
Some(
webview2_version()
.map(|v| {
v.map(|v| (format!("WebView2: {}", v), Status::Success))
.unwrap_or_else(|| (error(), Status::Error))
})
.unwrap_or_else(|_| (error(), Status::Error)),
)
},
|| None,
false,
),
SectionItem::new().action(|| {
let error = format!(
"Webview2: {}\nVisit {}",
"not installed!".red(),
"https://developer.microsoft.com/en-us/microsoft-edge/webview2/".cyan()
);
webview2_version()
.map(|v| {
v.map(|v| (format!("WebView2: {}", v), Status::Success))
.unwrap_or_else(|| (error.clone(), Status::Error))
})
.unwrap_or_else(|_| (error, Status::Error)).into()
}),
#[cfg(windows)]
SectionItem::new(
|| {
let build_tools = build_tools_version().unwrap_or_default();
if build_tools.is_empty() {
Some((
SectionItem::new().action(|| {
let build_tools = build_tools_version().unwrap_or_default();
if build_tools.is_empty() {
(
format!(
"Couldn't detect any Visual Studio or VS Build Tools instance with MSVC and SDK components. Download from {}",
"https://aka.ms/vs/17/release/vs_BuildTools.exe".cyan()
),
Status::Error,
))
} else {
Some((
format!(
"MSVC: {}{}",
if build_tools.len() > 1 {
format!("\n {} ", "-".cyan())
} else {
"".into()
},
build_tools.join(format!("\n {} ", "-".cyan()).as_str()),
),
Status::Success,
))
}
},
|| None,
false,
),
).into()
} else {
(
format!(
"MSVC: {}{}",
if build_tools.len() > 1 {
format!("\n {} ", "-".cyan())
} else {
"".into()
},
build_tools.join(format!("\n {} ", "-".cyan()).as_str()),
),
Status::Success,
).into()
}
}),
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
@ -252,9 +232,7 @@ pub fn items() -> Vec<SectionItem> {
target_os = "openbsd",
target_os = "netbsd"
))]
SectionItem::new(
|| {
Some(
SectionItem::new().action(|| {
webkit2gtk_ver()
.map(|v| (format!("webkit2gtk-4.0: {v}"), Status::Success))
.unwrap_or_else(|| {
@ -266,11 +244,8 @@ pub fn items() -> Vec<SectionItem> {
),
Status::Error,
)
}),
)
}).into()
},
|| None,
false,
),
#[cfg(any(
target_os = "linux",
@ -279,9 +254,7 @@ pub fn items() -> Vec<SectionItem> {
target_os = "openbsd",
target_os = "netbsd"
))]
SectionItem::new(
|| {
Some(
SectionItem::new().action(|| {
rsvg2_ver()
.map(|v| (format!("rsvg2: {v}"), Status::Success))
.unwrap_or_else(|| {
@ -293,16 +266,12 @@ pub fn items() -> Vec<SectionItem> {
),
Status::Error,
)
}),
)
}).into()
},
|| None,
false,
),
#[cfg(target_os = "macos")]
SectionItem::new(
|| {
Some(if is_xcode_command_line_tools_installed() {
SectionItem::new().action(|| {
if is_xcode_command_line_tools_installed() {
(
"Xcode Command Line Tools: installed".into(),
Status::Success,
@ -316,10 +285,8 @@ pub fn items() -> Vec<SectionItem> {
),
Status::Error,
)
})
}.into()
},
|| None,
false,
),
]
}

View File

@ -4,7 +4,7 @@
use crate::Result;
use clap::Parser;
use colored::Colorize;
use colored::{ColoredString, Colorize};
use dialoguer::{theme::ColorfulTheme, Confirm};
use serde::Deserialize;
use std::{
@ -92,6 +92,18 @@ pub enum Status {
Error,
}
impl Status {
fn color<S: AsRef<str>>(&self, s: S) -> ColoredString {
let s = s.as_ref();
match self {
Status::Neutral => s.normal(),
Status::Success => s.green(),
Status::Warning => s.yellow(),
Status::Error => s.red(),
}
}
}
impl Display for Status {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
@ -107,15 +119,55 @@ impl Display for Status {
}
}
#[derive(Default)]
pub enum ActionResult {
Full {
description: String,
status: Status,
},
Description(String),
#[default]
None,
}
impl From<String> for ActionResult {
fn from(value: String) -> Self {
ActionResult::Description(value)
}
}
impl From<(String, Status)> for ActionResult {
fn from(value: (String, Status)) -> Self {
ActionResult::Full {
description: value.0,
status: value.1,
}
}
}
impl From<Option<String>> for ActionResult {
fn from(value: Option<String>) -> Self {
value.map(ActionResult::Description).unwrap_or_default()
}
}
impl From<Option<(String, Status)>> for ActionResult {
fn from(value: Option<(String, Status)>) -> Self {
value
.map(|v| ActionResult::Full {
description: v.0,
status: v.1,
})
.unwrap_or_default()
}
}
pub struct SectionItem {
/// If description is none, the item is skipped
description: Option<String>,
status: Status,
/// This closure return will be assigned to status and description
action: Box<dyn FnMut() -> Option<(String, Status)>>,
/// This closure return will be assigned to status and description
action_if_err: Box<dyn FnMut() -> Option<(String, Status)>>,
has_action_if_err: bool,
action: Option<Box<dyn FnMut() -> ActionResult>>,
action_if_err: Option<Box<dyn FnMut() -> ActionResult>>,
}
impl Display for SectionItem {
@ -131,29 +183,66 @@ impl Display for SectionItem {
}
impl SectionItem {
fn new<
F1: FnMut() -> Option<(String, Status)> + 'static,
F2: FnMut() -> Option<(String, Status)> + 'static,
>(
action: F1,
action_if_err: F2,
has_action_if_err: bool,
) -> Self {
fn new() -> Self {
Self {
action: Box::new(action),
action_if_err: Box::new(action_if_err),
has_action_if_err,
action: None,
action_if_err: None,
description: None,
status: Status::Neutral,
}
}
fn run(&mut self, interactive: bool) -> Status {
if let Some(ret) = (self.action)() {
self.description = Some(ret.0);
self.status = ret.1;
}
if self.status == Status::Error && interactive && self.has_action_if_err {
fn action<F: FnMut() -> ActionResult + 'static>(mut self, action: F) -> Self {
self.action = Some(Box::new(action));
self
}
// fn action_if_err<F: FnMut() -> ActionResult + 'static>(mut self, action: F) -> Self {
// self.action_if_err = Some(Box::new(action));
// self
// }
fn description<S: AsRef<str>>(mut self, description: S) -> Self {
self.description = Some(description.as_ref().to_string());
self
}
fn run_action(&mut self) {
let mut res = ActionResult::None;
if let Some(action) = &mut self.action {
res = action();
}
self.apply_action_result(res);
}
fn run_action_if_err(&mut self) {
let mut res = ActionResult::None;
if let Some(action) = &mut self.action_if_err {
res = action();
}
self.apply_action_result(res);
}
fn apply_action_result(&mut self, result: ActionResult) {
match result {
ActionResult::Full {
description,
status,
} => {
self.description = Some(description);
self.status = status;
}
ActionResult::Description(description) => {
self.description = Some(description);
}
ActionResult::None => {}
}
}
fn run(&mut self, interactive: bool) -> Status {
self.run_action();
if self.status == Status::Error && interactive && self.action_if_err.is_some() {
if let Some(description) = &self.description {
let confirmed = Confirm::with_theme(&ColorfulTheme::default())
.with_prompt(format!(
@ -163,13 +252,11 @@ impl SectionItem {
.interact()
.unwrap_or(false);
if confirmed {
if let Some(ret) = (self.action_if_err)() {
self.description = Some(ret.0);
self.status = ret.1;
}
self.run_action_if_err()
}
}
}
self.status
}
}
@ -192,12 +279,7 @@ impl Section<'_> {
}
let status_str = format!("[{status}]");
let status = match status {
Status::Neutral => status_str.normal(),
Status::Success => status_str.green(),
Status::Warning => status_str.yellow(),
Status::Error => status_str.red(),
};
let status = status.color(status_str);
println!();
println!("{} {}", status, self.label.bold().yellow());
@ -239,7 +321,7 @@ pub fn command(options: Options) -> Result<()> {
};
environment.items.extend(env_system::items());
environment.items.extend(env_rust::items());
let (items, yarn_version) = env_nodejs::items(&metadata);
let items = env_nodejs::items(&metadata);
environment.items.extend(items);
let mut packages = Section {
@ -252,7 +334,7 @@ pub fn command(options: Options) -> Result<()> {
.extend(packages_rust::items(app_dir, tauri_dir.clone()));
packages
.items
.extend(packages_nodejs::items(app_dir, &metadata, yarn_version));
.extend(packages_nodejs::items(app_dir, &metadata));
let mut app = Section {
label: "App",

View File

@ -2,8 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::{cross_command, VersionMetadata};
use super::{SectionItem, Status};
use super::SectionItem;
use super::{cross_command, env_nodejs::manager_version, VersionMetadata};
use colored::Colorize;
use serde::Deserialize;
use std::fmt::Display;
@ -241,11 +241,7 @@ fn get_package_manager<T: AsRef<str>>(app_dir_entries: &[T]) -> PackageManager {
}
}
pub fn items(
app_dir: Option<&PathBuf>,
metadata: &VersionMetadata,
yarn_version: Option<String>,
) -> Vec<SectionItem> {
pub fn items(app_dir: Option<&PathBuf>, metadata: &VersionMetadata) -> Vec<SectionItem> {
let mut package_manager = PackageManager::Npm;
if let Some(app_dir) = &app_dir {
let app_dir_entries = std::fs::read_dir(app_dir)
@ -256,7 +252,7 @@ pub fn items(
}
if package_manager == PackageManager::Yarn
&& yarn_version
&& manager_version("yarn")
.map(|v| v.chars().next().map(|c| c > '1').unwrap_or_default())
.unwrap_or(false)
{
@ -270,46 +266,40 @@ pub fn items(
("@tauri-apps/cli", Some(metadata.js_cli.version.clone())),
] {
let app_dir = app_dir.clone();
let item = SectionItem::new(
move || {
let version = version.clone().unwrap_or_else(|| {
npm_package_version(&package_manager, package, &app_dir)
.unwrap_or_default()
.unwrap_or_default()
});
let latest_ver = npm_latest_version(&package_manager, package)
let item = SectionItem::new().action(move || {
let version = version.clone().unwrap_or_else(|| {
npm_package_version(&package_manager, package, &app_dir)
.unwrap_or_default()
.unwrap_or_default();
.unwrap_or_default()
});
let latest_ver = npm_latest_version(&package_manager, package)
.unwrap_or_default()
.unwrap_or_default();
Some((
if version.is_empty() {
format!("{} {}: not installed!", package, "[NPM]".dimmed())
if version.is_empty() {
format!("{} {}: not installed!", package, "".green())
} else {
format!(
"{} {}: {}{}",
package,
"[NPM]".dimmed(),
version,
if !(version.is_empty() || latest_ver.is_empty()) {
let version = semver::Version::parse(version.as_str()).unwrap();
let target_version = semver::Version::parse(latest_ver.as_str()).unwrap();
if version < target_version {
format!(" ({}, latest: {})", "outdated".yellow(), latest_ver.green())
} else {
"".into()
}
} else {
format!(
"{} {}: {}{}",
package,
"[NPM]".dimmed(),
version,
if !(version.is_empty() || latest_ver.is_empty()) {
let version = semver::Version::parse(version.as_str()).unwrap();
let target_version = semver::Version::parse(latest_ver.as_str()).unwrap();
if version < target_version {
format!(" ({}, latest: {})", "outdated".yellow(), latest_ver.green())
} else {
"".into()
}
} else {
"".into()
}
)
},
Status::Neutral,
))
},
|| None,
false,
);
"".into()
}
)
}
.into()
});
items.push(item);
}

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::{SectionItem, Status};
use super::{ActionResult, SectionItem};
use crate::interface::rust::get_workspace_dir;
use colored::Colorize;
use serde::Deserialize;
@ -212,96 +212,58 @@ pub fn items(app_dir: Option<&PathBuf>, tauri_dir: Option<PathBuf>) -> Vec<Secti
for dep in ["tauri", "tauri-build", "wry", "tao"] {
let (version_string, version_suffix) =
crate_version(&tauri_dir, manifest.as_ref(), lock.as_ref(), dep);
let dep = dep.to_string();
let item = SectionItem::new(
move || {
Some((
format!(
"{} {}: {}{}",
dep,
"[RUST]".dimmed(),
version_string,
version_suffix
.clone()
.map(|s| format!(",{s}"))
.unwrap_or_else(|| "".into())
),
Status::Neutral,
))
},
|| None,
false,
);
let item = SectionItem::new().description(format!(
"{} {}: {}{}",
dep,
"[RUST]".dimmed(),
version_string,
version_suffix
.clone()
.map(|s| format!(",{s}"))
.unwrap_or_else(|| "".into())
));
items.push(item);
}
}
}
if let Ok(rust_cli) = std::process::Command::new("cargo")
.arg("tauri")
.arg("-V")
.output()
{
if rust_cli.status.success() {
let stdout = String::from_utf8_lossy(rust_cli.stdout.as_slice()).to_string();
let mut output = stdout.split(' ');
let dep = output.next().unwrap_or_default().to_string();
let version_string = output
.next()
.unwrap_or_default()
.strip_suffix('\n')
.unwrap_or_default()
.to_string();
let tauri_cli_rust_item = SectionItem::new().action(|| {
std::process::Command::new("cargo")
.arg("tauri")
.arg("-V")
.output()
.ok()
.map(|o| {
if o.status.success() {
let out = String::from_utf8_lossy(o.stdout.as_slice());
let (package, version) = out.split_once(' ').unwrap_or_default();
let latest_ver = crate_latest_version(package).unwrap_or_default();
format!(
"{} {}: {}{}",
package,
"[RUST]".dimmed(),
version.split_once('\n').unwrap_or_default().0,
if !(version.is_empty() || latest_ver.is_empty()) {
let version = semver::Version::parse(version).unwrap();
let target_version = semver::Version::parse(latest_ver.as_str()).unwrap();
let version_suffix = match crate_latest_version(&dep) {
Some(target_version) => {
let version = semver::Version::parse(&version_string).unwrap();
let target_version = semver::Version::parse(&target_version).unwrap();
if version < target_version {
Some(format!(
" ({}, latest: {})",
"outdated".yellow(),
target_version.to_string().green()
))
} else {
None
}
if version < target_version {
format!(" ({}, latest: {})", "outdated".yellow(), latest_ver.green())
} else {
"".into()
}
} else {
"".into()
}
)
.into()
} else {
ActionResult::None
}
None => None,
};
items.push(SectionItem::new(
move || {
Some((
format!(
"{} {}: {}{}",
dep,
"[RUST]".dimmed(),
version_string,
version_suffix
.clone()
.map(|s| format!(", {s}"))
.unwrap_or_else(|| "".into())
),
Status::Neutral,
))
},
|| None,
false,
));
} else {
items.push(SectionItem::new(
move || {
Some((
format!("tauri-cli {}: not installed!", "[RUST]".dimmed()),
Status::Neutral,
))
},
|| None,
false,
));
}
}
})
.unwrap_or_default()
});
items.push(tauri_cli_rust_item);
items
}