1use std::path::Path;
2
3use super::file_io::read_from_file;
4use clap::Parser;
5use tokio::io;
6
7#[derive(Parser, Debug)]
9#[command(version, about, long_about = None)]
10pub struct Cli {
11 #[arg(short, long)]
13 path: Option<String>,
14
15 #[arg(short, long)]
17 code: Option<String>,
18
19 #[arg(short, long)]
21 out: Option<String>,
22}
23
24pub async fn get_app_args(cli: Cli) -> io::Result<(String, String)> {
25 let input: String;
26
27 if let Some(code) = cli.code {
28 input = code;
29 } else if let Some(path) = cli.path {
30 let file_path = Path::new(&path).canonicalize()?;
31 input = read_from_file(&file_path).await?;
32 } else {
33 return Err(io::Error::new(
34 io::ErrorKind::NotFound,
35 "No input or destination provided",
36 ));
37 }
38
39 return Ok((input, cli.out.unwrap_or_else(|| "".to_string())));
40}
41
42#[cfg(test)]
43mod tests {
44 use std::ffi::OsString;
45
46 use tokio::fs;
47
48 use super::*;
49
50 const PSEUDO_PATH: &str = "./temp/Test.sol";
51 const PSEUDO_CODE: &str = "contract Test {\
52 bool test = true;
53 }";
54 const PSEUDO_OUT: &str = "./temp/test_out.sol";
55
56 async fn create_temp_file(dir_name: &str, file_name: &str) -> io::Result<()> {
57 let file_path = Path::new(dir_name).join(file_name);
58
59 if fs::try_exists(dir_name).await? || fs::try_exists(&file_path).await? {
60 return Ok(());
61 }
62
63 fs::create_dir(dir_name).await?;
64
65 {
66 fs::File::create(&file_path).await?;
67 fs::write(file_path, PSEUDO_CODE).await?;
68 }
69
70 Ok(())
71 }
72
73 async fn remove_temp_file(dir_name: &str, file_name: &str) -> io::Result<()> {
74 let file_path = Path::new(dir_name).join(file_name);
75
76 fs::remove_file(file_path).await?;
77 fs::remove_dir(dir_name).await?;
78
79 Ok(())
80 }
81
82 pub fn create_mock_cli<I, T>(itr: I) -> Result<Cli, Box<dyn std::error::Error>>
83 where
84 I: IntoIterator<Item = T>,
85 T: Into<OsString> + Clone,
86 {
87 return Ok(Cli::try_parse_from(itr)?);
88 }
89
90 #[test]
91 fn should_get_args() -> Result<(), Box<dyn std::error::Error>> {
92 let cli = create_mock_cli(&["pygmalion", "--path", PSEUDO_PATH])?;
93 assert_eq!(cli.path, Some(PSEUDO_PATH.to_string()));
94
95 Ok(())
96 }
97
98 #[tokio::test]
99 async fn should_get_app_args_code_only() -> Result<(), Box<dyn std::error::Error>> {
100 let cli = create_mock_cli(&["pygmalion", "--code", PSEUDO_CODE, "--out", PSEUDO_OUT])?;
101 let (input, out) = get_app_args(cli).await?;
102
103 assert_eq!(input, PSEUDO_CODE);
104 assert_eq!(out, PSEUDO_OUT);
105
106 Ok(())
107 }
108
109 #[tokio::test]
110 async fn should_get_app_args_path_only() -> Result<(), Box<dyn std::error::Error>> {
111 let dir_name = "temp";
112 let file_name = "Test.sol";
113 create_temp_file(dir_name, file_name).await?;
114 let cli = create_mock_cli(&["pygmalion", "--path", PSEUDO_PATH])?;
115 let (input, _out) = get_app_args(cli).await?;
116
117 assert_eq!(input, PSEUDO_CODE);
118
119 remove_temp_file(dir_name, file_name).await?;
120
121 Ok(())
122 }
123
124 #[tokio::test]
125 async fn should_panic_for_code_and_path() -> Result<(), Box<dyn std::error::Error>> {
126 let cli = create_mock_cli(&["pygmalion"])?;
127 let e = get_app_args(cli).await.unwrap_err();
128
129 assert_eq!(e.kind(), io::ErrorKind::NotFound);
130
131 Ok(())
132 }
133}