ethers_hardhat_rs/
cmds.rs

1use std::{
2    fs::{create_dir_all, remove_dir_all},
3    path::PathBuf,
4};
5
6use async_process::{Child, Command, ExitStatus, Stdio};
7use futures::{executor::block_on, io::BufReader, task::SpawnExt, AsyncBufReadExt, TryStreamExt};
8
9use crate::{
10    error::HardhatError,
11    utils::{thread_pool, HardhatCommand, HardhatCommandContext},
12};
13
14/// Hardhat network helper structure.
15///
16#[derive(Debug)]
17pub struct NetworkContext;
18
19#[async_trait::async_trait]
20impl HardhatCommandContext for NetworkContext {
21    fn init_command(_: PathBuf, c: &mut Command) -> anyhow::Result<()> {
22        c.arg("node").stdout(Stdio::piped()).stderr(Stdio::piped());
23
24        Ok(())
25    }
26
27    async fn start_command(child_process: &mut Child) -> anyhow::Result<()> {
28        let mut lines = BufReader::new(child_process.stdout.take().unwrap()).lines();
29
30        let mut waiting_started = false;
31
32        while let Some(line) = lines.try_next().await.expect("") {
33            if "Any funds sent to them on Mainnet or any other live network WILL BE LOST." == line {
34                log::info!("hardhat node started");
35                waiting_started = true;
36                break;
37            }
38        }
39
40        // Start hardhat network failed.
41        if !waiting_started {
42            let status = child_process.status().await?;
43
44            return Err(HardhatError::ChildProcess("npx hardhat node".to_string(), status).into());
45        }
46
47        thread_pool().spawn(async move {
48            while let Some(line) = lines.try_next().await.expect("") {
49                log::trace!(target:"hardhat node" ,"{}", line);
50            }
51        })?;
52
53        Ok(())
54    }
55}
56/// Command helper structure for hardhat network
57pub type HardhatNetwork = HardhatCommand<NetworkContext>;
58
59/// Create new hardhat project if target not exists
60#[derive(Debug)]
61pub struct NewProjectContext;
62
63#[async_trait::async_trait]
64impl HardhatCommandContext for NewProjectContext {
65    fn init_command(hardhat_root: PathBuf, _c: &mut Command) -> anyhow::Result<()> {
66        if hardhat_root.exists() {
67            Err(HardhatError::ProjectExists(hardhat_root.to_string_lossy().into_owned()).into())
68        } else {
69            log::debug!(
70                "create hardhat project root dir,{}",
71                hardhat_root.to_string_lossy()
72            );
73
74            create_dir_all(hardhat_root)?;
75
76            Ok(())
77        }
78    }
79}
80
81/// Command for creating new hardhat project .
82pub type HardhatNewProject = HardhatCommand<NewProjectContext>;
83
84/// Create new hardhat project, even if target path exists.
85///
86/// If target path exists, remove it first.
87#[derive(Debug)]
88pub struct ForceNewProjectContext;
89
90#[async_trait::async_trait]
91impl HardhatCommandContext for ForceNewProjectContext {
92    fn init_command(hardhat_root: PathBuf, _c: &mut Command) -> anyhow::Result<()> {
93        if hardhat_root.exists() {
94            remove_dir_all(hardhat_root.clone())?;
95        }
96
97        create_dir_all(hardhat_root)?;
98
99        Ok(())
100    }
101}
102
103/// Command for creating new hardhat project .
104pub type HardhatForceNewProject = HardhatCommand<ForceNewProjectContext>;
105
106#[derive(Debug)]
107pub struct BuildProjectContext;
108
109#[async_trait::async_trait]
110impl HardhatCommandContext for BuildProjectContext {
111    fn init_command(hardhat_root: PathBuf, c: &mut Command) -> anyhow::Result<()> {
112        log::debug!(
113            "try build hardhat project {}",
114            hardhat_root.to_string_lossy()
115        );
116
117        c.arg("compile");
118
119        Ok(())
120    }
121}
122
123/// Command for creating new hardhat project .
124pub type HardhatBuildProject = HardhatCommand<BuildProjectContext>;
125
126/// Helper fn to block run [`HardhatBuildProject`] command
127pub fn block_run_build() -> anyhow::Result<ExitStatus> {
128    block_on(async {
129        let mut command = HardhatBuildProject::new()?;
130
131        command.start().await?;
132
133        Ok(command.status().await?)
134    })
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[async_std::test]
142    async fn test_start_network() {
143        _ = pretty_env_logger::try_init();
144
145        let mut network =
146            HardhatNetwork::new().expect("Create default hardhat network helper command");
147
148        assert!(!network.is_started());
149
150        network.start().await.expect("Start hardhat network");
151    }
152
153    #[async_std::test]
154    async fn test_create_new_project() {
155        _ = pretty_env_logger::try_init();
156
157        let mut command =
158            HardhatForceNewProject::new().expect("Create default hardhat new project command");
159
160        assert!(!command.is_started());
161
162        command
163            .start()
164            .await
165            .expect("Start create new hardhat project");
166
167        assert!(command
168            .status()
169            .await
170            .expect("Create new project")
171            .success());
172
173        _ = HardhatNewProject::new().expect_err("Create new project in exists path, expect failed");
174    }
175}