pub mod editors;
pub mod ui;
use std::collections::HashSet;
use std::path::Path;
use anyhow::Result;
use ratatui::DefaultTerminal;
use crate::vsix::download;
use crate::vsix::install::{self, Editor};
use editors::InstalledExtension;
pub const SOURCE: Editor = Editor::Code;
pub const TARGET: Editor = Editor::Cursor;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SyncCandidate {
pub id: String,
pub source_version: Option<String>,
pub installed_in_target: bool,
}
pub fn build_candidates(
source: &[InstalledExtension],
target: &[InstalledExtension],
) -> Vec<SyncCandidate> {
let target_ids: HashSet<String> = target.iter().map(|e| e.id.to_lowercase()).collect();
let mut seen: HashSet<String> = HashSet::new();
let mut candidates: Vec<SyncCandidate> = Vec::new();
for ext in source {
let key = ext.id.to_lowercase();
if !seen.insert(key.clone()) {
continue;
}
candidates.push(SyncCandidate {
id: ext.id.clone(),
source_version: ext.version.clone(),
installed_in_target: target_ids.contains(&key),
});
}
candidates.sort_by_key(|c| c.id.to_lowercase());
candidates
}
#[derive(Debug, Clone)]
pub struct SyncItemResult {
pub id: String,
pub success: bool,
pub message: String,
}
pub fn sync_extension(id: &str, target: Editor, tmp_dir: &Path) -> SyncItemResult {
match download::resolve_and_download_to(id, Some(tmp_dir)) {
Ok(downloaded) => {
let result = install::install(target, &downloaded.path);
SyncItemResult {
id: id.to_string(),
success: result.success,
message: result.message,
}
}
Err(err) => SyncItemResult {
id: id.to_string(),
success: false,
message: err.to_string(),
},
}
}
pub fn run() -> Result<()> {
let mut terminal = ratatui::init();
let result = ui::App::new().run(&mut terminal);
ratatui::restore();
result
}
pub fn run_in_terminal(terminal: &mut DefaultTerminal) -> Result<()> {
ui::App::new().run(terminal)
}
#[cfg(test)]
mod tests {
use super::*;
fn ext(id: &str) -> InstalledExtension {
InstalledExtension {
id: id.to_string(),
version: Some("1.0.0".to_string()),
}
}
#[test]
fn marks_target_installed_and_sorts() {
let source = vec![ext("b.two"), ext("A.one"), ext("b.two")];
let target = vec![ext("A.one")];
let candidates = build_candidates(&source, &target);
assert_eq!(candidates.len(), 2);
assert_eq!(candidates[0].id, "A.one");
assert!(candidates[0].installed_in_target);
assert_eq!(candidates[1].id, "b.two");
assert!(!candidates[1].installed_in_target);
}
}