summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2025-11-03 18:48:52 +0800
committer魏曹先生 <1992414357@qq.com>2025-11-03 18:48:52 +0800
commit5416c501e61e591cea85c1f30daa53818baa5f23 (patch)
tree4d7d156404da206b064231890a1e4862dd911f92
parentffb5805291343ba1cd4bb4f38788d9ce6e3e2ba6 (diff)
feat: Add sheet creation action
- Implement make_sheet_action for creating sheets - Add sheet-related constants - Update sheet data structures
-rw-r--r--crates/vcs_actions/src/actions/sheet_actions.rs79
-rw-r--r--crates/vcs_data/src/constants.rs4
-rw-r--r--crates/vcs_data/src/data/sheet.rs39
3 files changed, 110 insertions, 12 deletions
diff --git a/crates/vcs_actions/src/actions/sheet_actions.rs b/crates/vcs_actions/src/actions/sheet_actions.rs
index e69de29..b6ea51d 100644
--- a/crates/vcs_actions/src/actions/sheet_actions.rs
+++ b/crates/vcs_actions/src/actions/sheet_actions.rs
@@ -0,0 +1,79 @@
+use action_system::{action::ActionContext, macros::action_gen};
+use serde::{Deserialize, Serialize};
+use tcp_connection::error::TcpTargetError;
+use vcs_data::data::sheet::SheetName;
+
+use crate::actions::{auth_member, check_connection_instance, try_get_vault};
+
+#[derive(Default, Serialize, Deserialize)]
+pub enum MakeSheetActionResult {
+ Success,
+
+ // Fail
+ AuthorizeFailed(String),
+ SheetAlreadyExists,
+ SheetCreationFailed(String),
+
+ #[default]
+ Unknown,
+}
+
+/// Build a sheet with context
+#[action_gen]
+pub async fn make_sheet_action(
+ ctx: ActionContext,
+ sheet_name: SheetName,
+) -> Result<MakeSheetActionResult, TcpTargetError> {
+ let instance = check_connection_instance(&ctx)?;
+
+ // Auth Member
+ let member_id = match auth_member(&ctx, instance).await {
+ Ok(id) => id,
+ Err(e) => return Ok(MakeSheetActionResult::AuthorizeFailed(e.to_string())),
+ };
+
+ if ctx.is_proc_on_remote() {
+ let vault = try_get_vault(&ctx)?;
+
+ // Check if the sheet already exists
+ if vault.sheet(&sheet_name).await.is_ok() {
+ instance
+ .lock()
+ .await
+ .write(MakeSheetActionResult::SheetAlreadyExists)
+ .await?;
+ return Ok(MakeSheetActionResult::SheetAlreadyExists);
+ } else {
+ // Create the sheet
+ match vault.create_sheet(&sheet_name, &member_id).await {
+ Ok(_) => {
+ instance
+ .lock()
+ .await
+ .write(MakeSheetActionResult::Success)
+ .await?;
+ return Ok(MakeSheetActionResult::Success);
+ }
+ Err(e) => {
+ instance
+ .lock()
+ .await
+ .write(MakeSheetActionResult::SheetCreationFailed(e.to_string()))
+ .await?;
+ return Ok(MakeSheetActionResult::SheetCreationFailed(e.to_string()));
+ }
+ }
+ }
+ }
+
+ if ctx.is_proc_on_local() {
+ let result = instance
+ .lock()
+ .await
+ .read::<MakeSheetActionResult>()
+ .await?;
+ return Ok(result);
+ }
+
+ Err(TcpTargetError::NoResult("No result.".to_string()))
+}
diff --git a/crates/vcs_data/src/constants.rs b/crates/vcs_data/src/constants.rs
index cd6eaa3..1d17927 100644
--- a/crates/vcs_data/src/constants.rs
+++ b/crates/vcs_data/src/constants.rs
@@ -42,6 +42,7 @@ pub const SERVER_FILE_README: &str = "./README.md";
// Client
pub const CLIENT_PATH_WORKSPACE_ROOT: &str = "./.jv/";
+pub const CLIENT_FOLDER_WORKSPACE_ROOT_NAME: &str = ".jv";
// Client - Workspace (Main)
pub const CLIENT_FILE_WORKSPACE: &str = "./.jv/workspace.toml";
@@ -52,6 +53,9 @@ pub const CLIENT_FILE_LATEST_INFO: &str = "./.jv/latest.json";
// Client - Sheets
pub const CLIENT_FILE_SHEET_COPY: &str = "./.jv/sheets/{sheet_name}.copy.json";
+// Client - Local Draft
+pub const CLIENT_PATH_LOCAL_DRAFT: &str = "./.jv/drafts/{sheet_name}/";
+
// Client - Other
pub const CLIENT_FILE_IGNOREFILES: &str = "IGNORE_RULES.toml";
pub const CLIENT_FILE_README: &str = "./README.md";
diff --git a/crates/vcs_data/src/data/sheet.rs b/crates/vcs_data/src/data/sheet.rs
index b558c0d..ce450a6 100644
--- a/crates/vcs_data/src/data/sheet.rs
+++ b/crates/vcs_data/src/data/sheet.rs
@@ -51,7 +51,7 @@ pub struct Sheet<'a> {
#[derive(Default, Serialize, Deserialize, ConfigFile, Clone)]
pub struct SheetData {
/// The holder of the current sheet, who has full operation rights to the sheet mapping
- pub(crate) holder: MemberId,
+ pub(crate) holder: Option<MemberId>,
/// Inputs
pub(crate) inputs: Vec<InputPackage>,
@@ -66,8 +66,8 @@ impl<'a> Sheet<'a> {
}
/// Get the holder of this sheet
- pub fn holder(&self) -> &MemberId {
- &self.data.holder
+ pub fn holder(&self) -> Option<&MemberId> {
+ self.data.holder.as_ref()
}
/// Get the inputs of this sheet
@@ -143,10 +143,11 @@ impl<'a> Sheet<'a> {
/// Add (or Edit) a mapping entry to the sheet
///
/// This operation performs safety checks to ensure the member has the right to add the mapping:
- /// 1. If the virtual file ID doesn't exist in the vault, the mapping is added directly
- /// 2. If the virtual file exists, check if the member has edit rights to the virtual file
- /// 3. If member has edit rights, the mapping is not allowed to be modified and returns an error
- /// 4. If member doesn't have edit rights, the mapping is allowed (member is giving up the file)
+ /// 1. The sheet must have a holder (member) to perform this operation
+ /// 2. If the virtual file ID doesn't exist in the vault, the mapping is added directly
+ /// 3. If the virtual file exists, check if the member has edit rights to the virtual file
+ /// 4. If member has edit rights, the mapping is not allowed to be modified and returns an error
+ /// 5. If member doesn't have edit rights, the mapping is allowed (member is giving up the file)
///
/// Note: Full validation adds overhead - avoid frequent calls
pub async fn add_mapping(
@@ -161,10 +162,18 @@ impl<'a> Sheet<'a> {
return Ok(());
}
+ // Check if the sheet has a holder
+ let Some(holder) = self.holder() else {
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::PermissionDenied,
+ "This sheet has no holder",
+ ));
+ };
+
// Check if the holder has edit rights to the virtual file
match self
.vault_reference
- .has_virtual_file_edit_right(self.holder(), &virtual_file_id)
+ .has_virtual_file_edit_right(holder, &virtual_file_id)
.await
{
Ok(false) => {
@@ -191,9 +200,10 @@ impl<'a> Sheet<'a> {
/// Remove a mapping entry from the sheet
///
/// This operation performs safety checks to ensure the member has the right to remove the mapping:
- /// 1. Member must NOT have edit rights to the virtual file to release it (ensuring clear ownership)
- /// 2. If the virtual file doesn't exist, the mapping is removed but no ID is returned
- /// 3. If member has no edit rights and the file exists, returns the removed virtual file ID
+ /// 1. The sheet must have a holder (member) to perform this operation
+ /// 2. Member must NOT have edit rights to the virtual file to release it (ensuring clear ownership)
+ /// 3. If the virtual file doesn't exist, the mapping is removed but no ID is returned
+ /// 4. If member has no edit rights and the file exists, returns the removed virtual file ID
///
/// Note: Full validation adds overhead - avoid frequent calls
pub async fn remove_mapping(&mut self, sheet_path: &SheetPathBuf) -> Option<VirtualFileId> {
@@ -212,10 +222,15 @@ impl<'a> Sheet<'a> {
return None;
}
+ // Check if the sheet has a holder
+ let Some(holder) = self.holder() else {
+ return None;
+ };
+
// Check if the holder has edit rights to the virtual file
match self
.vault_reference
- .has_virtual_file_edit_right(self.holder(), virtual_file_id)
+ .has_virtual_file_edit_right(holder, virtual_file_id)
.await
{
Ok(false) => {