freenet_test_network/
binary.rs1use crate::{Error, Result};
2use std::path::{Path, PathBuf};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum BuildProfile {
7 Debug,
8 Release,
9}
10
11#[derive(Debug, Clone)]
13pub enum FreenetBinary {
14 CurrentCrate(BuildProfile),
16
17 Installed,
19
20 Path(PathBuf),
22
23 Workspace {
26 path: PathBuf,
27 profile: BuildProfile,
28 },
29}
30
31impl Default for FreenetBinary {
32 fn default() -> Self {
33 if is_in_freenet_workspace() {
36 Self::CurrentCrate(BuildProfile::Debug)
37 } else if which::which("freenet").is_ok() {
38 Self::Installed
39 } else {
40 Self::Installed }
42 }
43}
44
45impl FreenetBinary {
46 pub fn resolve(&self) -> Result<PathBuf> {
48 match self {
49 Self::CurrentCrate(profile) => {
50 tracing::info!(
51 "Building freenet binary from current workspace ({:?})",
52 profile
53 );
54 build_current_workspace(*profile)
55 }
56 Self::Installed => which::which("freenet").map_err(|_| {
57 Error::InvalidBinary(
58 "freenet binary not found in PATH. Install with: cargo install freenet".into(),
59 )
60 }),
61 Self::Path(p) => {
62 if !p.exists() {
63 return Err(Error::InvalidBinary(format!(
64 "Binary not found: {}",
65 p.display()
66 )));
67 }
68 Ok(p.clone())
69 }
70 Self::Workspace { path, profile } => {
71 tracing::info!(
72 "Building freenet binary from workspace: {} ({:?})",
73 path.display(),
74 profile
75 );
76 build_workspace(path, *profile)
77 }
78 }
79 }
80}
81
82fn is_in_freenet_workspace() -> bool {
83 std::env::current_dir()
84 .ok()
85 .and_then(|dir| {
86 dir.ancestors()
87 .find(|p| p.join("Cargo.toml").exists())
88 .and_then(|p| std::fs::read_to_string(p.join("Cargo.toml")).ok())
89 })
90 .map(|content| content.contains("name = \"freenet\""))
91 .unwrap_or(false)
92}
93
94fn build_current_workspace(profile: BuildProfile) -> Result<PathBuf> {
95 let cwd = std::env::current_dir()?;
98 let workspace_root = cwd
99 .ancestors()
100 .find(|p| {
101 let cargo_toml = p.join("Cargo.toml");
102 if !cargo_toml.exists() {
103 return false;
104 }
105 std::fs::read_to_string(&cargo_toml)
107 .map(|content| content.contains("[workspace]"))
108 .unwrap_or(false)
109 })
110 .or_else(|| {
111 cwd.ancestors().find(|p| {
113 let cargo_toml = p.join("Cargo.toml");
114 if !cargo_toml.exists() {
115 return false;
116 }
117 std::fs::read_to_string(&cargo_toml)
118 .map(|content| content.contains("name = \"freenet\""))
119 .unwrap_or(false)
120 })
121 })
122 .ok_or_else(|| {
123 Error::InvalidBinary("Could not find workspace root with freenet binary".into())
124 })?
125 .to_path_buf();
126
127 let profile_arg = match profile {
128 BuildProfile::Debug => vec!["build"],
129 BuildProfile::Release => vec!["build", "--release"],
130 };
131
132 let mut cmd = std::process::Command::new("cargo");
133 cmd.args(&profile_arg)
134 .args(["--bin", "freenet"])
135 .current_dir(&workspace_root);
136
137 let output = cmd.output()?;
138
139 if !output.status.success() {
140 return Err(Error::InvalidBinary(format!(
141 "Failed to build freenet: {}",
142 String::from_utf8_lossy(&output.stderr)
143 )));
144 }
145
146 let target_dir = if let Ok(target) = std::env::var("CARGO_TARGET_DIR") {
148 PathBuf::from(target)
149 } else {
150 workspace_root.join("target")
151 };
152
153 let profile_dir = match profile {
154 BuildProfile::Debug => "debug",
155 BuildProfile::Release => "release",
156 };
157 let binary = target_dir.join(format!("{}/freenet", profile_dir));
158
159 if !binary.exists() {
160 return Err(Error::InvalidBinary(format!(
161 "Built binary not found at: {} (workspace: {}, target_dir: {})",
162 binary.display(),
163 workspace_root.display(),
164 target_dir.display()
165 )));
166 }
167
168 Ok(binary)
169}
170
171fn build_workspace(workspace: &Path, profile: BuildProfile) -> Result<PathBuf> {
172 let profile_arg = match profile {
173 BuildProfile::Debug => vec!["build"],
174 BuildProfile::Release => vec!["build", "--release"],
175 };
176
177 let mut cmd = std::process::Command::new("cargo");
178 cmd.args(&profile_arg)
179 .args(["--bin", "freenet"])
180 .current_dir(workspace);
181
182 let output = cmd.output()?;
183
184 if !output.status.success() {
185 return Err(Error::InvalidBinary(format!(
186 "Failed to build freenet in {}: {}",
187 workspace.display(),
188 String::from_utf8_lossy(&output.stderr)
189 )));
190 }
191
192 let target_dir = workspace.join("target");
193 let profile_dir = match profile {
194 BuildProfile::Debug => "debug",
195 BuildProfile::Release => "release",
196 };
197 let binary = target_dir.join(format!("{}/freenet", profile_dir));
198
199 if !binary.exists() {
200 return Err(Error::InvalidBinary(format!(
201 "Built binary not found in target/{}",
202 profile_dir
203 )));
204 }
205
206 Ok(binary)
207}
208
209fn get_target_dir() -> Result<PathBuf> {
210 if let Ok(target) = std::env::var("CARGO_TARGET_DIR") {
212 return Ok(PathBuf::from(target));
213 }
214
215 std::env::current_dir()?
217 .ancestors()
218 .find(|p| p.join("Cargo.toml").exists())
219 .map(|p| p.join("target"))
220 .ok_or_else(|| Error::InvalidBinary("Could not find workspace root".into()))
221}