oh-my-todo 0.2.0

Local-first terminal task manager with a mouse-first TUI and CLI.
Documentation
use crate::domain::{SpaceId, Task, TaskId};
use directories::ProjectDirs;
use std::env;
use std::path::{Path, PathBuf};

use super::repository::StorageError;

const DATA_DIR_ENV: &str = "OH_MY_TODO_DATA_DIR";

#[derive(Debug, Clone)]
pub struct DataPaths {
    root: PathBuf,
}

impl DataPaths {
    pub fn resolve_default() -> Result<Self, StorageError> {
        if let Some(path) = env::var_os(DATA_DIR_ENV) {
            return Ok(Self::from_root(PathBuf::from(path)));
        }

        let project_dirs = ProjectDirs::from("", "", "oh-my-todo").ok_or_else(|| {
            StorageError::PathResolution("unable to determine OS data directory".to_owned())
        })?;

        Ok(Self::from_root(project_dirs.data_local_dir().to_path_buf()))
    }

    pub fn from_root(root: PathBuf) -> Self {
        Self { root }
    }

    pub fn root(&self) -> &Path {
        &self.root
    }

    pub fn config_dir(&self) -> PathBuf {
        self.root.join("config")
    }

    pub fn spaces_dir(&self) -> PathBuf {
        self.root.join("spaces")
    }

    pub fn config_file(&self) -> PathBuf {
        self.config_dir().join("config.ron")
    }

    pub fn state_file(&self) -> PathBuf {
        self.config_dir().join("state.ron")
    }

    pub fn space_dir(&self, space_id: &SpaceId) -> PathBuf {
        self.spaces_dir().join(space_id.as_str())
    }

    pub fn space_file(&self, space_id: &SpaceId) -> PathBuf {
        self.space_dir(space_id).join("space.ron")
    }

    pub fn space_todo_dir(&self, space_id: &SpaceId) -> PathBuf {
        self.space_dir(space_id).join("todo")
    }

    pub fn space_archive_dir(&self, space_id: &SpaceId) -> PathBuf {
        self.space_dir(space_id).join("archive")
    }

    pub fn task_path(&self, task: &Task) -> PathBuf {
        self.task_path_for_id(&task.space_id, &task.id, task.storage_bucket())
    }

    pub fn task_path_for_id(&self, space_id: &SpaceId, task_id: &TaskId, bucket: &str) -> PathBuf {
        let base = match bucket {
            "archive" => self.space_archive_dir(space_id),
            _ => self.space_todo_dir(space_id),
        };

        base.join(format!("{}.ron", task_id.as_str()))
    }
}