]> code.octet-stream.net Git - hashgood/blob - src/display.rs
Test on all OSes on push/PR
[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::Clipboard => {
74 writeln!(&mut stdout, "pasted from clipboard")?;
75 }
76 VerificationSource::RawFile(raw_path) => match raw_path.as_str() {
77 "-" => {
78 writeln!(&mut stdout, "from standard input")?;
79 }
80 path => {
81 writeln!(&mut stdout, "from file '{}' containing raw hash", path)?;
82 }
83 },
84 VerificationSource::DigestsFile(digest_path) => match digest_path.as_str() {
85 "-" => {
86 writeln!(
87 &mut stdout,
88 "'{}' from digests on standard input",
89 candidate_filename.as_ref().unwrap()
90 )?;
91 }
92 path => {
93 writeln!(
94 &mut stdout,
95 "'{}' in digests file '{}'",
96 candidate_filename.as_ref().unwrap(),
97 path
98 )?;
99 }
100 },
101 }
102 stdout.reset()?;
103 Ok(())
104 }
105
106 pub fn print_hash(
107 hash: &Hash,
108 verify_hash: Option<&CandidateHash>,
109 verify_source: Option<&VerificationSource>,
110 no_colour: bool,
111 ) -> PrintResult {
112 let mut stdout = get_stdout(no_colour);
113
114 write_filename(&mut stdout, &hash.filename)?;
115 write!(&mut stdout, " / ")?;
116 write_algorithm(&mut stdout, hash.alg)?;
117 writeln!(&mut stdout)?;
118
119 // Handle basic case first - nothing to compare it to
120 let hash_hex = hex::encode(&hash.bytes);
121 let verify_hash = match verify_hash {
122 None => {
123 write!(&mut stdout, "{}\n\n", hash_hex)?;
124 return Ok(());
125 }
126 Some(verify_hash) => verify_hash,
127 };
128 let other_hex = hex::encode(&verify_hash.bytes);
129
130 // Do a top-to-bottom comparison
131 print_hex_compare(&hash_hex, &other_hex, &mut stdout)?;
132 print_hex_compare(&other_hex, &hash_hex, &mut stdout)?;
133
134 // Show the source of our hash
135 if let Some(source) = verify_source {
136 write_source(&mut stdout, source, &verify_hash.filename)?;
137 }
138
139 writeln!(&mut stdout)?;
140 Ok(())
141 }
142
143 pub fn print_messages(messages: Vec<(MessageLevel, String)>, no_colour: bool) -> PrintResult {
144 let mut stdout = get_stdout(no_colour);
145
146 for (level, msg) in &messages {
147 match level {
148 MessageLevel::Error => {
149 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
150 write!(&mut stdout, "(error) ")?;
151 }
152 MessageLevel::Warning => {
153 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?;
154 write!(&mut stdout, "(warning) ")?;
155 }
156 MessageLevel::Note => {
157 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)))?;
158 write!(&mut stdout, "(note) ")?;
159 }
160 }
161 stdout.reset()?;
162 writeln!(&mut stdout, "{}", msg)?;
163 }
164 if !messages.is_empty() {
165 writeln!(&mut stdout)?
166 }
167
168 Ok(())
169 }
170
171 pub fn print_match_level(match_level: MatchLevel, no_colour: bool) -> PrintResult {
172 let mut stdout = get_stdout(no_colour);
173 write!(&mut stdout, "Result: ")?;
174 match match_level {
175 MatchLevel::Ok => {
176 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
177 writeln!(&mut stdout, "OK")?;
178 }
179 MatchLevel::Maybe => {
180 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?;
181 writeln!(&mut stdout, "MAYBE")?;
182 }
183 MatchLevel::Fail => {
184 stdout.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
185 writeln!(&mut stdout, "FAIL")?;
186 }
187 }
188 stdout.reset()?;
189 Ok(())
190 }