1use crate::cli_utils::*;
2use crate::encode_core;
3use crate::encode_core::Param;
4use crate::file_utils;
5use crate::json_printer::BracketType;
6use crate::misc_utils;
7use crate::multihash;
8use crate::rand_utils;
9use crate::sbx_specs::{
10 ver_to_block_size, ver_to_data_size, ver_to_usize, ver_uses_rs, SBX_FILE_UID_LEN,
11};
12use crate::time_utils;
13use clap::*;
14use std::str::FromStr;
15use std::time::UNIX_EPOCH;
16
17pub fn sub_command<'a, 'b>() -> App<'a, 'b> {
18 SubCommand::with_name("encode")
19 .about("Encode file")
20 .arg(
21 in_file_arg()
22 .help("File to encode. Supply - to use stdin as input. Use ./- for files named -."),
23 )
24 .arg(out_arg().help(
25 "SBX container name (defaults to INFILE.sbx or INFILE.ecsbx). If OUT is a
26directory, then the container is stored as OUT/INFILE.sbx or
27OUT/INFILE.ecsbx (only the file part of INFILE is used).",
28 ))
29 .arg(force_arg().help("Force overwrite even if OUT exists"))
30 .arg(
31 Arg::with_name("hash_type")
32 .value_name("HASH-TYPE")
33 .long("hash")
34 .takes_value(true)
35 .help(
36 "Hash function to use, one of (case-insensitive) :
37 sha1
38(default) sha256
39 sha512
40 blake2b-256
41 blake2b-512
42 blake2s-128
43 blake2s-256",
44 ),
45 )
46 .arg(Arg::with_name("no_meta").long("no-meta").help(
47 "Skip metadata block in the SBX container. Metadata block is
48never skipped for version 17, 18, 19.
49This means this option has no effect for version 17, 18, 19.",
50 ))
51 .arg(pr_verbosity_level_arg())
52 .arg(sbx_version_arg())
53 .arg(only_pick_uid_arg().long("uid").help(
54 "Alternative file UID in hex (by default UID is randomly generated).
55UID must be exactly 6 bytes (12 hex digits) in length.",
56 ))
57 .arg(rs_data_arg())
58 .arg(rs_parity_arg())
59 .arg(from_byte_arg().help(FROM_BYTE_ARG_HELP_MSG_RAW_UNALIGNED))
60 .arg(to_byte_inc_arg())
61 .arg(to_byte_exc_arg())
62 .arg(burst_arg().help(
63 "Burst error resistance level. Note that blkar only guesses up to
641000 in repair, show, and sort mode. If you use level above 1000,
65then blkar will make an incorrect guess, and you will need to
66specify it explicitly in repair and sort mode. Show mode does
67not rely on burst level, but provides an option for enabling
68automatic guessing.",
69 ))
70 .arg(
71 Arg::with_name("info_only")
72 .long("info-only")
73 .help("Only display information about encoding then exit"),
74 )
75 .arg(json_arg())
76}
77
78pub fn encode<'a>(matches: &ArgMatches<'a>) -> i32 {
79 let json_printer = get_json_printer!(matches);
80
81 json_printer.print_open_bracket(None, BracketType::Curly);
82
83 let mut uid: [u8; SBX_FILE_UID_LEN] = [0; SBX_FILE_UID_LEN];
85 {
86 match matches.value_of("uid") {
87 None => {
88 rand_utils::fill_random_bytes(&mut uid);
89 }
90 Some(x) => {
91 parse_uid!(uid, x, json_printer);
92 }
93 }
94 }
95
96 let (version, data_par_burst) = get_ver_and_data_par_burst_w_defaults!(matches, json_printer);
97
98 let out_extension = if ver_uses_rs(version) { "ecsbx" } else { "sbx" };
99
100 let in_file = get_in_file!(accept_stdin matches, json_printer);
101
102 let out = match matches.value_of("out") {
103 None => {
104 if file_utils::check_if_file_is_stdin(in_file) {
105 exit_with_msg!(usr json_printer => "Explicit output file name is required when input is stdin");
106 } else {
107 format!("{}.{}", in_file, out_extension)
108 }
109 }
110 Some(x) => {
111 if file_utils::check_if_file_is_dir(x) {
112 if file_utils::check_if_file_is_stdin(in_file) {
113 exit_with_msg!(usr json_printer => "Explicit output file name is required when input is stdin");
114 }
115
116 let in_file = file_utils::get_file_name_part_of_path(in_file).unwrap();
117 misc_utils::make_path(&[x, &format!("{}.{}", in_file, out_extension)])
118 } else {
119 String::from(x)
120 }
121 }
122 };
123
124 let hash_type = match matches.value_of("hash_type") {
125 None => multihash::HashType::SHA256,
126 Some(x) => match multihash::string_to_hash_type(x) {
127 Ok(x) => x,
128 Err(_) => exit_with_msg!(usr json_printer => "Invalid hash type"),
129 },
130 };
131
132 let pr_verbosity_level = get_pr_verbosity_level!(matches, json_printer);
133
134 let meta_enabled = get_meta_enabled!(matches);
135
136 let from_pos = get_from_pos!(matches, json_printer);
137 let to_pos = get_to_pos!(matches, json_printer);
138
139 if matches.is_present("info_only") {
140 json_printer.print_open_bracket(Some("stats"), BracketType::Curly);
141
142 if file_utils::check_if_file_is_stdin(in_file) {
143 exit_with_msg!(usr json_printer => "No information is available for stdin input");
144 }
145
146 let in_file_meta = match file_utils::get_file_metadata(in_file) {
147 Ok(x) => x,
148 Err(_) => exit_with_msg!(usr json_printer => "Failed to get metadata of \"{}\"",
149 in_file),
150 };
151
152 let in_file_size = match file_utils::get_file_size(in_file) {
153 Ok(x) => x,
154 Err(_) => exit_with_msg!(usr json_printer => "Failed to get file size of \"{}\"",
155 in_file),
156 };
157
158 let in_file_mod_time = match in_file_meta.modified() {
159 Ok(t) => match t.duration_since(UNIX_EPOCH) {
160 Ok(t) => Some(t.as_secs() as i64),
161 Err(_) => None,
162 },
163 Err(_) => None,
164 };
165
166 let in_file_mod_time_str = match in_file_mod_time {
167 None => null_if_json_else_NA!(json_printer).to_string(),
168 Some(x) => match (
169 time_utils::i64_secs_to_date_time_string(x, time_utils::TimeMode::UTC),
170 time_utils::i64_secs_to_date_time_string(x, time_utils::TimeMode::Local),
171 ) {
172 (Some(u), Some(l)) => format!("{} (UTC) {} (Local)", u, l),
173 _ => "Invalid file modification time".to_string(),
174 },
175 };
176
177 let out_file_size = file_utils::from_orig_file_size::calc_container_size(
178 version,
179 Some(meta_enabled),
180 data_par_burst,
181 in_file_size,
182 );
183
184 if ver_uses_rs(version) {
185 print_maybe_json!(json_printer, "File name : {}", in_file);
186 print_maybe_json!(json_printer, "SBX container name : {}", out);
187 print_maybe_json!(
188 json_printer,
189 "SBX container version : {}",
190 ver_to_usize(version)
191 );
192 print_maybe_json!(
193 json_printer,
194 "SBX container block size : {}",
195 ver_to_block_size(version)
196 );
197 print_maybe_json!(
198 json_printer,
199 "SBX container data size : {}",
200 ver_to_data_size(version)
201 );
202 print_maybe_json!(
203 json_printer,
204 "RS data shard count : {}",
205 data_par_burst.unwrap().0
206 );
207 print_maybe_json!(
208 json_printer,
209 "RS parity shard count : {}",
210 data_par_burst.unwrap().1
211 );
212 print_maybe_json!(
213 json_printer,
214 "Burst error resistance level : {}",
215 data_par_burst.unwrap().2
216 );
217 print_maybe_json!(
218 json_printer,
219 "File size : {}",
220 in_file_size
221 );
222 print_maybe_json!(
223 json_printer,
224 "SBX container size : {}",
225 out_file_size
226 );
227 print_maybe_json!(
228 json_printer,
229 "File modification time : {}",
230 in_file_mod_time_str
231 );
232 } else {
233 print_maybe_json!(json_printer, "File name : {}", in_file);
234 print_maybe_json!(json_printer, "SBX container name : {}", out);
235 print_maybe_json!(
236 json_printer,
237 "SBX container version : {}",
238 ver_to_usize(version)
239 );
240 print_maybe_json!(
241 json_printer,
242 "SBX container block size : {}",
243 ver_to_block_size(version)
244 );
245 print_maybe_json!(
246 json_printer,
247 "SBX container data size : {}",
248 ver_to_data_size(version)
249 );
250 print_maybe_json!(json_printer, "File size : {}", in_file_size);
251 print_maybe_json!(json_printer, "SBX container size : {}", out_file_size);
252 print_maybe_json!(
253 json_printer,
254 "File modification time : {}",
255 in_file_mod_time_str
256 );
257 }
258
259 json_printer.print_close_bracket();
260
261 exit_with_msg!(ok json_printer => "")
262 } else {
263 exit_if_file!(exists &out
264 => matches.is_present("force")
265 => json_printer
266 => "File \"{}\" already exists", out);
267
268 let in_file = if file_utils::check_if_file_is_stdin(in_file) {
269 None
270 } else {
271 Some(in_file)
272 };
273
274 let param = Param::new(
275 version,
276 &uid,
277 data_par_burst,
278 meta_enabled,
279 &json_printer,
280 hash_type,
281 from_pos,
282 to_pos,
283 in_file,
284 &out,
285 pr_verbosity_level,
286 );
287 match encode_core::encode_file(¶m) {
288 Ok(s) => exit_with_msg!(ok json_printer => "{}", s),
289 Err(e) => exit_with_msg!(op json_printer => "{}", e),
290 }
291 }
292}