blkar_lib/
cli_macros.rs

1#[macro_export]
2macro_rules! exit_with_msg {
3    (
4        ok $json_printer:expr => $($x:expr),*
5    ) => {{
6        print_at_output_channel!($json_printer.output_channel() => $($x),*);
7        print_field_if_json!($json_printer, "error : null");
8        $json_printer.print_close_bracket();
9        return 0;
10    }};
11    (
12        usr $json_printer:expr => $($x:expr),*
13    ) => {{
14        if $json_printer.json_enabled() {
15            print_json_field!($json_printer.output_channel() => "error", format!($($x),*), false, $json_printer.first_item());
16        } else {
17            println_at_output_channel!($json_printer.output_channel() => $($x),*);
18        }
19        $json_printer.print_close_bracket();
20        return 1;
21    }};
22    (
23        op $json_printer:expr => $($x:expr),*
24    ) => {{
25        if $json_printer.json_enabled() {
26            print_json_field!($json_printer.output_channel() => "error", format!($($x),*), false, $json_printer.first_item());
27        } else {
28            println_at_output_channel!($json_printer.output_channel() => $($x),*);
29        }
30        $json_printer.print_close_bracket();
31        return 2;
32    }}
33}
34
35macro_rules! exit_if_file {
36    (
37        exists $file:expr => $force_write:expr => $json_printer:expr => $($x:expr),*
38    ) => {{
39        use crate::file_utils;
40        if file_utils::check_if_file_exists($file)
41            && !$force_write
42        {
43            exit_with_msg!(usr $json_printer => $($x),*);
44        }
45    }};
46    (
47        does_not_exist $file:expr => $json_printer:expr => $($x:expr),*
48    ) => {{
49        use crate::file_utils;
50        if !file_utils::check_if_file_exists($file) {
51            exit_with_msg!(usr $json_printer => $($x),*);
52        }
53    }};
54    (
55        is_dir $file:expr => $json_printer:expr => $($x:expr),*
56    ) => {{
57        use crate::file_utils;
58        if file_utils::check_if_file_is_dir($file) {
59            exit_with_msg!(usr $json_printer => $($x),*);
60        }
61    }};
62    (
63        is_not_file $file:expr => $json_printer:expr => $($x:expr),*
64    ) => {{
65        use crate::file_utils;
66        if !file_utils::check_if_file_is_file($file) {
67            exit_with_msg!(usr $json_printer => $($x),*);
68        }
69    }};
70    (
71        has_no_file_name_part $file:expr => $json_printer:expr => $($x:expr),*
72    ) => {{
73        use crate::file_utils;
74        if let None = file_utils::get_file_name_part_of_path($file) {
75            exit_with_msg!(usr $json_printer => $($x),*);
76        }
77    }}
78}
79
80macro_rules! get_pr_verbosity_level {
81    (
82        $matches:expr, $json_printer:expr
83    ) => {{
84        use crate::progress_report;
85        use crate::progress_report::PRVerbosityLevel;
86
87        match $matches.value_of("pr_verbosity_level") {
88            None    => if get_json_enabled!($matches) { PRVerbosityLevel::L0 } else { PRVerbosityLevel::L2 },
89            Some(x) => match progress_report::string_to_verbosity_level(x) {
90                Ok(x)  => x,
91                Err(_) => exit_with_msg!(usr $json_printer => "Invalid progress report verbosity level")
92            }
93        }
94    }}
95}
96
97macro_rules! get_from_pos {
98    (
99        $matches:expr, $json_printer:expr
100    ) => {{
101        use std::str::FromStr;
102
103        match $matches.value_of("from_pos") {
104            None    => None,
105            Some(x) => match u64::from_str(x) {
106                Ok(x)  => Some(x),
107                Err(_) => exit_with_msg!(usr $json_printer => "Invalid from position")
108            }
109        }
110    }}
111}
112
113macro_rules! get_to_pos {
114    (
115        $matches:expr, $json_printer:expr
116    ) => {{
117        use std::str::FromStr;
118        use crate::misc_utils::RangeEnd;
119
120        match ($matches.value_of("to_pos_inc"), $matches.value_of("to_pos_exc")) {
121            (None,    None   ) => None,
122            (Some(x), None   ) =>
123                match u64::from_str(x) {
124                    Ok(x)  => Some(RangeEnd::Inc(x)),
125                    Err(_) => exit_with_msg!(usr $json_printer => "Invalid to inc position")
126                },
127            (None,    Some(x)) =>
128                match u64::from_str(x) {
129                    Ok(x)  => Some(RangeEnd::Exc(x)),
130                    Err(_) => exit_with_msg!(usr $json_printer => "Invalid to exc position")
131                },
132            (Some(_), Some(_)) =>
133                unreachable!(),
134        }
135    }}
136}
137
138macro_rules! get_guess_burst_from_pos {
139    (
140        $matches:expr, $json_printer:expr
141    ) => {{
142        use std::str::FromStr;
143
144        match $matches.value_of("guess_burst_from_pos") {
145            None    => None,
146            Some(x) => match u64::from_str(x) {
147                Ok(x)  => Some(x),
148                Err(_) => exit_with_msg!(usr $json_printer => "Invalid guess burst from position")
149            }
150        }
151    }}
152}
153
154macro_rules! get_ref_from_pos {
155    (
156        $matches:expr, $json_printer:expr
157    ) => {{
158        use std::str::FromStr;
159
160        match $matches.value_of("ref_from_pos") {
161            None    => None,
162            Some(x) => match u64::from_str(x) {
163                Ok(x)  => Some(x),
164                Err(_) => exit_with_msg!(usr $json_printer => "Invalid ref from position")
165            }
166        }
167    }}
168}
169
170macro_rules! get_ref_to_pos {
171    (
172        $matches:expr, $json_printer:expr
173    ) => {{
174        use std::str::FromStr;
175        use crate::misc_utils::RangeEnd;
176
177        match ($matches.value_of("ref_to_pos_inc"), $matches.value_of("ref_to_pos_exc")) {
178            (None,    None   ) => None,
179            (Some(x), None   ) =>
180                match u64::from_str(x) {
181                    Ok(x)  => Some(RangeEnd::Inc(x)),
182                    Err(_) => exit_with_msg!(usr $json_printer => "Invalid ref to inc position")
183                },
184            (None,    Some(x)) =>
185                match u64::from_str(x) {
186                    Ok(x)  => Some(RangeEnd::Exc(x)),
187                    Err(_) => exit_with_msg!(usr $json_printer => "Invalid ref to exc position")
188                },
189            (Some(_), Some(_)) =>
190                unreachable!()
191        }
192    }}
193}
194
195macro_rules! get_in_file {
196    (
197        $matches:expr, $json_printer:expr
198    ) => {{
199        let in_file  = $matches.value_of("in_file").unwrap();
200
201        exit_if_file!(does_not_exist in_file
202                      => $json_printer
203                      => "File \"{}\" does not exist", in_file);
204
205        exit_if_file!(has_no_file_name_part in_file
206                      => $json_printer
207                      => "File name \"{}\"does not have a file name component", in_file);
208
209        exit_if_file!(is_dir in_file
210                      => $json_printer
211                      => "File \"{}\" is a directory", in_file);
212
213        in_file
214    }};
215    (
216        accept_stdin $matches:expr, $json_printer:expr
217    ) => {{
218        use crate::file_utils;
219        let in_file  = $matches.value_of("in_file").unwrap();
220        if !file_utils::check_if_file_is_stdin(in_file) {
221            exit_if_file!(does_not_exist in_file
222                          => $json_printer
223                          => "File \"{}\" does not exist", in_file);
224
225            exit_if_file!(has_no_file_name_part in_file
226                          => $json_printer
227                          => "File name \"{}\"does not have a file name component", in_file);
228
229            exit_if_file!(is_dir in_file
230                          => $json_printer
231                          => "File \"{}\" is a directory", in_file);
232        }
233        in_file
234    }};
235}
236
237macro_rules! get_data_or_parity_shards {
238    (
239        data => $matches:expr, $version:expr, $json_printer:expr
240    ) => {{
241        get_data_or_parity_shards!("rs_data", "data", $matches, $version, $json_printer)
242    }};
243    (
244        parity => $matches:expr, $version:expr, $json_printer:expr
245    ) => {{
246        get_data_or_parity_shards!("rs_parity", "parity", $matches, $version, $json_printer)
247    }};
248    (
249        $match_val:expr, $data_or_par:expr, $matches:expr, $version:expr, $json_printer:expr
250    ) => {{
251        use crate::sbx_specs::ver_to_usize;
252
253        let ver_usize = ver_to_usize($version);
254
255        match $matches.value_of($match_val) {
256            None    => {
257                exit_with_msg!(usr $json_printer => "Reed-Solomon erasure code {} shard count must be specified for version {}", $data_or_par, ver_usize);
258            },
259            Some(x) => {
260                match usize::from_str(&x) {
261                    Ok(x)  => x,
262                    Err(_) => {
263                        exit_with_msg!(usr $json_printer => "Failed to parse Reed-Solomon erasure code {} shard count", $data_or_par);
264                    }
265                }
266            }
267        }
268    }}
269}
270
271macro_rules! get_data_shards {
272    (
273        $matches:expr, $version:expr, $json_printer:expr
274    ) => {{
275        get_data_or_parity_shards!(data => $matches, $version, $json_printer)
276    }}
277}
278
279macro_rules! get_parity_shards {
280    (
281        $matches:expr, $version:expr, $json_printer:expr
282    ) => {{
283        get_data_or_parity_shards!(parity => $matches, $version, $json_printer)
284    }}
285}
286
287macro_rules! get_ver_and_data_par_burst_w_defaults {
288    (
289        $matches:expr, $json_printer:expr
290    ) => {{
291        use crate::sbx_specs::string_to_ver;
292        use crate::encode_defaults;
293
294        match $matches.value_of("sbx_version") {
295            None => {
296                if let Some(_) = $matches.value_of("rs_data") {
297                    exit_with_msg!(usr $json_printer => "Please state the SBX version explicitly if you want to use a custom Reed-Solomon data shard count");
298                }
299                if let Some(_) = $matches.value_of("rs_parity") {
300                    exit_with_msg!(usr $json_printer => "Please state the SBX version explicitly if you want to use a custom Reed-Solomon parity shard count");
301                }
302                if let Some(_) = $matches.value_of("burst") {
303                    exit_with_msg!(usr $json_printer => "Please state the SBX version explicitly if you want to use a custom burst error resistance level");
304                }
305
306                (encode_defaults::VERSION, Some(encode_defaults::DATA_PAR_BURST))
307            },
308            Some(x) => {
309                let version =
310                    match string_to_ver(&x) {
311                        Ok(v)   => v,
312                        Err(()) => {
313                            exit_with_msg!(usr $json_printer => "Invalid SBX version");
314                        }
315                    };
316
317                let data_par_burst = if ver_uses_rs(version) {
318                    // deal with RS related options
319                    let data_shards = get_data_shards!($matches, version, $json_printer);
320                    let parity_shards = get_parity_shards!($matches, version, $json_printer);
321
322                    check_data_parity_shards!(data_shards, parity_shards, $json_printer);
323
324                    let burst = get_burst_or_zero!($matches, $json_printer);
325
326                    Some((data_shards, parity_shards, burst))
327                } else {
328                    None
329                };
330
331                (version, data_par_burst)
332            }
333        }
334    }}
335}
336
337macro_rules! check_data_parity_shards {
338    (
339        $data_shards:expr, $parity_shards:expr, $json_printer:expr
340    ) => {{
341        use reed_solomon_erasure::galois_8::ReedSolomon;
342        use reed_solomon_erasure::Error;
343
344        match ReedSolomon::new($data_shards, $parity_shards) {
345            Ok(_)                          => {},
346            Err(Error::TooFewDataShards)   => {
347                exit_with_msg!(usr $json_printer => "Too few data shards for Reed-Solomon erasure code");
348            },
349            Err(Error::TooFewParityShards) => {
350                exit_with_msg!(usr $json_printer => "Too few parity shards for Reed-Solomon erasure code");
351            },
352            Err(Error::TooManyShards)      => {
353                exit_with_msg!(usr $json_printer => "Too many shards for Reed-Solomon erasure code");
354            },
355            Err(_)                         => { panic!(); }
356        }
357    }}
358}
359
360macro_rules! get_burst_or_zero {
361    (
362        $matches:expr, $json_printer:expr
363    ) => {{
364        match $matches.value_of("burst") {
365            None    => 0,
366            Some(x) => {
367                match usize::from_str(x) {
368                    Ok(x)  => x,
369                    Err(_) => {
370                        exit_with_msg!(usr $json_printer => "Failed to parse burst error resistance level");
371                    }
372                }
373            }
374        }
375    }}
376}
377
378macro_rules! ask_if_wish_to_continue {
379    (
380    ) => {{
381        use std::io::{stdin, stdout, Read, Write};
382
383        print!("Do you wish to continue? [y/N] ");
384
385        stdout().flush().unwrap();
386
387        let mut ans: [u8; 1] = [0; 1];
388
389        let _ = stdin().read(&mut ans).unwrap();
390
391        if ans != *b"y" {
392            return 0;
393        }
394    }};
395}
396
397macro_rules! get_burst_opt {
398    (
399        $matches:expr, $json_printer:expr
400    ) => {{
401        use std::str::FromStr;
402
403        match $matches.value_of("burst") {
404            None    => None,
405            Some(x) => {
406                match usize::from_str(x) {
407                    Ok(x)  => Some(x),
408                    Err(_) => {
409                        exit_with_msg!(usr $json_printer => "Failed to parse burst error resistance level");
410                    }
411                }
412            }
413        }
414    }}
415}
416
417macro_rules! parse_uid {
418    (
419        $buf:expr, $uid:expr, $json_printer:expr
420    ) => {{
421        use crate::misc_utils::HexError;
422        use crate::misc_utils;
423        use crate::sbx_specs::SBX_FILE_UID_LEN;
424
425        match misc_utils::hex_string_to_bytes($uid) {
426            Ok(x) => {
427                if x.len() != SBX_FILE_UID_LEN {
428                    exit_with_msg!(usr $json_printer => "UID provided does not have the correct number of hex digits, provided : {}, need : {}",
429                                   $uid.len(),
430                                   SBX_FILE_UID_LEN * 2);
431                }
432
433                $buf.copy_from_slice(&x);
434            },
435            Err(HexError::InvalidHexString) => {
436                exit_with_msg!(usr $json_printer => "UID provided is not a valid hex string");
437            },
438            Err(HexError::InvalidLen) => {
439                exit_with_msg!(usr $json_printer => "UID provided does not have the correct number of hex digits, provided : {}, need : {}",
440                               $uid.len(),
441                               SBX_FILE_UID_LEN * 2);
442            }
443        }
444    }}
445}
446
447macro_rules! get_uid {
448    (
449        $matches:expr, $buf:expr, $json_printer:expr
450    ) => {{
451        match $matches.value_of("uid") {
452            None => None,
453            Some(uid) => {
454                parse_uid!($buf, uid, $json_printer);
455                Some(&$buf)
456            }
457        }
458    }};
459}
460
461macro_rules! get_ref_block_choice {
462    (
463        $matches:expr
464    ) => {{
465        use crate::block_utils::RefBlockChoice::*;
466        use crate::sbx_block::BlockType;
467
468        if $matches.is_present("no_meta") {
469            Any
470        } else {
471            Prefer(BlockType::Meta)
472        }
473    }};
474}
475
476macro_rules! get_meta_enabled {
477    (
478        $matches:expr
479    ) => {{
480        !$matches.is_present("no_meta")
481    }};
482}
483
484macro_rules! get_json_enabled {
485    (
486        $matches:expr
487    ) => {{
488        $matches.is_present("json")
489    }};
490}
491
492macro_rules! get_json_printer {
493    (
494        $matches:expr
495    ) => {{
496        use crate::json_printer::JSONPrinter;
497        use crate::output_channel::OutputChannel;
498        use std::sync::Arc;
499
500        Arc::new(JSONPrinter::new(
501            $matches.is_present("json"),
502            OutputChannel::Stdout,
503        ))
504    }};
505}
506
507#[macro_export]
508macro_rules! print_at_output_channel {
509    (
510        $channel:expr => $($x:expr),*
511    ) => {{
512        use crate::output_channel::OutputChannel;
513
514        match $channel {
515            OutputChannel::Stdout => print!($($x),*),
516            OutputChannel::Stderr => eprint!($($x),*),
517        }
518    }}
519}
520
521#[macro_export]
522macro_rules! println_at_output_channel {
523    (
524        $channel:expr => $($x:expr),*
525    ) => {{
526        use crate::output_channel::OutputChannel;
527
528        match $channel {
529            OutputChannel::Stdout => println!($($x),*),
530            OutputChannel::Stderr => eprintln!($($x),*),
531        }
532    }}
533}
534
535macro_rules! get_multi_pass {
536    (
537        $matches:expr, $json_printer:expr
538    ) => {{
539        use crate::misc_utils::MultiPassType;
540
541        match (
542            $matches.is_present("multi_pass"),
543            $matches.is_present("multi_pass_no_skip"),
544        ) {
545            (false, false) => None,
546            (true, false) => Some(MultiPassType::SkipGood),
547            (false, true) => Some(MultiPassType::OverwriteAll),
548            (true, true) => unreachable!(),
549        }
550    }};
551}