cargo_e/
ext.rs

1//! High-level API for embedding cargo-e: unified target collection (builtins + plugins)
2//!
3//! Provides `ExtContext` which holds the parsed CLI and process manager,
4//! and a `collect_targets` method to enumerate both built-in and plugin targets.
5// Core types always available
6use crate::{
7    e_collect::collect_all_targets, e_processmanager::ProcessManager, e_target::CargoTarget, Cli,
8};
9// Plugin API loading and target collection (only when plugins are enabled)
10#[cfg(feature = "uses_plugins")]
11use crate::plugins::plugin_api::load_plugins;
12use anyhow::{anyhow, Result};
13#[cfg(feature = "uses_plugins")]
14use once_cell::sync::OnceCell;
15use std::{collections::HashSet, path::PathBuf, sync::Arc};
16
17/// Embedding context for cargo-e: CLI, process manager, and discovery directory.
18pub struct ExtContext {
19    /// Parsed CLI options (same flags as the `cargo-e` binary)
20    pub cli: Cli,
21    /// Shared process manager for plugin in-process execution
22    pub manager: Arc<ProcessManager>,
23    /// Working directory used for scanning plugins
24    pub cwd: PathBuf,
25    /// Lazily-loaded plugin instances
26    #[cfg(feature = "uses_plugins")]
27    plugins: OnceCell<Vec<Box<dyn crate::plugins::plugin_api::Plugin>>>,
28}
29
30impl ExtContext {
31    /// Create a new embedding context with a CLI and ProcessManager.
32    /// Does not perform any target collection yet.
33    /// Create a new ExtContext; plugin loading is deferred until first use.
34    pub fn new(cli: Cli, manager: Arc<ProcessManager>) -> Result<Self> {
35        let cwd = std::env::current_dir()?;
36        Ok(ExtContext {
37            cli,
38            manager,
39            cwd,
40            #[cfg(feature = "uses_plugins")]
41            plugins: OnceCell::new(),
42        })
43    }
44
45    /// Collect all targets: built-in examples/binaries/tests/benches and plugin targets.
46    /// Deduplicates by (name, kind, extended).
47    pub fn collect_targets(&self) -> Result<Vec<CargoTarget>> {
48        // 1. Built-in targets
49        let threads = std::thread::available_parallelism()
50            .map(|n| n.get())
51            .unwrap_or(4);
52        let mut all = collect_all_targets(
53            self.cli.manifest_path.clone(),
54            self.cli.workspace,
55            threads,
56            self.cli.json_all_targets,
57            false,
58        )
59        .map_err(|e| anyhow!("collect_all_targets failed: {}", e))?;
60        // 2. Plugin targets (load plugins only once)
61        #[cfg(feature = "uses_plugins")]
62        {
63            use crate::e_target::TargetKind;
64            use crate::e_target::TargetOrigin;
65            let plugins = self
66                .plugins
67                .get_or_try_init(|| load_plugins(&self.cli, self.manager.clone()))?;
68            for plugin in plugins.iter() {
69                if plugin.matches(&self.cwd) {
70                    let plugin_path = plugin
71                        .source()
72                        .map(PathBuf::from)
73                        .unwrap_or_else(|| self.cwd.clone());
74                    for pt in plugin.collect_targets(&self.cwd)? {
75                        let ct = if let Some(ct) = pt.cargo_target {
76                            ct
77                        } else {
78                            let reported = pt
79                                .metadata
80                                .as_ref()
81                                .map(PathBuf::from)
82                                .unwrap_or_else(|| self.cwd.clone());
83                            CargoTarget {
84                                name: pt.name.clone(),
85                                display_name: pt.name.clone(),
86                                manifest_path: self.cwd.clone(),
87                                kind: TargetKind::Plugin,
88                                extended: false,
89                                toml_specified: false,
90                                origin: Some(TargetOrigin::Plugin {
91                                    plugin_path: plugin_path.clone(),
92                                    reported,
93                                }),
94                            }
95                        };
96                        all.push(ct);
97                    }
98                }
99            }
100        }
101        // 3. Deduplicate
102        let mut seen = HashSet::new();
103        all.retain(|t| seen.insert((t.name.clone(), t.kind, t.extended)));
104        Ok(all)
105    }
106    /// Run a target (example, binary, or plugin-provided) using the shared ProcessManager.
107    /// Returns Ok(Some(status)) for built-in runs, or Ok(None) if no action was taken.
108    pub fn run_target(&self, target: &CargoTarget) -> Result<Option<std::process::ExitStatus>> {
109        // Plugin targets
110        #[cfg(feature = "uses_plugins")]
111        {
112            #[allow(unused_imports)]
113            use crate::e_target::{TargetKind, TargetOrigin};
114            if target.kind == TargetKind::Plugin {
115                // Find matching plugin (cached)
116                let plugins = self
117                    .plugins
118                    .get_or_try_init(|| load_plugins(&self.cli, self.manager.clone()))?;
119                for plugin in plugins.iter() {
120                    if let Some(crate::e_target::TargetOrigin::Plugin { plugin_path, .. }) =
121                        &target.origin
122                    {
123                        if plugin.source().map(PathBuf::from) == Some(plugin_path.clone()) {
124                            // Convert to plugin_api::Target
125                            let _plugin_target =
126                                crate::plugins::plugin_api::Target::from(target.clone());
127                            // Run in-process via plugin
128                            plugin.run_with_manager(self.manager.clone(), &self.cli, target)?;
129                            return Ok(None);
130                        }
131                    }
132                }
133                return Ok(None);
134            }
135        }
136        // Built-in via e_runner
137        crate::e_runner::run_example(self.manager.clone(), &self.cli, target)
138    }
139}