aven 0.1.13

Local-first task manager CLI and sync server
Documentation
use anyhow::Result;

use crate::operations::rename_config_project_mapping;
use crate::projects::{inferred_project_key_for_add_with_database, project_has_config_mapping};
use crate::tui::store::{MutationMessage, TaskScope};
use crate::undo::UndoCommand;

use super::TuiStore;

impl TuiStore {
    pub(crate) async fn create_project(&mut self, name: String) -> Result<String> {
        let name = name.trim().to_string();
        let outcome = self
            .database
            .create_project(&self.active_workspace, &name)
            .await?;
        let commands = if outcome.created {
            vec![UndoCommand::DeleteCreatedProject {
                project_key: outcome.project.key.clone(),
                create_change_id: outcome.change_id.unwrap_or_default(),
                expected_name: outcome.project.name.clone(),
                expected_prefix: outcome.project.prefix.clone(),
            }]
        } else {
            Vec::new()
        };
        self.record_undo_commands(&format!("project {}", outcome.project.key), commands)
            .await?;
        self.refresh(None).await?;
        Ok(format!("created project {}", outcome.project.key))
    }

    pub(crate) async fn delete_project(&mut self, project: &str) -> Result<MutationMessage> {
        let project = self
            .database
            .resolve_existing_project(&self.active_workspace.id, project)
            .await?;
        let config_mapping = project_has_config_mapping(
            &self.active_workspace.id,
            &self.active_workspace.key,
            &project.key,
        )
        .unwrap_or(false);
        let outcome = self
            .database
            .delete_project(&self.active_workspace, &project.key)
            .await?;

        if self.scope_project() == Some(outcome.project.key.as_str()) {
            self.view_state.scope = TaskScope::Workspace;
        }
        let selected = self.refresh(None).await?;
        let mut message = format!("deleted project {}", outcome.project.key);
        if config_mapping {
            message.push_str("; config path mappings were left unchanged");
        }
        Ok(MutationMessage::new(message, selected))
    }

    pub(crate) async fn rename_project(
        &mut self,
        project: &str,
        new_name: String,
    ) -> Result<MutationMessage> {
        let outcome = self
            .database
            .rename_project(&self.active_workspace, project, &new_name, None)
            .await?;
        let config_mapping = if outcome.changed {
            rename_config_project_mapping(
                &self.active_workspace,
                &outcome.previous.key,
                &outcome.project.key,
            )?
        } else {
            project_has_config_mapping(
                &self.active_workspace.id,
                &self.active_workspace.key,
                &outcome.previous.key,
            )
            .unwrap_or(false)
        };
        if outcome.changed {
            self.record_undo_commands(
                &format!("project {}", outcome.project.key),
                vec![UndoCommand::SetProjectMetadata {
                    project_id: outcome.project.id.clone(),
                    before_key: outcome.previous.key.clone(),
                    before_name: outcome.previous.name.clone(),
                    before_prefix: outcome.previous.prefix.clone(),
                    after_key: outcome.project.key.clone(),
                    after_name: outcome.project.name.clone(),
                    after_prefix: outcome.project.prefix.clone(),
                }],
            )
            .await?;
        }
        if self.scope_project() == Some(outcome.previous.key.as_str()) {
            self.view_state.scope = TaskScope::Project(outcome.project.key.clone());
        }
        let selected = self.refresh(None).await?;
        let mut message = format!(
            "renamed project {} prefix={}",
            outcome.project.key, outcome.project.prefix
        );
        if !outcome.changed {
            message = format!("renamed project {} changed=none", outcome.project.key);
        } else if config_mapping {
            message.push_str("; updated config path mappings");
        }
        Ok(MutationMessage::new(message, selected))
    }

    pub(crate) async fn create_label(&mut self, name: String) -> Result<String> {
        let name = name.trim().to_string();
        let outcome = self
            .database
            .create_label(&self.active_workspace, &name)
            .await?;
        let commands = if outcome.created {
            vec![UndoCommand::DeleteCreatedLabel {
                label: outcome.name.clone(),
                create_change_id: outcome.change_id.unwrap_or_default(),
            }]
        } else {
            Vec::new()
        };
        self.record_undo_commands(&format!("label {}", outcome.name), commands)
            .await?;
        self.labels = self
            .database
            .list_labels(&self.active_workspace.id, None)
            .await?;
        Ok(format!("created label {}", outcome.name))
    }

    pub(crate) async fn inferred_add_project(&self) -> Result<Option<String>> {
        inferred_project_key_for_add_with_database(&self.database, &self.active_workspace).await
    }
}