use crate::core::types;
use std::path::{Path, PathBuf};
pub(crate) fn cmd_workspace_new(name: &str) -> Result<(), String> {
workspace_new_in(Path::new("."), Path::new("state"), name)
}
pub(crate) fn cmd_workspace_list() -> Result<(), String> {
workspace_list_in(Path::new("."), Path::new("state"))
}
pub(crate) fn cmd_workspace_select(name: &str) -> Result<(), String> {
workspace_select_in(Path::new("."), Path::new("state"), name)
}
pub(crate) fn cmd_workspace_delete(name: &str, yes: bool) -> Result<(), String> {
workspace_delete_in(Path::new("."), Path::new("state"), name, yes)
}
pub(crate) fn cmd_workspace_current() -> Result<(), String> {
match current_workspace() {
Some(ws) => println!("{ws}"),
None => println!("(default — no workspace selected)"),
}
Ok(())
}
pub(crate) fn workspace_new_in(root: &Path, state_base: &Path, name: &str) -> Result<(), String> {
let meta = root.join(".forjar");
std::fs::create_dir_all(&meta).map_err(|e| format!("cannot create workspace metadata: {e}"))?;
let ws_dir = state_base.join(name);
if ws_dir.exists() {
return Err(format!("workspace '{name}' already exists"));
}
std::fs::create_dir_all(&ws_dir)
.map_err(|e| format!("cannot create workspace dir {}: {}", ws_dir.display(), e))?;
std::fs::write(meta.join("workspace"), name)
.map_err(|e| format!("cannot write workspace file: {e}"))?;
println!("Created and selected workspace '{name}'");
Ok(())
}
pub(crate) fn workspace_list_in(root: &Path, state_base: &Path) -> Result<(), String> {
let active = current_workspace_in(root);
if !state_base.exists() {
println!("No workspaces (state/ does not exist)");
return Ok(());
}
let mut found = false;
let entries =
std::fs::read_dir(state_base).map_err(|e| format!("cannot read state dir: {e}"))?;
for entry in entries.flatten() {
if entry.path().is_dir() {
let name = entry.file_name().to_string_lossy().to_string();
let marker = if active.as_deref() == Some(&name) {
" *"
} else {
""
};
println!(" {name}{marker}");
found = true;
}
}
if !found {
println!("No workspaces found");
}
Ok(())
}
pub(crate) fn workspace_select_in(
root: &Path,
state_base: &Path,
name: &str,
) -> Result<(), String> {
let ws_dir = state_base.join(name);
if !ws_dir.exists() {
return Err(format!(
"workspace '{name}' does not exist (no state/{name}/)"
));
}
let meta = root.join(".forjar");
std::fs::create_dir_all(&meta).map_err(|e| format!("cannot create workspace metadata: {e}"))?;
std::fs::write(meta.join("workspace"), name)
.map_err(|e| format!("cannot write workspace file: {e}"))?;
println!("Selected workspace '{name}'");
Ok(())
}
pub(crate) fn workspace_delete_in(
root: &Path,
state_base: &Path,
name: &str,
yes: bool,
) -> Result<(), String> {
let ws_dir = state_base.join(name);
if !ws_dir.exists() {
return Err(format!("workspace '{name}' does not exist"));
}
if !yes {
println!("This will delete workspace '{name}' and all its state. Use --yes to confirm.");
return Ok(());
}
std::fs::remove_dir_all(&ws_dir).map_err(|e| format!("cannot delete workspace dir: {e}"))?;
if current_workspace_in(root).as_deref() == Some(name) {
let _ = std::fs::remove_file(root.join(".forjar").join("workspace"));
}
println!("Deleted workspace '{name}'");
Ok(())
}
pub(crate) fn current_workspace_in(root: &Path) -> Option<String> {
std::fs::read_to_string(root.join(".forjar").join("workspace"))
.ok()
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
}
pub(crate) fn current_workspace() -> Option<String> {
current_workspace_in(Path::new("."))
}
pub(crate) fn resolve_state_dir(state_dir: &Path, workspace_flag: Option<&str>) -> PathBuf {
if let Some(ws) = workspace_flag {
return state_dir.join(ws);
}
if let Some(ws) = current_workspace() {
return state_dir.join(ws);
}
state_dir.to_path_buf()
}
pub(crate) fn inject_workspace_param(
config: &mut types::ForjarConfig,
workspace_flag: Option<&str>,
) {
let ws = workspace_flag
.map(|s| s.to_string())
.or_else(current_workspace)
.unwrap_or_else(|| "default".to_string());
config
.params
.insert("workspace".to_string(), serde_yaml_ng::Value::String(ws));
}