blkar_lib/
cli_encode.rs

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    // compute uid
84    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            // deal with RS related options
97            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(&param) {
236            Ok(s)  => exit_with_msg!(ok json_printer => "{}", s),
237            Err(e) => exit_with_msg!(op json_printer => "{}", e)
238        }
239    }
240}