use crate::prelude::*;
pub(crate) const FLAC_VERSION_PATTERN: &str = r"flac (\d+\.\d+(?:\.\d+)?)";
pub(crate) const LAME_VERSION_PATTERN: &str = r"version (\d+\.\d+)";
pub(crate) const SOX_VERSION_PATTERN: &str = r"v(\d+\.\d+\.\d+(?:\.\d+)?)";
#[injectable]
pub struct VersionCommand {
sox: Ref<SoxFactory>,
}
impl VersionCommand {
pub async fn execute(&self) -> bool {
let sox_binary = self.sox.binary();
let flac = get_version(FLAC, FLAC_VERSION_PATTERN).await;
let lame = get_version(LAME, LAME_VERSION_PATTERN).await;
let sox = get_version(sox_binary, SOX_VERSION_PATTERN).await;
let dependencies = [(FLAC, flac), (LAME, lame), (sox_binary, sox)];
let any_error = dependencies.iter().any(|(_, result)| result.is_err());
if any_error {
error!("Failed to find all dependencies\n");
}
let table = build_table(dependencies);
print!("{table}");
GitHubRelease::check_for_update().await;
!any_error
}
}
pub(super) async fn get_version(binary: &str, pattern: &str) -> Result<VersionInfo, VersionError> {
let output = TokioCommand::new(binary)
.arg("--version")
.output()
.await
.map_err(VersionError::Process)?;
let stdout = String::from_utf8_lossy(&output.stdout);
let first_line = stdout
.lines()
.next()
.ok_or(VersionError::EmptyStdout)?
.to_owned();
let version = Regex::new(pattern)
.expect("version pattern should be valid")
.captures(&first_line)
.and_then(|caps| caps.get(1))
.map(|m| m.as_str().to_owned());
Ok(VersionInfo {
first_line,
version,
})
}
fn build_table(dependencies: [(&str, Result<VersionInfo, VersionError>); 3]) -> String {
let mut builder = TableBuilder::new().row([
APP_NAME.to_owned(),
app_version_or_describe().trim_start_matches('v').to_owned(),
app_user_agent(false).dimmed().to_string(),
]);
for (name, result) in dependencies {
let (version, detail) = match result {
Ok(info) => (
info.version.unwrap_or_else(|| String::from("?")),
info.first_line.dimmed().to_string(),
),
Err(e) => ("⚠".yellow().to_string(), e.to_string().yellow().to_string()),
};
builder = builder.row([name.to_owned(), version, detail]);
}
builder.build()
}
pub(super) struct VersionInfo {
pub(super) first_line: String,
pub(super) version: Option<String>,
}
#[derive(Debug, ThisError)]
pub(super) enum VersionError {
#[error("Unable to get version information")]
EmptyStdout,
#[error("Unable to run dependency: {0}")]
Process(IoError),
}