1use std::{
4 io::{BufRead, BufReader},
5 path::{Path, PathBuf},
6 process::{Child, Command, Stdio},
7 sync::{Arc, Mutex},
8 thread::spawn,
9};
10
11use crate::errors::{Error, Result};
12
13pub struct HardHatProject {
15 #[allow(unused)]
16 path: PathBuf,
18 network: Arc<Mutex<RawHardHatNetwork>>,
20}
21
22impl<P: AsRef<Path>> From<P> for HardHatProject {
23 fn from(value: P) -> Self {
24 Self {
25 path: value.as_ref().to_owned(),
26 network: Arc::new(Mutex::new(RawHardHatNetwork {
27 root_path: value.as_ref().to_owned(),
28 child_process: None,
29 })),
30 }
31 }
32}
33
34impl HardHatProject {
35 pub fn start_network(&self) -> Result<HardHatNetwork> {
39 {
40 let mut network = self.network.lock().unwrap();
41
42 network.restart()?;
43 }
44
45 Ok(HardHatNetwork {
46 inner: self.network.clone(),
47 })
48 }
49
50 pub fn compile(&self) -> Result<()> {
52 let mut child_process = Command::new("npx")
53 .arg("hardhat")
54 .arg("compile")
55 .current_dir(&self.path)
56 .stdout(Stdio::piped())
57 .spawn()?;
58
59 let stdout = child_process.stdout.take().unwrap();
60
61 let stdout_reader = BufReader::new(stdout);
62 let stdout_lines = stdout_reader.lines();
63
64 for line in stdout_lines {
65 if let Ok(line) = line {
66 println!("{}", line);
67 } else {
68 break;
69 }
70 }
71
72 let status = child_process.wait()?;
73
74 if !status.success() {
75 return Err(Error::Other(format!(
76 "hardhat compile exit with errorcode: {}",
77 status
78 )));
79 }
80
81 Ok(())
82 }
83}
84
85#[derive(Default)]
86struct RawHardHatNetwork {
87 root_path: PathBuf,
88 child_process: Option<Child>,
89}
90
91impl RawHardHatNetwork {
92 fn restart(&mut self) -> Result<()> {
93 self.stop()?;
94
95 log::trace!("start hardhat node in directory: {:#?}", self.root_path);
96
97 let mut child_process = Command::new("npx")
98 .arg("hardhat")
99 .arg("node")
100 .current_dir(&self.root_path)
101 .stdout(Stdio::piped())
102 .spawn()?;
103
104 let stdout = child_process.stdout.take().unwrap();
105
106 spawn(move || {
107 let stdout_reader = BufReader::new(stdout);
108 let stdout_lines = stdout_reader.lines();
109
110 log::trace!("spawn stdout lines");
111
112 for line in stdout_lines {
113 if let Ok(line) = line {
114 println!("{}", line);
115 } else {
116 break;
117 }
118 }
119 });
120
121 self.child_process = Some(child_process);
122
123 Ok(())
124 }
125
126 fn stop(&mut self) -> Result<()> {
127 if let Some(mut child_process) = self.child_process.take() {
128 child_process.kill()?;
129 child_process.wait()?;
130 }
131
132 Ok(())
133 }
134}
135
136pub struct HardHatNetwork {
137 inner: Arc<Mutex<RawHardHatNetwork>>,
138}
139
140impl HardHatNetwork {
141 pub fn restart(&self) -> Result<()> {
143 self.inner.lock().unwrap().restart()?;
144
145 Ok(())
146 }
147
148 pub fn stop(&self) -> Result<()> {
150 self.inner.lock().unwrap().stop()?;
151
152 Ok(())
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use std::{thread::sleep, time::Duration};
159
160 use super::*;
161
162 #[ignore]
163 #[test]
164 fn test_hardhat() {
165 pretty_env_logger::init();
166 let root_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/hardhat");
167
168 let project = HardHatProject::from(root_path);
169
170 let network = project.start_network().unwrap();
171
172 sleep(Duration::from_secs(2));
173
174 network.stop().unwrap();
175
176 project.compile().unwrap();
177 }
178}