diff options
Diffstat (limited to 'src/cmds')
| -rw-r--r-- | src/cmds/arg/status.rs | 4 | ||||
| -rw-r--r-- | src/cmds/cmd/status.rs (renamed from src/cmds/status.rs) | 12 | ||||
| -rw-r--r-- | src/cmds/collect/status.rs | 38 | ||||
| -rw-r--r-- | src/cmds/in/status.rs | 1 | ||||
| -rw-r--r-- | src/cmds/out/status.rs | 47 | ||||
| -rw-r--r-- | src/cmds/renderer/json.rs | 27 | ||||
| -rw-r--r-- | src/cmds/renderer/json_pretty.rs | 26 | ||||
| -rw-r--r-- | src/cmds/renderer/status.rs | 262 |
8 files changed, 412 insertions, 5 deletions
diff --git a/src/cmds/arg/status.rs b/src/cmds/arg/status.rs new file mode 100644 index 0000000..c713227 --- /dev/null +++ b/src/cmds/arg/status.rs @@ -0,0 +1,4 @@ +use clap::Parser; + +#[derive(Parser, Debug)] +pub struct JVStatusArgument; diff --git a/src/cmds/status.rs b/src/cmds/cmd/status.rs index 6f04408..f34dbb8 100644 --- a/src/cmds/status.rs +++ b/src/cmds/cmd/status.rs @@ -5,11 +5,13 @@ use just_enough_vcs::vcs::{ }; use crate::{ - args::status::JVStatusArgument, - collects::status::JVStatusCollect, - inputs::status::JVStatusInput, - outputs::status::{JVStatusOutput, JVStatusWrongModifyReason}, - renderers::status::JVStatusRenderer, + cmds::{ + arg::status::JVStatusArgument, + collect::status::JVStatusCollect, + r#in::status::JVStatusInput, + out::status::{JVStatusOutput, JVStatusWrongModifyReason}, + renderer::status::JVStatusRenderer, + }, systems::cmd::{ cmd_system::{JVCommand, JVCommandContext}, errors::{CmdExecuteError, CmdPrepareError}, diff --git a/src/cmds/collect/status.rs b/src/cmds/collect/status.rs new file mode 100644 index 0000000..b0e8fcd --- /dev/null +++ b/src/cmds/collect/status.rs @@ -0,0 +1,38 @@ +use std::time::SystemTime; + +use just_enough_vcs::vcs::data::{ + local::{ + latest_file_data::LatestFileData, local_sheet::LocalSheetData, + workspace_analyzer::AnalyzeResultPure, + }, + member::MemberId, + sheet::SheetName, +}; + +pub struct JVStatusCollect { + pub current_account: MemberId, + pub current_sheet: SheetName, + pub is_host_mode: bool, + pub in_ref_sheet: bool, + pub analyzed_result: AnalyzeResultPure, + pub latest_file_data: LatestFileData, + pub local_sheet_data: LocalSheetData, + pub update_time: SystemTime, + pub now_time: SystemTime, +} + +impl Default for JVStatusCollect { + fn default() -> Self { + Self { + current_account: MemberId::default(), + current_sheet: SheetName::default(), + is_host_mode: false, + in_ref_sheet: false, + analyzed_result: AnalyzeResultPure::default(), + latest_file_data: LatestFileData::default(), + local_sheet_data: LocalSheetData::default(), + update_time: SystemTime::now(), + now_time: SystemTime::now(), + } + } +} diff --git a/src/cmds/in/status.rs b/src/cmds/in/status.rs new file mode 100644 index 0000000..859ccd2 --- /dev/null +++ b/src/cmds/in/status.rs @@ -0,0 +1 @@ +pub struct JVStatusInput; diff --git a/src/cmds/out/status.rs b/src/cmds/out/status.rs new file mode 100644 index 0000000..2b8d9c6 --- /dev/null +++ b/src/cmds/out/status.rs @@ -0,0 +1,47 @@ +use std::{collections::HashMap, time::SystemTime}; + +use just_enough_vcs::vcs::data::{ + local::workspace_analyzer::{AnalyzeResultPure, ModifiedRelativePathBuf}, + member::MemberId, + sheet::SheetName, +}; +use serde::Serialize; + +#[derive(Serialize)] +pub struct JVStatusOutput { + pub current_account: MemberId, + pub current_sheet: SheetName, + pub is_host_mode: bool, + pub in_ref_sheet: bool, + pub analyzed_result: AnalyzeResultPure, + pub wrong_modified_items: HashMap<ModifiedRelativePathBuf, JVStatusWrongModifyReason>, + pub update_time: SystemTime, + pub now_time: SystemTime, +} + +#[derive(Serialize)] +pub enum JVStatusWrongModifyReason { + BaseVersionMismatch { + base_version: String, + latest_version: String, + }, + ModifiedButNotHeld { + holder: String, + }, + NoHolder, +} + +impl Default for JVStatusOutput { + fn default() -> Self { + Self { + current_account: MemberId::default(), + current_sheet: SheetName::default(), + is_host_mode: false, + in_ref_sheet: false, + analyzed_result: AnalyzeResultPure::default(), + wrong_modified_items: HashMap::new(), + update_time: SystemTime::now(), + now_time: SystemTime::now(), + } + } +} diff --git a/src/cmds/renderer/json.rs b/src/cmds/renderer/json.rs new file mode 100644 index 0000000..9a3105d --- /dev/null +++ b/src/cmds/renderer/json.rs @@ -0,0 +1,27 @@ +use serde::Serialize; +use serde_json; + +use crate::{ + r_print, + systems::cmd::{ + errors::CmdRenderError, + renderer::{JVRenderResult, JVResultRenderer}, + }, +}; + +pub struct JVResultJsonRenderer; + +impl<T> JVResultRenderer<T> for JVResultJsonRenderer +where + T: Serialize + Sync, +{ + async fn render(data: &T) -> Result<JVRenderResult, CmdRenderError> { + let mut r = JVRenderResult::default(); + let json_string = serde_json::to_string(data) + .map_err(|e| CmdRenderError::SerializeFailed(e.to_string()))?; + + r_print!(r, "{}", json_string); + + Ok(r) + } +} diff --git a/src/cmds/renderer/json_pretty.rs b/src/cmds/renderer/json_pretty.rs new file mode 100644 index 0000000..a4a3ba5 --- /dev/null +++ b/src/cmds/renderer/json_pretty.rs @@ -0,0 +1,26 @@ +use serde::Serialize; + +use crate::{ + r_print, + systems::cmd::{ + errors::CmdRenderError, + renderer::{JVRenderResult, JVResultRenderer}, + }, +}; + +pub struct JVResultPrettyJsonRenderer; + +impl<T> JVResultRenderer<T> for JVResultPrettyJsonRenderer +where + T: Serialize + Sync, +{ + async fn render(data: &T) -> Result<JVRenderResult, CmdRenderError> { + let mut r = JVRenderResult::default(); + let json_string = serde_json::to_string_pretty(data) + .map_err(|e| CmdRenderError::SerializeFailed(e.to_string()))?; + + r_print!(r, "{}", json_string); + + Ok(r) + } +} diff --git a/src/cmds/renderer/status.rs b/src/cmds/renderer/status.rs new file mode 100644 index 0000000..965ff87 --- /dev/null +++ b/src/cmds/renderer/status.rs @@ -0,0 +1,262 @@ +use cli_utils::{ + display::{SimpleTable, md}, + env::auto_update_outdate, +}; +use rust_i18n::t; + +use crate::{ + cmds::out::status::{JVStatusOutput, JVStatusWrongModifyReason}, + r_println, + systems::cmd::{ + errors::CmdRenderError, + renderer::{JVRenderResult, JVResultRenderer}, + }, +}; + +pub struct JVStatusRenderer; + +enum Mode { + StructuralChangesMode, + ContentChangesMode, + Clean, +} + +impl JVResultRenderer<JVStatusOutput> for JVStatusRenderer { + async fn render(data: &JVStatusOutput) -> Result<JVRenderResult, CmdRenderError> { + let mut r = JVRenderResult::default(); + + // Render Header + render_header(&mut r, data); + + // Render Info and Mode + render_info_and_mode(&mut r, data); + + // Render Hint + render_hint(&mut r, data); + + Ok(r) + } +} + +fn render_header(r: &mut JVRenderResult, data: &JVStatusOutput) { + let account = &data.current_account; + let sheet = &data.current_sheet; + r_println!( + r, + "{}", + md(t!("status.header", account = account, sheet = sheet)) + ); +} + +fn render_info_and_mode(r: &mut JVRenderResult, data: &JVStatusOutput) { + let mut info_erased = String::default(); + let mut info_moved = String::default(); + let mut info_lost = String::default(); + let mut info_created = String::default(); + let mut info_modified = String::default(); + + // Collect erased items + if !data.analyzed_result.erased.is_empty() { + info_erased.push_str(format!("{}\n", md(t!("status.info_display.erased.header"))).as_str()); + for erased in data.analyzed_result.erased.iter() { + info_erased.push_str( + format!( + "{}\n", + md(t!( + "status.info_display.erased.item", + item = erased.display() + )) + ) + .as_str(), + ); + } + } + + // Collect moved items + if !data.analyzed_result.moved.is_empty() { + let mut table = SimpleTable::new(vec![ + format!("{}", md(t!("status.info_display.moved.header"))), + "".to_string(), + ]); + for (_, (from, to)) in data.analyzed_result.moved.iter() { + table.push_item(vec![ + format!( + "{}", + md(t!("status.info_display.moved.left", left = from.display())) + ), + format!( + "{}", + md(t!("status.info_display.moved.right", right = to.display())) + ), + ]); + } + info_moved.push_str(table.to_string().as_str()); + } + + // Collect lost items + if !data.analyzed_result.lost.is_empty() { + info_lost.push_str(format!("{}\n", md(t!("status.info_display.lost.header"))).as_str()); + for lost in data.analyzed_result.lost.iter() { + info_lost.push_str( + format!( + "{}\n", + md(t!("status.info_display.lost.item", item = lost.display())) + ) + .as_str(), + ); + } + } + + // Collect created items + if !data.analyzed_result.created.is_empty() { + info_created + .push_str(format!("{}\n", md(t!("status.info_display.created.header"))).as_str()); + for created in data.analyzed_result.created.iter() { + info_created.push_str( + format!( + "{}\n", + md(t!( + "status.info_display.created.item", + item = created.display() + )) + ) + .as_str(), + ); + } + } + + // Collect modified items + if !data.analyzed_result.modified.is_empty() { + info_modified + .push_str(format!("{}\n", md(t!("status.info_display.modified.header"))).as_str()); + for modified in data.analyzed_result.modified.iter() { + if let Some(reason) = data.wrong_modified_items.get(modified) { + let reason_str = match reason { + JVStatusWrongModifyReason::BaseVersionMismatch { + base_version, + latest_version, + } => md(t!( + "status.info_display.modified.reason.base_version_mismatch", + base_version = base_version, + latest_version = latest_version + )), + JVStatusWrongModifyReason::ModifiedButNotHeld { holder } => md(t!( + "status.info_display.modified.reason.modified_but_not_held", + holder = holder + )), + JVStatusWrongModifyReason::NoHolder => { + md(t!("status.info_display.modified.reason.no_holder")) + } + }; + info_modified.push_str( + format!( + "{}\n", + md(t!( + "status.info_display.modified.item_wrong", + item = modified.display(), + reason = reason_str + )) + ) + .as_str(), + ); + continue; + } + info_modified.push_str( + format!( + "{}\n", + md(t!( + "status.info_display.modified.item", + item = modified.display() + )) + ) + .as_str(), + ); + } + } + + let structural_info = vec![info_erased, info_moved, info_lost].join("\n"); + let content_info = vec![info_created, info_modified].join("\n"); + + let mode = get_mode(data); + match mode { + Mode::StructuralChangesMode => { + r_println!( + r, + "{}", + md(t!("status.current_mode.structural", info = structural_info)) + ); + } + Mode::ContentChangesMode => { + r_println!( + r, + "{}", + md(t!("status.current_mode.content", info = content_info)) + ); + } + Mode::Clean => r_println!(r, "{}", md(t!("status.current_mode.clean"))), + } +} + +fn render_hint(r: &mut JVRenderResult, data: &JVStatusOutput) { + // Outdate Hint + let update_time = &data.update_time; + let now_time = &data.now_time; + let duration_minutes: i64 = (now_time + .duration_since(*update_time) + .unwrap_or_default() + .as_secs() + / 60) as i64; + let outdate_minutes = auto_update_outdate(); + + // Outdated + if duration_minutes > outdate_minutes { + let hours = duration_minutes / 60; + let minutes = duration_minutes % 60; + let seconds = (now_time + .duration_since(*update_time) + .unwrap_or_default() + .as_secs() + % 60) as i64; + + r_println!( + r, + "{}", + md(t!( + "status.hints.outdate", + h = hours, + m = minutes, + s = seconds + )) + ); + } + + let in_ref_sheet = &data.in_ref_sheet; + let is_host_mode = &data.is_host_mode; + + // Readonly + if *in_ref_sheet && !is_host_mode { + r_println!(r, "{}", md(t!("status.hints.readonly"))); + } + + // Host + if *is_host_mode { + r_println!(r, "{}", md(t!("status.hints.host"))); + } +} + +fn get_mode(data: &JVStatusOutput) -> Mode { + let analyzed = &data.analyzed_result; + + // If there are any lost, moved, or erased items, use structural changes mode + if !analyzed.moved.is_empty() || !analyzed.lost.is_empty() || !analyzed.erased.is_empty() { + Mode::StructuralChangesMode + } + // Otherwise, if there are any created or modified items, use content changes mode + else if !analyzed.created.is_empty() || !analyzed.modified.is_empty() { + Mode::ContentChangesMode + } + // Otherwise, it's clean + else { + Mode::Clean + } +} |
