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