use crate::config::{Config, Project};
use crate::lock::Lock;
use anyhow::{bail, Result};
use std::path::{Path, PathBuf};
pub struct Workspace {
pub root: PathBuf,
pub config: Config,
#[allow(dead_code)]
pub config_path: PathBuf,
pub lock_path: PathBuf,
}
impl Workspace {
pub fn discover() -> Result<Workspace> {
let cwd = std::env::current_dir()?;
Self::discover_from(&cwd)
}
pub fn discover_from(start: &Path) -> Result<Workspace> {
let config_path = Config::discover(start).ok_or_else(|| {
anyhow::anyhow!(
"no {} found (run `rv adopt` or `rv init`)",
crate::config::CONFIG_FILE
)
})?;
let root = config_path.parent().unwrap().to_path_buf();
let config = Config::load(&config_path)?;
let lock_path = root.join(crate::lock::LOCK_FILE);
Ok(Workspace {
root,
config,
config_path,
lock_path,
})
}
pub fn lock(&self) -> Result<Lock> {
Lock::load_or_default(&self.lock_path)
}
pub fn project_dir(&self, p: &Project) -> PathBuf {
if p.path == "." {
self.root.clone()
} else {
self.root.join(&p.path)
}
}
pub fn resolve_targets<'a>(&'a self, targets: &[String]) -> Result<Vec<&'a Project>> {
if targets.is_empty() || targets == ["all"] {
return Ok(self.config.projects.iter().collect());
}
let mut out = Vec::new();
for t in targets {
let matches: Vec<&Project> = self
.config
.projects
.iter()
.filter(|p| {
&p.path == t || &p.name == t || p.name.rsplit('/').next() == Some(t.as_str())
})
.collect();
match matches.as_slice() {
[p] => out.push(*p),
[] => bail!("unknown target `{t}`"),
_ => bail!("ambiguous target `{t}`; use a full path or owner/repo name"),
}
}
Ok(out)
}
}