string_inspector/
cli.rs

1//! Functions for parsing command line input and displaying output.
2use colored::*;
3use std::io;
4use std::io::Read;
5use std::os::unix::ffi::OsStringExt;
6use encoding::types::EncodingRef;
7use clap::{Arg, App};
8use crate::decoding::DecodedString;
9
10const LABEL_SIZE: u16 = 7; // "bytes: / chars:" labels
11
12pub fn parse_command_line() -> (Vec<EncodingRef>, Vec<u8>) {
13    let matches = App::new("string-inspector")
14                          .version("0.0.1")
15                          .about("Inspects unicode strings")
16                          .arg(Arg::with_name("text")
17                              .index(1)
18                              .multiple(true))
19                          .arg(Arg::with_name("encoding")
20                               .short("e")
21                               .long("encoding")
22                               .value_name("ENCODING")
23                               .multiple(true)
24                               .number_of_values(1)
25                               .possible_values(&["utf8", "latin1"])
26                               .default_value("utf8")
27                               .help("Encoding to include in the output")
28                               .takes_value(true))
29
30                          .get_matches();
31
32        let encodings: Vec<EncodingRef> = matches.values_of("encoding").unwrap()
33        .map(|e| encoding::label::encoding_from_whatwg_label(e).unwrap())
34        .collect();
35
36    let text = matches.values_of_os("text");
37
38    let buffer = match text {
39        Some(args) => {
40            let mut arg_bytes: Vec<Vec<u8>> = Vec::new();
41            for arg in args {
42                arg_bytes.push(arg.to_owned().into_vec());
43            }
44
45            arg_bytes.join(&0x20)
46        }
47        None => {
48            eprintln!("No arguments passed to program: reading text from standard input...");
49            let mut result: Vec<u8> = Vec::new();
50            io::stdin().read_to_end(&mut result).expect("Unable to read from stdin");
51            result
52        }
53    };
54
55    (encodings, buffer)
56}
57
58pub fn display_decoding(decoding: &DecodedString, max_line_width: usize) {
59    println!("[{}]", decoding.encoding.name());
60
61    let chunks = decoding.wrap_lines(max_line_width - LABEL_SIZE as usize);
62    let mut first = true;
63
64    for chunk in chunks.iter() {
65        if first {
66            first = false;
67        } else {
68            println!("");
69        }
70
71        print!("bytes: ");
72        println!("{}", chunk.format_bytes());
73
74        print!("chars: ");
75        println!("{}", chunk.format_characters());
76    }
77
78    println!("");
79    println!("{}", highlight_non_ascii(&decoding.to_string()));
80}
81
82pub fn display_decodings(decodings: &Vec<DecodedString>, max_line_width: usize) {
83    let mut first = true;
84    for decoded_string in decodings.iter() {
85        if first {
86            first = false;
87        } else {
88            println!("");
89        }
90        display_decoding(&decoded_string, max_line_width);
91    }
92}
93
94fn highlight_non_ascii(input: &str) -> String {
95    let mut output = String::new();
96
97    for character in input.chars() {
98        if character.is_ascii() {
99            output.push_str(&character.to_string().green().to_string());
100        } else {
101            output.push_str(&character.to_string().red().to_string());
102        }
103    }
104
105    output
106}