1use std::time::UNIX_EPOCH;
2use encode_core::Param;
3use encode_core;
4use sbx_specs::{SBX_FILE_UID_LEN,
5 Version,
6 ver_to_usize,
7 ver_to_block_size,
8 ver_to_data_size,
9 ver_uses_rs};
10use std::str::FromStr;
11
12use json_printer::BracketType;
13
14use multihash;
15
16use file_utils;
17use misc_utils;
18use rand_utils;
19use time_utils;
20
21use clap::*;
22use cli_utils::*;
23
24pub fn sub_command<'a, 'b>() -> App<'a, 'b> {
25 SubCommand::with_name("encode")
26 .about("Encode file
27
28===== IMPORTANT =====
29Please note that this is the last version of this software to be released under the name rsbx,
30future releases will be published under the name blkar. See project repo for details.
31=====================")
32 .arg(in_file_arg()
33 .help("File to encode. Supply - to use STDIN as input."))
34 .arg(out_arg()
35 .help("SBX container name (defaults to INFILE.sbx). If OUT is a
36directory, then the container is stored as OUT/INFILE.sbx."))
37 .arg(Arg::with_name("force")
38 .short("f")
39 .long("force")
40 .help("Force overwrite even if OUT exists"))
41 .arg(Arg::with_name("hash_type")
42 .value_name("HASH-TYPE")
43 .long("hash")
44 .takes_value(true)
45 .help("Hash function to use, one of (case-insensitive) :
46 sha1
47(default) sha256
48 sha512
49 blake2b-512"))
50 .arg(Arg::with_name("no_meta")
51 .long("no-meta")
52 .help("Skip metadata block in the SBX container. Metadata block is
53never skipped for version 17, 18, 19.
54This means this option has no effect for version 17, 18, 19."))
55 .arg(pr_verbosity_level_arg())
56 .arg(sbx_version_arg())
57 .arg(Arg::with_name("uid")
58 .value_name("UID-HEX")
59 .long("uid")
60 .takes_value(true)
61 .help("Alternative file uid in hex (by default uid is randomly generated).
62Uid must be exactly 6 bytes (12 hex digits) in length."))
63 .arg(rs_data_arg())
64 .arg(rs_parity_arg())
65 .arg(burst_arg()
66 .help("Burst error resistance level. Note that rsbx only guesses up to
671000 in repair, show, and sort mode. If you use level above 1000,
68then rsbx will make an incorrect guess, and you will need to
69specify it explicitly in repair and sort mode. Show mode does
70not rely on burst level, but provides an option for enabling
71automatic guessing."))
72 .arg(Arg::with_name("info_only")
73 .long("info-only")
74 .help("Only display information about encoding then exit"))
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 => { rand_utils::fill_random_bytes(&mut uid); },
88 Some(x) => { parse_uid!(uid, x, json_printer); }
89 }
90 }
91
92 let version = get_version!(matches, json_printer);
93
94 let data_par_burst =
95 if ver_uses_rs(version) {
96 let data_shards = get_data_shards!(matches, version, json_printer);
98 let parity_shards = get_parity_shards!(matches, version, json_printer);
99
100 check_data_parity_shards!(data_shards, parity_shards, json_printer);
101
102 let burst = get_burst_or_zero!(matches, json_printer);
103
104 Some((data_shards, parity_shards, burst))
105 } else {
106 None
107 };
108
109 let in_file = get_in_file!(accept_stdin matches, json_printer);
110 let out = match matches.value_of("out") {
111 None => {
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 } else {
115 format!("{}.sbx", in_file)
116 }
117 },
118 Some(x) => {
119 if file_utils::check_if_file_is_dir(x) {
120 if file_utils::check_if_file_is_stdin(in_file) {
121 exit_with_msg!(usr json_printer => "Explicit output file name is required when input is stdin");
122 }
123
124 let in_file = file_utils::get_file_name_part_of_path(in_file);
125 misc_utils::make_path(&[x,
126 &format!("{}.sbx", in_file)])
127 } else {
128 String::from(x)
129 }
130 }
131 };
132
133 let hash_type = match matches.value_of("hash_type") {
134 None => multihash::HashType::SHA256,
135 Some(x) => match multihash::string_to_hash_type(x) {
136 Ok(x) => x,
137 Err(_) => exit_with_msg!(usr json_printer => "Invalid hash type")
138 }
139 };
140
141 let pr_verbosity_level = get_pr_verbosity_level!(matches, json_printer);
142
143 let meta_enabled = get_meta_enabled!(matches);
144
145 if matches.is_present("info_only") {
146 json_printer.print_open_bracket(Some("stats"), BracketType::Curly);
147
148 if file_utils::check_if_file_is_stdin(in_file) {
149 exit_with_msg!(usr json_printer => "No information is available for stdin input");
150 }
151
152 let in_file_meta = match file_utils::get_file_metadata(in_file) {
153 Ok(x) => x,
154 Err(_) => exit_with_msg!(usr json_printer => "Failed to get metadata of \"{}\"",
155 in_file)
156 };
157
158 let in_file_size = match file_utils::get_file_size(in_file) {
159 Ok(x) => x,
160 Err(_) => exit_with_msg!(usr json_printer => "Failed to get file size of \"{}\"",
161 in_file)
162 };
163
164 let in_file_mod_time = match in_file_meta.modified() {
165 Ok(t) => match t.duration_since(UNIX_EPOCH) {
166 Ok(t) => Some(t.as_secs() as i64),
167 Err(_) => None,
168 },
169 Err(_) => None
170 };
171
172 let in_file_mod_time_str = match in_file_mod_time {
173 None => "N/A".to_string(),
174 Some(x) => match (time_utils::i64_secs_to_date_time_string(x, time_utils::TimeMode::UTC),
175 time_utils::i64_secs_to_date_time_string(x, time_utils::TimeMode::Local)) {
176 (Some(u), Some(l)) => format!("{} (UTC) {} (Local)", u, l),
177 _ => "Invalid file modification time".to_string(),
178 }
179 };
180
181 let out_file_size =
182 file_utils::from_orig_file_size::calc_container_size(version,
183 Some(meta_enabled),
184 data_par_burst,
185 in_file_size);
186
187 if ver_uses_rs(version) {
188 print_maybe_json!(json_printer, "File name : {}", in_file);
189 print_maybe_json!(json_printer, "SBX container name : {}", out);
190 print_maybe_json!(json_printer, "SBX container version : {}", ver_to_usize(version));
191 print_maybe_json!(json_printer, "SBX container block size : {}", ver_to_block_size(version) => skip_quotes);
192 print_maybe_json!(json_printer, "SBX container data size : {}", ver_to_data_size(version) => skip_quotes);
193 print_maybe_json!(json_printer, "RS data shard count : {}", data_par_burst.unwrap().0 => skip_quotes);
194 print_maybe_json!(json_printer, "RS parity shard count : {}", data_par_burst.unwrap().1 => skip_quotes);
195 print_maybe_json!(json_printer, "Burst error resistance level : {}", data_par_burst.unwrap().2 => skip_quotes);
196 print_maybe_json!(json_printer, "File size : {}", in_file_size => skip_quotes);
197 print_maybe_json!(json_printer, "SBX container size : {}", out_file_size => skip_quotes);
198 print_maybe_json!(json_printer, "File modification time : {}", in_file_mod_time_str);
199 } else {
200 print_maybe_json!(json_printer, "File name : {}", in_file);
201 print_maybe_json!(json_printer, "SBX container name : {}", out);
202 print_maybe_json!(json_printer, "SBX container version : {}", ver_to_usize(version));
203 print_maybe_json!(json_printer, "SBX container block size : {}", ver_to_block_size(version) => skip_quotes);
204 print_maybe_json!(json_printer, "SBX container data size : {}", ver_to_data_size(version) => skip_quotes);
205 print_maybe_json!(json_printer, "File size : {}", in_file_size => skip_quotes);
206 print_maybe_json!(json_printer, "SBX container size : {}", out_file_size => skip_quotes);
207 print_maybe_json!(json_printer, "File modification time : {}", in_file_mod_time_str);
208 }
209
210 json_printer.print_close_bracket();
211
212 exit_with_msg!(ok json_printer => "")
213 } else {
214 exit_if_file!(exists &out
215 => matches.is_present("force")
216 => json_printer
217 => "File \"{}\" already exists", out);
218
219 let in_file =
220 if file_utils::check_if_file_is_stdin(in_file) {
221 None
222 } else {
223 Some(in_file)
224 };
225
226 let param = Param::new(version,
227 &uid,
228 data_par_burst,
229 meta_enabled,
230 &json_printer,
231 hash_type,
232 in_file,
233 &out,
234 pr_verbosity_level);
235 match encode_core::encode_file(¶m) {
236 Ok(s) => exit_with_msg!(ok json_printer => "{}", s),
237 Err(e) => exit_with_msg!(op json_printer => "{}", e)
238 }
239 }
240}