aboutsummaryrefslogtreecommitdiff
path: root/actions/src/remote_actions/sheet_manage/drop_sheet.rs
blob: 0dcd79039cc390e58d66bd9ff2c5fdcaf0c881be (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use std::io::ErrorKind;

use action_system::{action::ActionContext, macros::action_gen};
use serde::{Deserialize, Serialize};
use tcp_connection::error::TcpTargetError;
use vcs_data::data::{local::vault_modified::sign_vault_modified, sheet::SheetName};

use crate::{
    remote_actions::{
        auth_member, check_connection_instance, try_get_local_workspace, try_get_vault,
    },
    write_and_return,
};

#[derive(Default, Serialize, Deserialize)]
pub enum DropSheetActionResult {
    Success,

    // Fail
    SheetInUse,
    AuthorizeFailed(String),
    SheetNotExists,
    SheetDropFailed(String),
    NoHolder,
    NotOwner,

    #[default]
    Unknown,
}

#[action_gen]
pub async fn drop_sheet_action(
    ctx: ActionContext,
    sheet_name: SheetName,
) -> Result<DropSheetActionResult, TcpTargetError> {
    let instance = check_connection_instance(&ctx)?;

    // Auth Member
    let (member_id, is_host_mode) = match auth_member(&ctx, instance).await {
        Ok(id) => id,
        Err(e) => {
            return Ok(DropSheetActionResult::AuthorizeFailed(e.to_string()));
        }
    };

    // Check sheet in use on local
    if ctx.is_proc_on_local() {
        let local_workspace = try_get_local_workspace(&ctx)?;
        if let Some(sheet) = local_workspace.config().lock().await.sheet_in_use() {
            if sheet == &sheet_name {
                instance.lock().await.write(false).await?;
                return Ok(DropSheetActionResult::SheetInUse);
            }
            instance.lock().await.write(true).await?;
        } else {
            instance.lock().await.write(true).await?;
        }
    }

    if ctx.is_proc_on_remote() {
        // Check if client sheet is in use
        let sheet_in_use = instance.lock().await.read::<bool>().await?;
        if !sheet_in_use {
            return Ok(DropSheetActionResult::SheetInUse);
        }

        let vault = try_get_vault(&ctx)?;

        // Check if the sheet exists
        let mut sheet = match vault.sheet(&sheet_name).await {
            Ok(sheet) => sheet,
            Err(e) => {
                if e.kind() == ErrorKind::NotFound {
                    write_and_return!(instance, DropSheetActionResult::SheetNotExists);
                } else {
                    write_and_return!(
                        instance,
                        DropSheetActionResult::SheetDropFailed(e.to_string())
                    );
                }
            }
        };

        // Get the sheet's holder
        let Some(holder) = sheet.holder() else {
            write_and_return!(instance, DropSheetActionResult::NoHolder);
        };

        // Verify that the sheet holder is either the current user or the host
        // All sheets belong to the host
        if holder != &member_id && !is_host_mode {
            write_and_return!(instance, DropSheetActionResult::NotOwner);
        }

        // Drop the sheet
        sheet.forget_holder();
        match sheet.persist().await {
            Ok(_) => {
                write_and_return!(instance, DropSheetActionResult::Success);
            }
            Err(e) => {
                write_and_return!(
                    instance,
                    DropSheetActionResult::SheetDropFailed(e.to_string())
                );
            }
        }
    }

    if ctx.is_proc_on_local() {
        let result = instance
            .lock()
            .await
            .read::<DropSheetActionResult>()
            .await?;
        if matches!(result, DropSheetActionResult::Success) {
            sign_vault_modified(true).await;
        }
        return Ok(result);
    }

    Err(TcpTargetError::NoResult("No result.".to_string()))
}