1use std::{
4 fs::read,
5 io::{self, BufRead},
6 path::Path,
7};
8
9forc_util::cli_examples! {
10 crate::Command {
11 [ Hashes an argument with SHA256 => "forc crypto sha256 test" ]
12 [ Hashes an argument with Keccak256 => "forc crypto keccak256 test" ]
13 [ Hashes a file path with SHA256 => "forc crypto sha256 {file}" ]
14 [ Hashes a file path with Keccak256 => "forc crypto keccak256 {file}" ]
15 }
16}
17
18#[derive(Debug, Clone, clap::Args)]
19#[clap(
20 version,
21 about = "Hashes the argument or file with this algorithm",
22 after_help = help(),
23)]
24pub struct HashArgs {
25 content_or_filepath: Option<String>,
39}
40
41fn checked_read_file<P: AsRef<Path>>(path: &Option<P>) -> Option<Vec<u8>> {
42 path.as_ref().map(read)?.ok()
43}
44
45fn checked_read_stdin<R: BufRead>(content: &Option<String>, mut stdin: R) -> Option<Vec<u8>> {
46 match content.as_ref().map(|x| x.as_str()) {
47 Some("-") | None => {
48 let mut buffer = Vec::new();
49 if stdin.read_to_end(&mut buffer).is_ok() {
50 Some(buffer)
51 } else {
52 Some(vec![])
53 }
54 }
55 _ => None,
56 }
57}
58
59fn read_as_binary(content: &Option<String>) -> Vec<u8> {
60 content
61 .as_ref()
62 .map(|x| {
63 if let Some(hex) = x.trim().strip_prefix("0x") {
64 if let Ok(bin) = hex::decode(hex) {
65 bin
66 } else {
67 x.as_bytes().to_vec()
68 }
69 } else {
70 x.as_bytes().to_vec()
71 }
72 })
73 .unwrap_or_default()
74}
75
76pub fn read_content_filepath_or_stdin(arg: Option<String>) -> Vec<u8> {
86 match checked_read_file(&arg) {
87 Some(bytes) => bytes,
88 None => match checked_read_stdin(&arg, io::stdin().lock()) {
89 Some(bytes) => bytes,
90 None => read_as_binary(&arg),
91 },
92 }
93}
94
95impl From<HashArgs> for Vec<u8> {
101 fn from(value: HashArgs) -> Self {
102 read_content_filepath_or_stdin(value.content_or_filepath)
103 }
104}
105
106#[cfg(test)]
107mod test {
108 use super::*;
109
110 #[test]
111 fn test_checked_read_file() {
112 assert!(checked_read_file(&Some("not a file")).is_none());
113 assert!(checked_read_file(&Some("Cargo.toml")).is_some());
114 assert!(checked_read_file::<String>(&None).is_none());
115 }
116
117 #[test]
118 fn test_checked_stdin() {
119 let stdin = b"I'm a test from stdin";
120 assert_eq!(
121 None,
122 checked_read_stdin(&Some("value".to_owned()), &stdin[..])
123 );
124 assert_eq!(
125 Some(b"I'm a test from stdin".to_vec()),
126 checked_read_stdin(&None, &stdin[..])
127 );
128 assert_eq!(
129 Some(b"I'm a test from stdin".to_vec()),
130 checked_read_stdin(&Some("-".to_owned()), &stdin[..])
131 );
132 assert_eq!(None, checked_read_stdin(&Some("".to_owned()), &stdin[..]));
133 }
134
135 #[test]
136 fn test_read_binary() {
137 let x = " 0xff";
138 assert_eq!(vec![255u8], read_as_binary(&Some(x.to_owned())));
139 let x = "0xFF";
140 assert_eq!(vec![255u8], read_as_binary(&Some(x.to_owned())));
141 let x = " 0xFf";
142 assert_eq!(vec![255u8], read_as_binary(&Some(x.to_owned())));
143 let x = " 0xFfx";
144 assert_eq!(b" 0xFfx".to_vec(), read_as_binary(&Some(x.to_owned())));
145 let x = " some random data\n\n\0";
146 assert_eq!(
147 b" some random data\n\n\0".to_vec(),
148 read_as_binary(&Some(x.to_owned()))
149 );
150 }
151}