]> code.octet-stream.net Git - hashgood/blob - src/display.rs
Add support for SHA512 and make colours brighter
[hashgood] / src / display.rs
1 use super::{Algorithm, CandidateHash, Hash, MatchLevel, MessageLevel, VerificationSource};
2 use std::error::Error;
3 use std::io::Write;
4 use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
5
6 pub type PrintResult = Result<(), Box<dyn Error>>;
7
8 fn filename_display(filename: &str) -> &str {
9 if filename == "-" {
10 return "standard input";
11 }
12 filename
13 }
14
15 fn get_stdout(no_colour: bool) -> StandardStream {
16 if no_colour {
17 StandardStream::stdout(ColorChoice::Never)
18 } else {
19 StandardStream::stdout(ColorChoice::Always)
20 }
21 }
22
23 fn write_filename(mut stdout: &mut StandardStream, filename: &str) -> PrintResult {
24 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)).set_bold(true))?;
25 write!(&mut stdout, "{}", filename_display(filename))?;
26 stdout.reset()?;
27 Ok(())
28 }
29
30 fn write_algorithm(mut stdout: &mut StandardStream, alg: Algorithm) -> PrintResult {
31 match alg {
32 Algorithm::Md5 => {
33 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Magenta)).set_bold(true))?;
34 write!(&mut stdout, "MD5")?;
35 }
36 Algorithm::Sha1 => {
37 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)).set_bold(true))?;
38 write!(&mut stdout, "SHA-1")?;
39 }
40 Algorithm::Sha256 => {
41 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
42 write!(&mut stdout, "SHA-256")?;
43 }
44 Algorithm::Sha512 => {
45 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Blue)).set_bold(true))?;
46 write!(&mut stdout, "SHA-512")?;
47 }
48 }
49 stdout.reset()?;
50 Ok(())
51 }
52
53 fn print_hex_compare(print: &str, against: &str, mut stdout: &mut StandardStream) -> PrintResult {
54 for (p, a) in print.chars().zip(against.chars()) {
55 if p == a {
56 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
57 } else {
58 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))?;
59 }
60 write!(&mut stdout, "{}", p)?;
61 }
62 stdout.reset()?;
63 writeln!(&mut stdout)?;
64 Ok(())
65 }
66
67 fn write_source(
68 mut stdout: &mut StandardStream,
69 verify_source: &VerificationSource,
70 candidate_filename: &Option<String>,
71 ) -> PrintResult {
72 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)).set_bold(true))?;
73 match &verify_source {
74 VerificationSource::CommandArgument => {
75 writeln!(&mut stdout, "command line argument")?;
76 }
77 VerificationSource::RawFile(raw_path) => match raw_path.as_str() {
78 "-" => {
79 writeln!(&mut stdout, "from standard input")?;
80 }
81 path => {
82 writeln!(&mut stdout, "from file '{}' containing raw hash", path)?;
83 }
84 },
85 VerificationSource::DigestsFile(digest_path) => match digest_path.as_str() {
86 "-" => {
87 writeln!(
88 &mut stdout,
89 "'{}' from digests on standard input",
90 candidate_filename.as_ref().unwrap()
91 )?;
92 }
93 path => {
94 writeln!(
95 &mut stdout,
96 "'{}' in digests file '{}'",
97 candidate_filename.as_ref().unwrap(),
98 path
99 )?;
100 }
101 },
102 }
103 stdout.reset()?;
104 Ok(())
105 }
106
107 pub fn print_hash(
108 hash: &Hash,
109 verify_hash: Option<&CandidateHash>,
110 verify_source: Option<&VerificationSource>,
111 no_colour: bool,
112 ) -> PrintResult {
113 let mut stdout = get_stdout(no_colour);
114
115 write_filename(&mut stdout, &hash.filename)?;
116 write!(&mut stdout, " / ")?;
117 write_algorithm(&mut stdout, hash.alg)?;
118 writeln!(&mut stdout)?;
119
120 // Handle basic case first - nothing to compare it to
121 let hash_hex = hex::encode(&hash.bytes);
122 let verify_hash = match verify_hash {
123 None => {
124 write!(&mut stdout, "{}\n\n", hash_hex)?;
125 return Ok(());
126 }
127 Some(verify_hash) => verify_hash,
128 };
129 let other_hex = hex::encode(&verify_hash.bytes);
130
131 // Do a top-to-bottom comparison
132 print_hex_compare(&hash_hex, &other_hex, &mut stdout)?;
133 print_hex_compare(&other_hex, &hash_hex, &mut stdout)?;
134
135 // Show the source of our hash
136 if let Some(source) = verify_source {
137 write_source(&mut stdout, source, &verify_hash.filename)?;
138 }
139
140 writeln!(&mut stdout)?;
141 Ok(())
142 }
143
144 pub fn print_messages(messages: Vec<(MessageLevel, String)>, no_colour: bool) -> PrintResult {
145 let mut stdout = get_stdout(no_colour);
146
147 for (level, msg) in &messages {
148 match level {
149 MessageLevel::Error => {
150 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
151 write!(&mut stdout, "(error) ")?;
152 }
153 MessageLevel::Warning => {
154 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?;
155 write!(&mut stdout, "(warning) ")?;
156 }
157 MessageLevel::Note => {
158 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)))?;
159 write!(&mut stdout, "(note) ")?;
160 }
161 }
162 stdout.reset()?;
163 writeln!(&mut stdout, "{}", msg)?;
164 }
165 if !messages.is_empty() {
166 writeln!(&mut stdout)?
167 }
168
169 Ok(())
170 }
171
172 pub fn print_match_level(match_level: MatchLevel, no_colour: bool) -> PrintResult {
173 let mut stdout = get_stdout(no_colour);
174 write!(&mut stdout, "Result: ")?;
175 match match_level {
176 MatchLevel::Ok => {
177 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
178 writeln!(&mut stdout, "OK")?;
179 }
180 MatchLevel::Maybe => {
181 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)).set_bold(true))?;
182 writeln!(&mut stdout, "MAYBE")?;
183 }
184 MatchLevel::Fail => {
185 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))?;
186 writeln!(&mut stdout, "FAIL")?;
187 }
188 }
189 stdout.reset()?;
190 Ok(())
191 }