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;
|
pub mod sync;
|
||||||
|
|
||||||
use onyx_core::{AppConfig, TaskRepository};
|
use onyx_core::{AppConfig, TaskRepository};
|
||||||
|
use onyx_core::config::WorkspaceConfig;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use std::path::PathBuf;
|
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")
|
config.save_to_file(&path).context("Failed to save config")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_repository(workspace_name: Option<String>) -> Result<(TaskRepository, String)> {
|
/// Resolve a user-supplied identifier to (id, WorkspaceConfig). Accepts either
|
||||||
let config = load_config()?;
|
/// the workspace's display name or its UUID. Falls back to the current
|
||||||
|
/// workspace when `identifier` is `None`.
|
||||||
let (name, workspace_config) = if let Some(name) = workspace_name {
|
pub fn resolve_workspace(config: &AppConfig, identifier: Option<&str>) -> Result<(String, WorkspaceConfig)> {
|
||||||
let workspace_config = config.get_workspace(&name)
|
if let Some(s) = identifier {
|
||||||
.ok_or_else(|| anyhow::anyhow!("Workspace '{}' not found", name))?;
|
// Try by UUID first (exact match on map key), then fall back to name lookup.
|
||||||
(name, workspace_config.clone())
|
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 {
|
} else {
|
||||||
let (name, workspace_config) = config.get_current_workspace()
|
let (id, ws) = config.get_current_workspace()
|
||||||
.context("No workspace set. Use 'onyx init' to create one.")?;
|
.context("No workspace set. Run 'onyx workspace add <name> <path>' to create one, or 'onyx workspace switch <name>' to select one.")?;
|
||||||
(name.clone(), workspace_config.clone())
|
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())
|
let repo = TaskRepository::new(workspace_config.path.clone())
|
||||||
.context(format!("Failed to open workspace '{}'", name))?;
|
.context(format!("Failed to open workspace '{}'", name))?;
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,8 @@ use anyhow::{Context, Result};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use onyx_core::sync::{SyncMode, sync_workspace, get_sync_status};
|
use onyx_core::sync::{SyncMode, sync_workspace, get_sync_status};
|
||||||
use onyx_core::webdav::{WebDavClient, store_credentials, load_credentials};
|
use onyx_core::webdav::{WebDavClient, store_credentials, load_credentials};
|
||||||
use onyx_core::config::AppConfig;
|
|
||||||
use crate::output;
|
use crate::output;
|
||||||
use super::{load_config, save_config};
|
use super::{load_config, save_config, resolve_workspace};
|
||||||
|
|
||||||
/// 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()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run sync setup: prompt for URL, username, password, test connection, store credentials.
|
/// Run sync setup: prompt for URL, username, password, test connection, store credentials.
|
||||||
pub fn setup(workspace_name: Option<String>) -> Result<()> {
|
pub fn setup(workspace_name: Option<String>) -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,21 @@ pub fn add(name: String, path: String) -> Result<()> {
|
||||||
// Add workspace
|
// Add workspace
|
||||||
let id = config.add_workspace(WorkspaceConfig::new(name.clone(), path_buf.clone()));
|
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
|
||||||
save_config(&config)?;
|
save_config(&config)?;
|
||||||
|
|
||||||
output::success(&format!("Added workspace \"{}\" ({}) at {}", name, &id[..8], path_buf.display()));
|
output::success(&format!("Added workspace \"{}\" ({}) at {}", name, &id[..8], path_buf.display()));
|
||||||
output::success("Created default list \"My Tasks\"");
|
output::success("Created default list \"My Tasks\"");
|
||||||
|
if made_current {
|
||||||
|
output::success(&format!("Set \"{}\" as the current workspace", name));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -64,15 +74,20 @@ pub fn list() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve a workspace name to its ID. Errors if not found or ambiguous.
|
/// Resolve a user-supplied identifier to a workspace ID. Accepts either the
|
||||||
fn resolve_name(config: &onyx_core::config::AppConfig, name: &str) -> Result<String> {
|
/// 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()
|
let matches: Vec<_> = config.workspaces.iter()
|
||||||
.filter(|(_, ws)| ws.name == name)
|
.filter(|(_, ws)| ws.name == identifier)
|
||||||
.collect();
|
.collect();
|
||||||
match matches.len() {
|
match matches.len() {
|
||||||
0 => anyhow::bail!("Workspace '{}' not found", name),
|
0 => anyhow::bail!("Workspace '{}' not found", identifier),
|
||||||
1 => Ok(matches[0].0.clone()),
|
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