use std::process::Command;
use anyhow::{anyhow, Result};
use crate::vsix::install::Editor;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InstalledExtension {
pub id: String,
pub version: Option<String>,
}
pub fn parse_extensions(stdout: &str) -> Vec<InstalledExtension> {
stdout
.lines()
.map(str::trim)
.filter(|line| !line.is_empty())
.map(|line| match line.rfind('@') {
Some(at) if at > 0 => InstalledExtension {
id: line[..at].to_string(),
version: Some(line[at + 1..].to_string()),
},
_ => InstalledExtension {
id: line.to_string(),
version: None,
},
})
.collect()
}
pub fn list_installed(editor: Editor) -> Result<Vec<InstalledExtension>> {
let output = Command::new(editor.bin())
.args(["--list-extensions", "--show-versions"])
.output()
.map_err(|err| anyhow!("无法执行 {}:{err}", editor.bin()))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
let detail = {
let stderr = stderr.trim();
if stderr.is_empty() {
stdout.trim()
} else {
stderr
}
};
return Err(anyhow!(
"{} --list-extensions 执行失败:{}",
editor.label(),
if detail.is_empty() {
"未知错误"
} else {
detail
}
));
}
Ok(parse_extensions(&String::from_utf8_lossy(&output.stdout)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_id_and_version() {
let exts = parse_extensions("ms-python.python@2024.1.0\nesbenp.prettier-vscode@10.4.0\n");
assert_eq!(exts.len(), 2);
assert_eq!(exts[0].id, "ms-python.python");
assert_eq!(exts[0].version.as_deref(), Some("2024.1.0"));
}
#[test]
fn tolerates_missing_version_and_blank_lines() {
let exts = parse_extensions(" \nfoo.bar\n\n");
assert_eq!(exts.len(), 1);
assert_eq!(exts[0].id, "foo.bar");
assert_eq!(exts[0].version, None);
}
}