fix(cli): accept workspace name or UUID, auto-select on first add
Three related CLI bugs found during smoke testing: 1. `get_repository` used `config.get_workspace(name)` which expects the UUID string, so `onyx list create -w dev` or `onyx task add -w dev` always failed with "Workspace 'dev' not found". Unified CLI resolution into a single `resolve_workspace()` helper that accepts either the display name or the UUID; removed sync.rs's duplicated local copy. 2. `workspace switch`/`remove`/`retarget`/`migrate` only accepted the display name — the error message even suggested "Use the workspace ID instead" on ambiguous names, but IDs were then rejected. Updated `resolve_name` to try the map key first. 3. `onyx workspace add` never set `current_workspace`, so the very next command failed with "No workspace set. Use 'onyx init'..." even though a workspace was just created. Now sets the new workspace as current whenever none was previously selected, and reports the fact. Updated the error message to point at the correct `workspace add` / `workspace switch` commands instead of `init`.
This commit is contained in:
parent
855fa46a0e
commit
433a950418
|
|
@ -6,6 +6,7 @@ pub mod group;
|
|||
pub mod sync;
|
||||
|
||||
use onyx_core::{AppConfig, TaskRepository};
|
||||
use onyx_core::config::WorkspaceConfig;
|
||||
use anyhow::{Context, Result};
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
|
@ -23,18 +24,29 @@ pub fn save_config(config: &AppConfig) -> Result<()> {
|
|||
config.save_to_file(&path).context("Failed to save config")
|
||||
}
|
||||
|
||||
pub fn get_repository(workspace_name: Option<String>) -> Result<(TaskRepository, String)> {
|
||||
let config = load_config()?;
|
||||
|
||||
let (name, workspace_config) = if let Some(name) = workspace_name {
|
||||
let workspace_config = config.get_workspace(&name)
|
||||
.ok_or_else(|| anyhow::anyhow!("Workspace '{}' not found", name))?;
|
||||
(name, workspace_config.clone())
|
||||
/// Resolve a user-supplied identifier to (id, WorkspaceConfig). Accepts either
|
||||
/// the workspace's display name or its UUID. Falls back to the current
|
||||
/// workspace when `identifier` is `None`.
|
||||
pub fn resolve_workspace(config: &AppConfig, identifier: Option<&str>) -> Result<(String, WorkspaceConfig)> {
|
||||
if let Some(s) = identifier {
|
||||
// Try by UUID first (exact match on map key), then fall back to name lookup.
|
||||
if let Some(ws) = config.get_workspace(s) {
|
||||
return Ok((s.to_string(), ws.clone()));
|
||||
}
|
||||
let (id, ws) = config.find_by_name(s)
|
||||
.ok_or_else(|| anyhow::anyhow!("Workspace '{}' not found", s))?;
|
||||
Ok((id.clone(), ws.clone()))
|
||||
} else {
|
||||
let (name, workspace_config) = config.get_current_workspace()
|
||||
.context("No workspace set. Use 'onyx init' to create one.")?;
|
||||
(name.clone(), workspace_config.clone())
|
||||
};
|
||||
let (id, ws) = config.get_current_workspace()
|
||||
.context("No workspace set. Run 'onyx workspace add <name> <path>' to create one, or 'onyx workspace switch <name>' to select one.")?;
|
||||
Ok((id.clone(), ws.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_repository(workspace_identifier: Option<String>) -> Result<(TaskRepository, String)> {
|
||||
let config = load_config()?;
|
||||
let (_id, workspace_config) = resolve_workspace(&config, workspace_identifier.as_deref())?;
|
||||
let name = workspace_config.name.clone();
|
||||
|
||||
let repo = TaskRepository::new(workspace_config.path.clone())
|
||||
.context(format!("Failed to open workspace '{}'", name))?;
|
||||
|
|
|
|||
|
|
@ -2,22 +2,8 @@ use anyhow::{Context, Result};
|
|||
use colored::Colorize;
|
||||
use onyx_core::sync::{SyncMode, sync_workspace, get_sync_status};
|
||||
use onyx_core::webdav::{WebDavClient, store_credentials, load_credentials};
|
||||
use onyx_core::config::AppConfig;
|
||||
use crate::output;
|
||||
use super::{load_config, save_config};
|
||||
|
||||
/// Resolve a workspace name to (id, config). Falls back to current workspace if name is None.
|
||||
fn resolve_workspace(config: &AppConfig, name: Option<&str>) -> Result<(String, onyx_core::config::WorkspaceConfig)> {
|
||||
if let Some(name) = name {
|
||||
let (id, ws) = config.find_by_name(name)
|
||||
.ok_or_else(|| anyhow::anyhow!("Workspace '{}' not found", name))?;
|
||||
Ok((id.clone(), ws.clone()))
|
||||
} else {
|
||||
let (id, ws) = config.get_current_workspace()
|
||||
.context("No workspace set. Use 'onyx init' to create one.")?;
|
||||
Ok((id.clone(), ws.clone()))
|
||||
}
|
||||
}
|
||||
use super::{load_config, save_config, resolve_workspace};
|
||||
|
||||
/// Run sync setup: prompt for URL, username, password, test connection, store credentials.
|
||||
pub fn setup(workspace_name: Option<String>) -> Result<()> {
|
||||
|
|
|
|||
|
|
@ -30,11 +30,21 @@ pub fn add(name: String, path: String) -> Result<()> {
|
|||
// Add workspace
|
||||
let id = config.add_workspace(WorkspaceConfig::new(name.clone(), path_buf.clone()));
|
||||
|
||||
// Select the new workspace as current when none was previously set, so the
|
||||
// very next command doesn't fail with "No workspace set".
|
||||
let made_current = config.current_workspace.is_none();
|
||||
if made_current {
|
||||
config.set_current_workspace(id.clone())?;
|
||||
}
|
||||
|
||||
// Save config
|
||||
save_config(&config)?;
|
||||
|
||||
output::success(&format!("Added workspace \"{}\" ({}) at {}", name, &id[..8], path_buf.display()));
|
||||
output::success("Created default list \"My Tasks\"");
|
||||
if made_current {
|
||||
output::success(&format!("Set \"{}\" as the current workspace", name));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -64,15 +74,20 @@ pub fn list() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolve a workspace name to its ID. Errors if not found or ambiguous.
|
||||
fn resolve_name(config: &onyx_core::config::AppConfig, name: &str) -> Result<String> {
|
||||
/// Resolve a user-supplied identifier to a workspace ID. Accepts either the
|
||||
/// display name or the UUID. Errors if not found or ambiguous.
|
||||
fn resolve_name(config: &onyx_core::config::AppConfig, identifier: &str) -> Result<String> {
|
||||
// Direct UUID hit on the map key — unambiguous.
|
||||
if config.workspaces.contains_key(identifier) {
|
||||
return Ok(identifier.to_string());
|
||||
}
|
||||
let matches: Vec<_> = config.workspaces.iter()
|
||||
.filter(|(_, ws)| ws.name == name)
|
||||
.filter(|(_, ws)| ws.name == identifier)
|
||||
.collect();
|
||||
match matches.len() {
|
||||
0 => anyhow::bail!("Workspace '{}' not found", name),
|
||||
0 => anyhow::bail!("Workspace '{}' not found", identifier),
|
||||
1 => Ok(matches[0].0.clone()),
|
||||
n => anyhow::bail!("Ambiguous: {} workspaces named '{}'. Use the workspace ID instead.", n, name),
|
||||
n => anyhow::bail!("Ambiguous: {} workspaces named '{}'. Use the workspace ID instead.", n, identifier),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue