workon 0.15.0

Development workspace launcher with Zellij, Claude CLI, and branchdiff
Documentation
use std::path::{Path, PathBuf};

use anyhow::{bail, Result};

#[derive(Debug)]
pub struct Project {
    pub dir: PathBuf,
    pub name: String,
}

pub fn resolve(arg: Option<&str>) -> Result<Project> {
    let dir = match arg {
        Some(input) => resolve_input(input)?,
        None => std::env::current_dir()?,
    };

    let name = dir
        .file_name()
        .map(|n| n.to_string_lossy().into_owned())
        .unwrap_or_else(|| "workon".into());

    Ok(Project { dir, name })
}

fn resolve_input(input: &str) -> Result<PathBuf> {
    let path = Path::new(input);
    if path.is_dir() {
        return Ok(path.canonicalize()?);
    }

    let workspace_path = home_dir()?.join("workspace").join(input);
    if workspace_path.is_dir() {
        return Ok(workspace_path.canonicalize()?);
    }

    bail!("directory not found: {input} (also checked ~/workspace/{input})")
}

fn home_dir() -> Result<PathBuf> {
    crate::home::home_dir()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn resolve_existing_directory() {
        let tmp = tempfile::tempdir().unwrap();
        let result = resolve(Some(tmp.path().to_str().unwrap())).unwrap();
        assert_eq!(result.dir, tmp.path().canonicalize().unwrap());
        assert_eq!(result.name, tmp.path().file_name().unwrap().to_str().unwrap());
    }

    #[test]
    fn resolve_nonexistent_errors() {
        let result = resolve(Some("/tmp/workon_test_nonexistent_xyz_999"));
        assert!(result.is_err());
        let err = result.unwrap_err().to_string();
        assert!(err.contains("directory not found"));
    }

    #[test]
    fn resolve_none_uses_cwd() {
        let result = resolve(None).unwrap();
        assert_eq!(result.dir, std::env::current_dir().unwrap());
    }
}