1pub mod bin_resolver;
2pub mod builder;
3pub(crate) mod cache;
4pub mod cargo;
5pub mod cli;
6pub mod config;
7pub mod crate_resolver;
8pub mod cratespec;
9pub mod downloader;
10pub mod error;
11pub mod git;
12pub(crate) mod helpers;
13pub(crate) mod http;
14pub(crate) mod logging;
15pub mod messages;
16pub(crate) mod registry;
17pub mod runner;
18pub(crate) mod sbom;
19pub(crate) mod target;
20#[cfg(test)]
21pub(crate) mod testdata;
22
23use std::sync::Arc;
24
25use bin_resolver::BinaryResolver;
26use builder::{BuildOptions, BuildOverrides, CrateBuilder};
27use cache::Cache;
28pub use cargo_metadata::Target;
30use config::Config;
31use crate_resolver::CrateResolver;
32use cratespec::{CrateRequest, CrateSpec};
33use downloader::CrateDownloader;
34use error::Result;
35use http::HttpClient;
36
37pub struct Cgx {
45 config: Config,
46 resolver: Arc<dyn CrateResolver>,
47 bin_resolver: Arc<dyn BinaryResolver>,
48 downloader: Arc<dyn CrateDownloader>,
49 builder: Arc<dyn CrateBuilder>,
50 reporter: messages::MessageReporter,
51}
52
53impl Cgx {
54 pub fn new(config: Config, reporter: messages::MessageReporter) -> Result<Self> {
58 tracing::debug!("Using config: {:#?}", config);
59
60 let http_client = HttpClient::new(&config.http)?;
61
62 let cache = Cache::new(config.clone(), reporter.clone());
63 let git_client = git::GitClient::new(cache.clone(), reporter.clone(), config.http.clone());
64
65 let cargo_runner = Arc::new(cargo::create_cargo_runner(config.clone(), reporter.clone())?);
66
67 let resolver = Arc::new(crate_resolver::create_resolver(
68 config.clone(),
69 cache.clone(),
70 git_client.clone(),
71 cargo_runner.clone(),
72 http_client.clone(),
73 ));
74
75 let bin_resolver = Arc::new(bin_resolver::create_resolver(
76 config.clone(),
77 cache.clone(),
78 reporter.clone(),
79 http_client.clone(),
80 )?);
81
82 let downloader = Arc::new(downloader::create_downloader(
83 config.clone(),
84 cache.clone(),
85 git_client,
86 http_client,
87 ));
88
89 let builder = Arc::new(builder::create_builder(config.clone(), cache, cargo_runner));
90
91 Ok(Self {
92 config,
93 resolver,
94 bin_resolver,
95 downloader,
96 builder,
97 reporter,
98 })
99 }
100
101 pub fn prepare_bin_crate(
111 &self,
112 request: &CrateRequest,
113 overrides: &BuildOverrides,
114 ) -> Result<std::path::PathBuf> {
115 let (crate_spec, build_options) = self.resolve_crate_request(request, overrides)?;
116 let downloaded_crate = self.resolve_and_download_crate_spec(&crate_spec, &build_options)?;
117
118 tracing::debug!("Attempting to resolve pre-built binary");
120 if let Some(resolved_binary) = self.bin_resolver.resolve(&downloaded_crate, &build_options)? {
121 let provider = resolved_binary.provider;
122 tracing::info!(
123 "Found pre-built binary from {:?} at: {}",
124 provider,
125 resolved_binary.path.display()
126 );
127 self.reporter.report(|| {
128 messages::CgxMessage::crate_provenance_prebuilt(
129 &downloaded_crate.resolved,
130 &downloaded_crate.crate_path,
131 &build_options,
132 &resolved_binary,
133 )
134 });
135 return Ok(resolved_binary.path);
136 }
137
138 tracing::info!(
140 "Pre-built binary not found, excluded by config, or disabled; building crate from source..."
141 );
142
143 let (bin_path, target_binary) = self.builder.build(&downloaded_crate, &build_options)?;
144
145 tracing::info!("Built crate binary at: {}", bin_path.display());
146 self.reporter.report(|| {
147 messages::CgxMessage::crate_provenance_built_from_source(
148 &downloaded_crate.resolved,
149 &downloaded_crate.crate_path,
150 &build_options,
151 &bin_path,
152 target_binary,
153 )
154 });
155
156 Ok(bin_path)
157 }
158
159 #[expect(
166 clippy::type_complexity,
167 reason = "the returned 4-tuple is documented above and clearer here than a one-off named struct"
168 )]
169 pub fn list_targets(
170 &self,
171 request: &CrateRequest,
172 overrides: &BuildOverrides,
173 ) -> Result<(String, Option<Target>, Vec<Target>, Vec<Target>)> {
174 let (crate_spec, build_options) = self.resolve_crate_request(request, overrides)?;
175 let downloaded_crate = self.resolve_and_download_crate_spec(&crate_spec, &build_options)?;
176 let crate_name = downloaded_crate.resolved.name.clone();
177 let (default, bins, examples) = self.builder.list_targets(&downloaded_crate, &build_options)?;
178 Ok((crate_name, default, bins, examples))
179 }
180
181 pub fn list_configured_tools(&self) -> Result<String> {
184 for (name, tool_config) in self.config.sorted_tools() {
186 self.reporter
187 .report(|| messages::RunnerMessage::list_tool(name, tool_config));
188 }
189 for (name, target) in self.config.sorted_aliases() {
190 self.reporter
191 .report(|| messages::RunnerMessage::list_alias(name, target));
192 }
193
194 self.config.tools_toml()
195 }
196
197 pub fn prefetch(&self, label: &str, request: &CrateRequest, overrides: &BuildOverrides) -> Result<()> {
205 self.reporter
206 .report(|| messages::RunnerMessage::prefetch_started(label));
207
208 let bin_path = self.prepare_bin_crate(request, overrides)?;
209
210 self.reporter
211 .report(|| messages::RunnerMessage::prefetch_completed(label, &bin_path));
212
213 Ok(())
214 }
215
216 pub fn prefetch_all(&self, overrides: &BuildOverrides) -> Result<()> {
223 let mut failures = Vec::new();
224
225 for tool in self.config.configured_tools() {
226 self.reporter
227 .report(|| messages::RunnerMessage::prefetch_all_started(&tool.name, &tool.aliases));
228
229 let request = CrateRequest::for_configured_tool(&tool.name);
230 match self.prepare_bin_crate(&request, overrides) {
231 Ok(bin_path) => {
232 self.reporter.report(|| {
233 messages::RunnerMessage::prefetch_all_completed(&tool.name, &tool.aliases, &bin_path)
234 });
235 }
236 Err(err) => {
237 self.reporter.report(|| {
238 messages::RunnerMessage::prefetch_all_failed(&tool.name, &tool.aliases, &err)
239 });
240 failures.push(format!("{}: {}", tool.name, err));
241 }
242 }
243 }
244
245 if failures.is_empty() {
246 Ok(())
247 } else {
248 error::PrefetchAllFailedSnafu { failures }.fail()
249 }
250 }
251
252 fn resolve_crate_request(
257 &self,
258 request: &CrateRequest,
259 overrides: &BuildOverrides,
260 ) -> Result<(CrateSpec, BuildOptions)> {
261 let crate_spec = CrateSpec::load(&self.config, request)?;
262 let build_options = BuildOptions::load_for_crate(&self.config, overrides, &crate_spec)?;
263 Ok((crate_spec, build_options))
264 }
265
266 fn resolve_and_download_crate_spec(
272 &self,
273 crate_spec: &CrateSpec,
274 build_options: &BuildOptions,
275 ) -> Result<downloader::DownloadedCrate> {
276 tracing::debug!("Got crate spec: {:?}", crate_spec);
277
278 tracing::info!("Resolving crate...");
279 let resolved_crate = self.resolver.resolve(crate_spec)?;
280 tracing::info!(
281 "Resolved crate {}@{}",
282 resolved_crate.name,
283 resolved_crate.version
284 );
285
286 let downloaded_crate = self.downloader.download(resolved_crate)?;
287 tracing::debug!("Downloaded crate to cache: {:#?}", downloaded_crate);
288
289 self.reporter.report(|| {
290 messages::CgxMessage::crate_plan(
291 &downloaded_crate.resolved,
292 &downloaded_crate.crate_path,
293 build_options,
294 )
295 });
296
297 Ok(downloaded_crate)
298 }
299}