use std::path::PathBuf;
use anyhow::{anyhow, bail, Result};
use clap::Args;
use nova_diagnostics::FileDb;
use nova_pm::{Lock, Unlock, LOCKFILE};
use crate::project::{self, Mode};
#[derive(Args)]
pub struct UpdateCmd {
name: Option<String>,
}
fn project() -> Result<(Mode, PathBuf)> {
let mode = project::mode(None)?;
let Mode::Project { root, .. } = &mode else {
bail!("no nova.toml here or in any directory above");
};
let root = root.clone();
Ok((mode, root))
}
pub fn fetch() -> Result<()> {
let (mode, _) = project()?;
let synced = project::sync_with(&mode, Unlock::Nothing)?
.ok_or_else(|| anyhow!("no nova.toml here or in any directory above"))?;
match synced.unpacked.len() {
0 => println!("nothing to fetch"),
n => println!("fetched {n} package{}", if n == 1 { "" } else { "s" }),
}
Ok(())
}
pub fn update(cmd: UpdateCmd) -> Result<()> {
let (mode, root) = project()?;
let unlock = match cmd.name {
None => Unlock::All,
Some(name) => {
match std::fs::read_to_string(root.join(LOCKFILE)) {
Err(_) => bail!("`{name}` is not in nova.lock"),
Ok(text) => {
let mut db = FileDb::new();
let file = db.add(LOCKFILE, text.as_str());
if let Ok(lock) = nova_pm::parse_lock(&text, file) {
if lock.find(&name).is_none() {
bail!("`{name}` is not in nova.lock");
}
}
}
}
Unlock::One(name)
}
};
let synced = project::sync_with(&mode, unlock)?
.ok_or_else(|| anyhow!("no nova.toml here or in any directory above"))?;
let changes = changes(synced.before.as_ref(), synced.lock.as_ref());
if changes.is_empty() {
println!("nothing to update");
}
for change in changes {
println!("{change}");
}
Ok(())
}
fn changes(before: Option<&Lock>, after: Option<&Lock>) -> Vec<String> {
let none = Vec::new();
let old = before.map_or(&none, |lock| &lock.packages);
let new = after.map_or(&none, |lock| &lock.packages);
let mut lines = Vec::new();
for package in new {
match old.iter().find(|p| p.name == package.name) {
Some(was) if was.version != package.version => lines.push(format!(
"{} {} -> {}",
package.name, was.version, package.version
)),
Some(_) => {}
None => lines.push(format!("+ {} {}", package.name, package.version)),
}
}
for package in old {
if !new.iter().any(|p| p.name == package.name) {
lines.push(format!("- {} {}", package.name, package.version));
}
}
lines
}