Skip to main content

cgx_core/messages/
runner.rs

1use std::{ffi::OsString, path::PathBuf};
2
3use serde::{Deserialize, Serialize};
4
5use super::Message;
6use crate::config::ToolConfig;
7
8/// Messages related to binary execution.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10#[serde(tag = "event", rename_all = "snake_case")]
11pub enum RunnerMessage {
12    /// The resolved binary and arguments cgx is about to execute, or would execute under
13    /// `--no-exec`.
14    ExecutionPlan {
15        /// Path to the resolved binary.
16        binary_path: PathBuf,
17        /// Arguments forwarded to the binary, lossily converted to UTF-8 for serialization.
18        args: Vec<String>,
19        /// True when `--no-exec` was given, so the plan is reported without being executed.
20        no_exec: bool,
21    },
22    /// One configured `[tools]` entry, reported by `--list-tools`.
23    ListTool {
24        /// The configured tool (crate) name.
25        name: String,
26        /// The tool's merged configuration.
27        config: ToolConfig,
28    },
29    /// One configured `[aliases]` entry, reported by `--list-tools`.
30    ListAlias {
31        /// The alias name.
32        name: String,
33        /// The tool name the alias resolves to.
34        target: String,
35    },
36    /// `--prefetch` started preparing a crate.
37    PrefetchStarted {
38        /// The crate spec as requested on the command line (a crate name, `name@version`, or an
39        /// alias, unresolved), or the configured tool name or a `<source>` placeholder when the
40        /// crate is discovered from a source such as `--git` or `--path`.
41        crate_spec: String,
42    },
43    /// `--prefetch` finished preparing a crate.
44    PrefetchCompleted {
45        /// The crate spec as requested on the command line (see [`Self::PrefetchStarted`]).
46        crate_spec: String,
47        /// Path to the prepared binary.
48        binary_path: PathBuf,
49    },
50    /// `--prefetch-all` started prefetching one configured tool.
51    PrefetchAllStarted {
52        /// The name of the crate in the `[tools]` config section.
53        tool: String,
54        /// List of aliases (if any) to the tool, in the `[aliases]` config section, that also
55        /// resolve to this tool.
56        aliases: Vec<String>,
57    },
58    /// `--prefetch-all` finished prefetching one configured tool.
59    PrefetchAllCompleted {
60        /// The name of the crate in the `[tools]` config section.
61        tool: String,
62        /// List of aliases (if any) to the tool, in the `[aliases]` config section, that also
63        /// resolve to this tool.
64        aliases: Vec<String>,
65        /// Path to the prepared binary.
66        binary_path: PathBuf,
67    },
68    /// `--prefetch-all` failed to prefetch one configured tool; the run continues with the
69    /// remaining tools and reports an overall failure at the end.
70    PrefetchAllFailed {
71        /// The name of the crate in the `[tools]` config section.
72        tool: String,
73        /// List of aliases (if any) to the tool, in the `[aliases]` config section, that also
74        /// resolve to this tool.
75        aliases: Vec<String>,
76        /// The rendered error that caused the failure.
77        error: String,
78    },
79}
80
81impl RunnerMessage {
82    pub fn execution_plan(binary_path: &std::path::Path, args: &[OsString], no_exec: bool) -> Self {
83        Self::ExecutionPlan {
84            binary_path: binary_path.to_path_buf(),
85            args: args.iter().map(|s| s.to_string_lossy().into_owned()).collect(),
86            no_exec,
87        }
88    }
89
90    pub fn list_tool(name: &str, config: &ToolConfig) -> Self {
91        Self::ListTool {
92            name: name.to_string(),
93            config: config.clone(),
94        }
95    }
96
97    pub fn list_alias(name: &str, target: &str) -> Self {
98        Self::ListAlias {
99            name: name.to_string(),
100            target: target.to_string(),
101        }
102    }
103
104    pub fn prefetch_started(crate_spec: &str) -> Self {
105        Self::PrefetchStarted {
106            crate_spec: crate_spec.to_string(),
107        }
108    }
109
110    pub fn prefetch_completed(crate_spec: &str, binary_path: &std::path::Path) -> Self {
111        Self::PrefetchCompleted {
112            crate_spec: crate_spec.to_string(),
113            binary_path: binary_path.to_path_buf(),
114        }
115    }
116
117    pub fn prefetch_all_started(tool: &str, aliases: &[String]) -> Self {
118        Self::PrefetchAllStarted {
119            tool: tool.to_string(),
120            aliases: aliases.to_vec(),
121        }
122    }
123
124    pub fn prefetch_all_completed(tool: &str, aliases: &[String], binary_path: &std::path::Path) -> Self {
125        Self::PrefetchAllCompleted {
126            tool: tool.to_string(),
127            aliases: aliases.to_vec(),
128            binary_path: binary_path.to_path_buf(),
129        }
130    }
131
132    pub fn prefetch_all_failed(tool: &str, aliases: &[String], error: &dyn std::fmt::Display) -> Self {
133        Self::PrefetchAllFailed {
134            tool: tool.to_string(),
135            aliases: aliases.to_vec(),
136            error: error.to_string(),
137        }
138    }
139}
140
141impl From<RunnerMessage> for Message {
142    fn from(msg: RunnerMessage) -> Self {
143        Message::Runner(msg)
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use assert_matches::assert_matches;
150
151    use super::*;
152    use crate::config::ToolConfig;
153
154    #[test]
155    fn list_tool_message_round_trips_as_json() {
156        let message: Message =
157            RunnerMessage::list_tool("timestamp", &ToolConfig::Version("0.1".to_string())).into();
158
159        let json = serde_json::to_string(&message).unwrap();
160        let parsed: Message = serde_json::from_str(&json).unwrap();
161
162        assert_matches!(
163            parsed,
164            Message::Runner(RunnerMessage::ListTool {
165                ref name,
166                config: ToolConfig::Version(ref version),
167            }) if name == "timestamp" && version == "0.1"
168        );
169    }
170
171    #[test]
172    fn list_alias_message_round_trips_as_json() {
173        let message: Message = RunnerMessage::list_alias("ts", "timestamp").into();
174
175        let json = serde_json::to_string(&message).unwrap();
176        let parsed: Message = serde_json::from_str(&json).unwrap();
177
178        assert_matches!(
179            parsed,
180            Message::Runner(RunnerMessage::ListAlias {
181                ref name,
182                ref target,
183            }) if name == "ts" && target == "timestamp"
184        );
185    }
186}