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
124
125
126
127
|
use crate::{
cmd_output,
cmds::{
arg::sheetedit::JVSheeteditArgument, collect::single_file::JVSingleFileCollect,
r#in::sheetedit::JVSheeteditInput, out::none::JVNoneOutput,
},
systems::{
cmd::{
cmd_system::JVCommandContext,
errors::{CmdExecuteError, CmdPrepareError},
},
helpdoc::helpdoc_viewer,
},
};
use cli_utils::{
display::table::Table, env::editor::get_default_editor,
input::editor::input_with_editor_cutsom, string_vec,
};
use cmd_system_macros::exec;
use just_enough_vcs::system::sheet_system::{mapping::LocalMapping, sheet::SheetData};
use just_fmt::fmt_path::{PathFormatError, fmt_path};
use rust_i18n::t;
use std::{any::TypeId, borrow::Cow, path::PathBuf};
use tokio::fs::create_dir_all;
pub struct JVSheeteditCommand;
type Cmd = JVSheeteditCommand;
type Arg = JVSheeteditArgument;
type In = JVSheeteditInput;
type Collect = JVSingleFileCollect;
async fn help_str() -> String {
helpdoc_viewer::display("commands/sheetedit").await;
String::new()
}
async fn prepare(args: &Arg, _ctx: &JVCommandContext) -> Result<In, CmdPrepareError> {
let file = fmt_path(args.file.clone()).map_err(|e| match e {
PathFormatError::InvalidUtf8(e) => CmdPrepareError::Error(e.to_string()),
})?;
let editor = args.editor.clone().unwrap_or(get_default_editor().await);
Ok(In { file, editor })
}
async fn collect(args: &Arg, _ctx: &JVCommandContext) -> Result<Collect, CmdPrepareError> {
let data = match tokio::fs::read(&args.file).await {
Ok(d) => d,
Err(_) => Vec::new(),
};
Ok(Collect { data })
}
#[exec]
async fn exec(
input: In,
collect: Collect,
) -> Result<(Box<dyn std::any::Any + Send + 'static>, TypeId), CmdExecuteError> {
let sheet = SheetData::try_from(collect.data).unwrap_or(SheetData::empty());
let mappings = sheet.mappings();
let mut mappings_vec = mappings.iter().cloned().collect::<Vec<LocalMapping>>();
mappings_vec.sort();
let template = build_template(&input.file, mappings_vec).to_string();
let temp_file = input.file.with_added_extension("md");
create_dir_all(temp_file.parent().unwrap()).await?;
let edit_result = input_with_editor_cutsom(template, &temp_file, "#", input.editor).await;
match edit_result {
Ok(t) => {
let rebuild_sheet_data = SheetData::try_from(t.as_str())
.map_err(|e| CmdExecuteError::Error(e.to_string()))?;
tokio::fs::write(&input.file, rebuild_sheet_data.as_bytes()).await?;
}
Err(e) => return Err(CmdExecuteError::Error(e.to_string())),
}
cmd_output!(JVNoneOutput => JVNoneOutput {})
}
fn build_template(file: &PathBuf, mappings: Vec<LocalMapping>) -> Cow<'static, str> {
let mapping_table = render_pretty_mappings(&mappings);
let template = t!(
"sheetedit.editor",
file_dir = file.display(),
info = mapping_table
);
template
}
fn render_pretty_mappings(mappings: &Vec<LocalMapping>) -> String {
let header = string_vec![
format!("# {}", t!("sheetedit.mapping")),
"",
t!("sheetedit.index_source"),
"",
t!("sheetedit.forward")
];
let mut table = Table::new(header);
for mapping in mappings {
let mapping_str = mapping
.to_string()
.split(" ")
.into_iter()
.map(|s| s.to_string())
.collect::<Vec<String>>();
table.push_item(vec![
format!(
" {} ",
mapping_str.get(0).unwrap_or(&String::default())
), // Mapping
format!("{} ", mapping_str.get(1).unwrap_or(&String::default())), // => & ==
format!("{} ", mapping_str.get(2).unwrap_or(&String::default())), // Index
format!("{} ", mapping_str.get(3).unwrap_or(&String::default())), // => & ==
format!("{} ", mapping_str.get(4).unwrap_or(&String::default())), // Forward
]);
}
table.to_string()
}
crate::command_template!();
|