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