ready_set_sdk/
dispatch.rs1use std::ffi::OsString;
8use std::process::Command;
9
10use crate::context::{ColorMode, Context, LogLevel};
11use crate::error::{Error, Result};
12use crate::output::OutputMode;
13
14fn locate_dispatcher() -> Result<std::path::PathBuf> {
18 if let Some(explicit) = std::env::var_os("READY_SET_BIN") {
19 return Ok(std::path::PathBuf::from(explicit));
20 }
21 which::which("ready-set").map_err(|_| Error::MissingDependency {
22 name: "ready-set".to_string(),
23 hint: Some("install via `cargo install ready-set`".to_string()),
24 })
25}
26
27#[derive(Debug)]
29#[non_exhaustive]
30pub enum DispatchOutcome {
31 Streamed {
33 exit_code: i32,
35 },
36 Captured {
38 stdout: Vec<u8>,
40 exit_code: i32,
42 },
43}
44
45pub struct DispatchBuilder {
47 subcommand: String,
48 args: Vec<OsString>,
49 capture: bool,
50}
51
52impl DispatchBuilder {
53 #[must_use]
55 pub fn new(subcommand: impl Into<String>) -> Self {
56 Self {
57 subcommand: subcommand.into(),
58 args: Vec::new(),
59 capture: false,
60 }
61 }
62
63 #[must_use]
65 pub fn arg(mut self, arg: impl Into<OsString>) -> Self {
66 self.args.push(arg.into());
67 self
68 }
69
70 #[must_use]
72 pub fn args<I, S>(mut self, args: I) -> Self
73 where
74 I: IntoIterator<Item = S>,
75 S: Into<OsString>,
76 {
77 self.args.extend(args.into_iter().map(Into::into));
78 self
79 }
80
81 #[must_use]
83 pub const fn capture(mut self, yes: bool) -> Self {
84 self.capture = yes;
85 self
86 }
87
88 pub fn run(self, ctx: &Context) -> Result<DispatchOutcome> {
96 let bin = locate_dispatcher()?;
97 let mut cmd = Command::new(bin);
98 cmd.arg(&self.subcommand);
99 cmd.args(&self.args);
100
101 export_contract(&mut cmd, ctx);
103
104 if self.capture {
105 cmd.stdout(std::process::Stdio::piped());
106 let output = cmd.output()?;
107 Ok(DispatchOutcome::Captured {
108 stdout: output.stdout,
109 exit_code: output.status.code().unwrap_or(-1),
110 })
111 } else {
112 let status = cmd.status()?;
113 Ok(DispatchOutcome::Streamed {
114 exit_code: status.code().unwrap_or(-1),
115 })
116 }
117 }
118}
119
120pub fn export_contract(cmd: &mut Command, ctx: &Context) {
122 if let Some(version) = ctx.dispatcher_version() {
123 cmd.env("READY_SET_DISPATCHER_VERSION", version.to_string());
124 }
125 if let Some(root) = ctx.project_root() {
126 cmd.env("READY_SET_PROJECT_ROOT", root);
127 }
128 if let Some(cfg) = ctx.config_path() {
129 cmd.env("READY_SET_CONFIG_PATH", cfg);
130 }
131 cmd.env(
132 "READY_SET_OUTPUT",
133 match ctx.output_mode() {
134 OutputMode::Human => "human",
135 OutputMode::Json => "json",
136 },
137 );
138 cmd.env(
139 "READY_SET_LOG",
140 match ctx.log_level() {
141 LogLevel::Quiet => "quiet",
142 LogLevel::Normal => "normal",
143 LogLevel::Verbose => "verbose",
144 },
145 );
146 cmd.env(
147 "READY_SET_COLOR",
148 match ctx.color() {
149 ColorMode::Auto => "auto",
150 ColorMode::Always => "always",
151 ColorMode::Never => "never",
152 },
153 );
154}