summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--locales/help_docs/en.yml41
-rw-r--r--locales/help_docs/zh-CN.yml41
-rw-r--r--src/bin/jv.rs441
3 files changed, 511 insertions, 12 deletions
diff --git a/locales/help_docs/en.yml b/locales/help_docs/en.yml
index 548ce0f..af6cea3 100644
--- a/locales/help_docs/en.yml
+++ b/locales/help_docs/en.yml
@@ -542,6 +542,31 @@ jv:
The current workspace is not stained, cannot perform the next operation!
**Tip**: Please first use `jv direct <upstream_address>` to direct to an upstream vault
+ change_edit_right:
+ no_selection: No files selected!
+ check_failed: |
+ In the %{num} selected files, there are items that failed pre-check!
+ Add `--details` after the command to view specific details
+
+ **Tip**: Add `--skip-failed` after the command to skip the current failed items and proceed with the operation
+
+ check_failed_details:
+ In the %{num} selected files, %{failed} files failed pre-check!
+ %{items}
+
+ check_fail_item: |
+ %{path} (%{reason})
+
+ check_fail_reason:
+ not_found_in_local: File Not Found
+ not_found_in_sheet: Mapping Not Found In Sheet
+ not_a_tracked_file: File Not Tracked
+ base_version_unmatch: Version Mismatch
+ not_holder: Not Holder
+ has_holder: Held by %{holder}
+ already_held: Already Held
+ already_modified: Already Modified
+
docs:
not_found: Doc `%{docs_name}` not found!
no_doc_dir: |
@@ -809,6 +834,22 @@ jv:
not_owner: |
You are not the holder of sheet `%{name}`, cannot drop it!
+ change_edit_right:
+ failed:
+ none: |
+ Do nothing!
+
+ success:
+ hold: |
+ Held %{num} files!
+
+ throw: |
+ Threw %{num} files!
+
+ mixed: |
+ Successfully modified edit rights for %{num} files!
+ Held %{num_hold}, Threw %{num_throw}
+
track:
done: |
Tracked %{count} files to latest!
diff --git a/locales/help_docs/zh-CN.yml b/locales/help_docs/zh-CN.yml
index 267e85c..2f496ae 100644
--- a/locales/help_docs/zh-CN.yml
+++ b/locales/help_docs/zh-CN.yml
@@ -544,6 +544,31 @@ jv:
当前工作区并未被染色,无法执行下一步操作!
**提示**:请先使用 `jv direct <上游地址>` 定向到上游库
+ change_edit_right:
+ no_selection: 您未选中任何文件!
+ check_failed: |
+ 在您选中的 %{num} 个文件中,存在预检查失败的项!
+ 在命令后添加 `--details` 查看具体事项
+
+ **提示**:命令后添加 `--skip-failed` 可跳过当前检查失败的项进行操作
+
+ check_failed_details: |
+ 在您选中的 %{num} 个文件中,有 %{failed} 个文件预先检查未通过!
+ %{items}
+
+ check_fail_item: |
+ %{path}(%{reason})
+
+ check_fail_reason:
+ not_found_in_local: 文件未找到
+ not_found_in_sheet: 表中不存在
+ not_a_tracked_file: 文件未被跟踪
+ base_version_unmatch: 基准版本不匹配
+ not_holder: 期望丢弃,但不是持有者
+ has_holder: 期望持有,但被 %{holder} 持有
+ already_held: 文件已持有
+ already_modified: 文件已修改
+
docs:
not_found: 文档 `%{docs_name}` 未找到!
no_doc_dir: |
@@ -815,6 +840,22 @@ jv:
not_owner: |
您不是表 `%{name}` 的持有人,无法放弃该表!
+ change_edit_right:
+ failed:
+ none: |
+ 没有处理任何文件!
+
+ success:
+ hold: |
+ 成功持有 %{num} 个文件!
+
+ throw: |
+ 成功丢弃 %{num} 个文件!
+
+ mixed: |
+ 成功修改 %{num} 个文件的编辑权!
+ 持有 %{num_hold},丢弃 %{num_throw}
+
track:
done: |
追踪 %{count} 个文件至最新!
diff --git a/src/bin/jv.rs b/src/bin/jv.rs
index 9d7220a..ac6c5b1 100644
--- a/src/bin/jv.rs
+++ b/src/bin/jv.rs
@@ -22,6 +22,10 @@ use just_enough_vcs::{
TrackFileActionResult, UpdateDescription, UpdateTaskResult, VerifyFailReason,
proc_track_file_action,
},
+ user_actions::{
+ ChangeVirtualFileEditRightResult, EditRightChangeBehaviour,
+ proc_change_virtual_file_edit_right_action,
+ },
},
constants::{
CLIENT_FILE_TODOLIST, CLIENT_FILE_WORKSPACE, CLIENT_FOLDER_WORKSPACE_ROOT_NAME,
@@ -30,9 +34,14 @@ use just_enough_vcs::{
current::{current_doc_dir, current_local_path},
data::{
local::{
- LocalWorkspace, align::AlignTasks, cached_sheet::CachedSheet, config::LocalConfig,
- file_status::AnalyzeResult, latest_file_data::LatestFileData,
- latest_info::LatestInfo, local_files::get_relative_paths,
+ LocalWorkspace,
+ align::AlignTasks,
+ cached_sheet::CachedSheet,
+ config::LocalConfig,
+ file_status::AnalyzeResult,
+ latest_file_data::LatestFileData,
+ latest_info::LatestInfo,
+ local_files::{RelativeFiles, get_relative_paths},
vault_modified::check_vault_modified,
},
member::{Member, MemberId},
@@ -511,6 +520,17 @@ struct HoldFileArgs {
/// Show help information
#[arg(short, long)]
help: bool,
+
+ /// Hold files
+ hold_files: Option<Vec<PathBuf>>,
+
+ /// Show fail details
+ #[arg(short = 'd', long = "details")]
+ show_fail_details: bool,
+
+ /// Skip failed items
+ #[arg(short = 'S', long)]
+ skip_failed: bool,
}
#[derive(Parser, Debug)]
@@ -518,6 +538,17 @@ struct ThrowFileArgs {
/// Show help information
#[arg(short, long)]
help: bool,
+
+ /// Throw files
+ throw_files: Option<Vec<PathBuf>>,
+
+ /// Show fail details
+ #[arg(short = 'd', long = "details")]
+ show_fail_details: bool,
+
+ /// Skip failed items
+ #[arg(short = 'S', long)]
+ skip_failed: bool,
}
#[derive(Parser, Debug)]
@@ -1425,11 +1456,6 @@ async fn jv_status(_args: StatusArgs) {
return;
};
- let Ok(cached_sheet) = CachedSheet::cached_sheet_data(&sheet_name).await else {
- eprintln!("{}", md(t!("jv.fail.read_cfg")));
- return;
- };
-
let Ok(analyzed) = AnalyzeResult::analyze_local_status(&local_workspace).await else {
eprintln!("{}", md(t!("jv.fail.status.analyze")).trim());
return;
@@ -2419,12 +2445,403 @@ async fn start_update_editor(
update_info
}
-async fn jv_hold(_args: HoldFileArgs) {
- todo!()
+async fn jv_hold(args: HoldFileArgs) {
+ let hold_files = if let Some(files) = args.hold_files.clone() {
+ files
+ .iter()
+ .map(|f| current_dir().unwrap().join(f))
+ .collect::<Vec<_>>()
+ } else {
+ println!("{}", md(t!("jv.hold")));
+ return;
+ };
+
+ jv_change_edit_right(
+ hold_files,
+ EditRightChangeBehaviour::Hold,
+ args.show_fail_details,
+ args.skip_failed,
+ )
+ .await;
}
-async fn jv_throw(_args: ThrowFileArgs) {
- todo!()
+async fn jv_throw(args: ThrowFileArgs) {
+ let throw_files = if let Some(files) = args.throw_files.clone() {
+ files
+ .iter()
+ .map(|f| current_dir().unwrap().join(f))
+ .collect::<Vec<_>>()
+ } else {
+ println!("{}", md(t!("jv.throw")));
+ return;
+ };
+
+ jv_change_edit_right(
+ throw_files,
+ EditRightChangeBehaviour::Throw,
+ args.show_fail_details,
+ args.skip_failed,
+ )
+ .await;
+}
+
+async fn jv_change_edit_right(
+ files: Vec<PathBuf>,
+ behaviour: EditRightChangeBehaviour,
+ show_fail_details: bool,
+ mut skip_failed: bool,
+) {
+ // If both `--details` and `--skip-failed` are set, only enable `--details`
+ if show_fail_details && skip_failed {
+ skip_failed = false;
+ }
+
+ let Some(local_dir) = current_local_path() else {
+ eprintln!("{}", t!("jv.fail.workspace_not_found").trim());
+ return;
+ };
+
+ let Ok(local_cfg) = LocalConfig::read_from(local_dir.join(CLIENT_FILE_WORKSPACE)).await else {
+ eprintln!("{}", md(t!("jv.fail.read_cfg")));
+ return;
+ };
+
+ let Some(local_workspace) = LocalWorkspace::init_current_dir(local_cfg.clone()) else {
+ eprintln!("{}", md(t!("jv.fail.workspace_not_found")).trim());
+ return;
+ };
+
+ // Get files
+ let Ok(analyzed) = AnalyzeResult::analyze_local_status(&local_workspace).await else {
+ eprintln!("{}", md(t!("jv.fail.status.analyze")).trim());
+ return;
+ };
+
+ let account = local_cfg.current_account();
+
+ let Ok(latest_file_data_path) = LatestFileData::data_path(&account) else {
+ eprintln!("{}", md(t!("jv.fail.read_cfg")));
+ return;
+ };
+
+ let Ok(latest_file_data) = LatestFileData::read_from(&latest_file_data_path).await else {
+ eprintln!("{}", md(t!("jv.fail.read_cfg")));
+ return;
+ };
+
+ let Some(sheet_name) = local_cfg.sheet_in_use().clone() else {
+ eprintln!("{}", md(t!("jv.fail.status.no_sheet_in_use")).trim());
+ return;
+ };
+
+ let Ok(local_sheet) = local_workspace.local_sheet(&account, &sheet_name).await else {
+ eprintln!("{}", md(t!("jv.fail.read_cfg")));
+ return;
+ };
+
+ let Ok(cached_sheet) = CachedSheet::cached_sheet_data(&sheet_name).await else {
+ eprintln!("{}", md(t!("jv.fail.read_cfg")));
+ return;
+ };
+
+ // Precheck and filter
+ let Some(filtered_files) = get_relative_paths(&local_dir, &files).await else {
+ eprintln!(
+ "{}",
+ md(t!("jv.fail.track.parse_fail", param = "track_files"))
+ );
+ return;
+ };
+ let num = filtered_files.iter().len();
+ if num < 1 {
+ eprintln!("{}", md(t!("jv.fail.change_edit_right.no_selection")));
+ return;
+ }
+
+ let mut passed_files = Vec::new();
+ let mut details = Vec::new();
+ let mut failed = 0;
+
+ for file in filtered_files {
+ let full_path = local_dir.join(&file);
+
+ // File exists
+ if !full_path.exists() {
+ if show_fail_details {
+ details.push(
+ t!(
+ "jv.fail.change_edit_right.check_fail_item",
+ path = file.display(),
+ reason =
+ t!("jv.fail.change_edit_right.check_fail_reason.not_found_in_local")
+ )
+ .trim()
+ .to_string(),
+ );
+ failed += 1;
+ continue;
+ } else {
+ eprintln!(
+ "{}",
+ md(t!("jv.fail.change_edit_right.check_failed", num = num))
+ );
+ return;
+ }
+ }
+
+ // Mapping exists
+ if !cached_sheet.mapping().contains_key(&file) {
+ if show_fail_details {
+ details.push(
+ t!(
+ "jv.fail.change_edit_right.check_fail_item",
+ path = file.display(),
+ reason =
+ t!("jv.fail.change_edit_right.check_fail_reason.not_found_in_sheet")
+ )
+ .trim()
+ .to_string(),
+ );
+ failed += 1;
+ continue;
+ } else {
+ eprintln!(
+ "{}",
+ md(t!("jv.fail.change_edit_right.check_failed", num = num))
+ );
+ return;
+ }
+ }
+
+ // Not tracked
+ let Ok(local_mapping) = local_sheet.mapping_data(&file) else {
+ if show_fail_details {
+ details.push(
+ t!(
+ "jv.fail.change_edit_right.check_fail_item",
+ path = file.display(),
+ reason =
+ t!("jv.fail.change_edit_right.check_fail_reason.not_a_tracked_file")
+ )
+ .trim()
+ .to_string(),
+ );
+ failed += 1;
+ continue;
+ } else {
+ eprintln!(
+ "{}",
+ md(t!("jv.fail.change_edit_right.check_failed", num = num))
+ );
+ return;
+ }
+ };
+
+ let vfid = local_mapping.mapping_vfid();
+ let local_version = local_mapping.version_when_updated();
+
+ // Base version unmatch
+ if local_version
+ != latest_file_data
+ .file_version(vfid)
+ .unwrap_or(&String::default())
+ {
+ if show_fail_details {
+ details.push(
+ t!(
+ "jv.fail.change_edit_right.check_fail_item",
+ path = file.display(),
+ reason =
+ t!("jv.fail.change_edit_right.check_fail_reason.base_version_unmatch")
+ )
+ .trim()
+ .to_string(),
+ );
+ failed += 1;
+ continue;
+ } else {
+ eprintln!(
+ "{}",
+ md(t!("jv.fail.change_edit_right.check_failed", num = num))
+ );
+ return;
+ }
+ }
+
+ // Hold
+ let holder = latest_file_data.file_holder(vfid);
+ match behaviour {
+ EditRightChangeBehaviour::Hold => {
+ if holder.is_some_and(|h| h != &account) {
+ if show_fail_details {
+ details.push(
+ t!(
+ "jv.fail.change_edit_right.check_fail_item",
+ path = file.display(),
+ reason = t!(
+ "jv.fail.change_edit_right.check_fail_reason.has_holder",
+ holder = holder.unwrap()
+ )
+ )
+ .trim()
+ .to_string(),
+ );
+ failed += 1;
+ continue;
+ } else {
+ eprintln!(
+ "{}",
+ md(t!("jv.fail.change_edit_right.check_failed", num = num))
+ );
+ return;
+ }
+ }
+
+ if holder.is_some_and(|h| h == &account) {
+ if show_fail_details {
+ details.push(
+ t!(
+ "jv.fail.change_edit_right.check_fail_item",
+ path = file.display(),
+ reason =
+ t!("jv.fail.change_edit_right.check_fail_reason.already_held")
+ )
+ .trim()
+ .to_string(),
+ );
+ failed += 1;
+ continue;
+ } else {
+ eprintln!(
+ "{}",
+ md(t!("jv.fail.change_edit_right.check_failed", num = num))
+ );
+ return;
+ }
+ }
+ }
+ EditRightChangeBehaviour::Throw => {
+ if holder.is_some_and(|h| h != &account) {
+ if show_fail_details {
+ details.push(
+ t!(
+ "jv.fail.change_edit_right.check_fail_item",
+ path = file.display(),
+ reason =
+ t!("jv.fail.change_edit_right.check_fail_reason.not_holder")
+ )
+ .trim()
+ .to_string(),
+ );
+ failed += 1;
+ continue;
+ } else {
+ eprintln!(
+ "{}",
+ md(t!("jv.fail.change_edit_right.check_failed", num = num))
+ );
+ return;
+ }
+ }
+ if analyzed.modified.contains(&file) {
+ if show_fail_details {
+ details.push(
+ t!(
+ "jv.fail.change_edit_right.check_fail_item",
+ path = file.display(),
+ reason = t!(
+ "jv.fail.change_edit_right.check_fail_reason.already_modified"
+ )
+ )
+ .trim()
+ .to_string(),
+ );
+ failed += 1;
+ continue;
+ } else {
+ eprintln!(
+ "{}",
+ md(t!("jv.fail.change_edit_right.check_failed", num = num))
+ );
+ return;
+ }
+ }
+ }
+ }
+ passed_files.push(file);
+ }
+ if failed > 0 && show_fail_details {
+ eprintln!(
+ "{}",
+ md(t!(
+ "jv.fail.change_edit_right.check_failed_details",
+ num = num,
+ failed = failed,
+ items = details.join("\n").trim()
+ ))
+ );
+ return;
+ }
+
+ if !(failed > 0 && skip_failed) && failed != 0 {
+ return;
+ }
+
+ let (pool, ctx) = match build_pool_and_ctx(&local_cfg).await {
+ Some(result) => result,
+ None => return,
+ };
+
+ let passed = passed_files
+ .iter()
+ .map(|f| (f.clone(), behaviour.clone()))
+ .collect();
+
+ match proc_change_virtual_file_edit_right_action(&pool, ctx, (passed, true)).await {
+ Ok(r) => match r {
+ ChangeVirtualFileEditRightResult::Success {
+ success_hold,
+ success_throw,
+ } => {
+ if success_hold.len() > 0 && success_throw.len() == 0 {
+ println!(
+ "{}",
+ md(t!(
+ "jv.result.change_edit_right.success.hold",
+ num = success_hold.len()
+ ))
+ )
+ } else if success_hold.len() == 0 && success_throw.len() > 0 {
+ println!(
+ "{}",
+ md(t!(
+ "jv.result.change_edit_right.success.throw",
+ num = success_throw.len()
+ ))
+ )
+ } else if success_hold.len() > 0 && success_throw.len() > 0 {
+ println!(
+ "{}",
+ md(t!(
+ "jv.result.change_edit_right.success.mixed",
+ num = success_hold.len() + success_throw.len(),
+ num_hold = success_hold.len(),
+ num_throw = success_throw.len()
+ ))
+ )
+ } else {
+ eprintln!("{}", md(t!("jv.result.change_edit_right.failed.none")))
+ }
+ }
+ ChangeVirtualFileEditRightResult::AuthorizeFailed(e) => {
+ eprintln!("{}", md(t!("jv.result.common.authroize_failed", err = e)))
+ }
+ ChangeVirtualFileEditRightResult::DoNothing => {
+ eprintln!("{}", md(t!("jv.result.change_edit_right.failed.none")))
+ }
+ },
+ Err(e) => handle_err(e),
+ }
}
async fn jv_move(_args: MoveFileArgs) {