blueprint_chain_setup_tangle/
foundry.rs1use blueprint_std::{
2 collections::VecDeque,
3 io::{BufRead, BufReader},
4 process::{Command, Stdio},
5 sync::mpsc,
6 thread,
7};
8use color_eyre::eyre::Result;
9use dialoguer::console::style;
10use indicatif::{ProgressBar, ProgressStyle};
11
12pub struct FoundryToolchain {
13 pub forge: Forge,
14}
15
16trait CommandInstalled {
18 fn is_installed(&self) -> bool;
20}
21
22impl CommandInstalled for Command {
23 fn is_installed(&self) -> bool {
24 let cmd = self.get_program();
25 if cfg!(target_os = "windows") {
26 Command::new("where")
27 .arg(cmd)
28 .output()
29 .is_ok_and(|v| v.status.success())
30 } else {
31 Command::new("which")
32 .arg(cmd)
33 .output()
34 .is_ok_and(|v| v.status.success())
35 }
36 }
37}
38
39impl Default for FoundryToolchain {
40 fn default() -> Self {
41 Self::new()
42 }
43}
44
45impl FoundryToolchain {
46 pub fn new() -> Self {
47 Self {
48 forge: Forge::new(),
49 }
50 }
51 pub fn check_installed_or_exit(&self) {
52 fn foundry_installation_instructions() {
53 eprintln!("Please install Foundry, follow https://getfoundry.sh/ for instructions.");
54 }
55 if !self.forge.is_installed() {
56 eprintln!("Forge is not installed.");
57 foundry_installation_instructions();
58 }
59 }
60}
61
62pub struct Forge {
63 pub default_template: String,
65 pub default_commit: String,
67}
68
69impl Default for Forge {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl Forge {
76 pub fn new() -> Self {
78 Self {
79 default_template: "foundry-rs/forge-template".to_string(),
80 default_commit: "main".to_string(),
81 }
82 }
83
84 pub fn version(&self) -> Result<String> {
86 let output = Command::new("forge").arg("--version").output()?;
87 Ok(String::from_utf8(output.stdout)?
88 .trim()
89 .replace("forge", ""))
90 }
91
92 pub fn is_installed(&self) -> bool {
94 Command::new("forge").is_installed()
95 }
96
97 pub fn install_dependencies(&self) -> Result<()> {
100 let status = Command::new("forge")
102 .args(["soldeer", "update", "-d"])
103 .stdout(Stdio::inherit())
104 .stderr(Stdio::inherit())
105 .status()?;
106
107 if !status.success() {
108 return Err(color_eyre::eyre::eyre!(
109 "Failed to install dependencies. Check the output above for details."
110 ));
111 }
112
113 Ok(())
114 }
115
116 pub fn build(&self) -> Result<()> {
118 let spinner = ProgressBar::new_spinner();
119 spinner.set_style(
120 ProgressStyle::default_spinner()
121 .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈")
122 .template("{spinner:.green} {msg}")
123 .unwrap(),
124 );
125
126 spinner.set_message("Building contracts...");
127
128 let mut child = Command::new("forge")
129 .arg("build")
130 .stdout(Stdio::piped())
131 .stderr(Stdio::piped())
132 .spawn()?;
133
134 let stdout = child.stdout.take().expect("Failed to capture stdout");
135 let stderr = child.stderr.take().expect("Failed to capture stderr");
136
137 let stdout_reader = BufReader::new(stdout);
139 let stderr_reader = BufReader::new(stderr);
140
141 let (tx, rx) = mpsc::channel();
143 let tx_stderr = tx.clone();
144
145 let mut output_buffer = VecDeque::with_capacity(100);
147
148 thread::spawn(move || {
150 for line in stdout_reader.lines().map_while(Result::ok) {
151 let _ = tx.send(line);
152 }
153 });
154
155 thread::spawn(move || {
157 for line in stderr_reader.lines().map_while(Result::ok) {
158 let _ = tx_stderr.send(line);
159 }
160 });
161
162 while child.try_wait()?.is_none() {
164 spinner.tick();
165
166 while let Ok(line) = rx.try_recv() {
168 if !output_buffer.contains(&line) {
170 output_buffer.push_back(line);
171
172 if output_buffer.len() > 100 {
174 output_buffer.pop_front();
175 }
176
177 spinner.suspend(|| {
179 println!("{}", style(&output_buffer[output_buffer.len() - 1]).dim());
180 });
181 }
182 }
183
184 thread::sleep(std::time::Duration::from_millis(100));
185 }
186
187 let status = child.wait()?;
188
189 if !status.success() {
190 spinner.finish_with_message("❌ Failed to build contracts");
191 return Err(color_eyre::eyre::eyre!(
192 "Failed to build contracts. Check the output above for details."
193 ));
194 }
195
196 spinner.finish_with_message("✨ Contracts built successfully!");
197 Ok(())
198 }
199}