flowey_cli/cli/debug/
interrogate.rs1use crate::cli::FlowBackendCli;
5use flowey_core::node::FlowArch;
6use flowey_core::node::FlowBackend;
7use flowey_core::node::FlowPlatform;
8use flowey_core::node::GhOutput;
9use flowey_core::node::GhToRust;
10use flowey_core::node::NodeHandle;
11use flowey_core::node::RustToGh;
12use flowey_core::node::steps::rust::RustRuntimeServices;
13use flowey_core::node::user_facing::ClaimedGhParam;
14use flowey_core::node::user_facing::GhPermission;
15use flowey_core::node::user_facing::GhPermissionValue;
16use flowey_core::pipeline::HostExt;
17use flowey_core::pipeline::PipelineBackendHint;
18use std::collections::BTreeMap;
19
20#[derive(clap::Args)]
33pub struct Interrogate {
34 node_handle: String,
36
37 flow_backend: FlowBackendCli,
39
40 #[clap(long)]
42 req: Vec<String>,
43}
44
45impl Interrogate {
46 pub fn run(self) -> anyhow::Result<()> {
47 let Self {
48 node_handle,
49 flow_backend,
50 req,
51 } = self;
52
53 let raw_json_reqs: Vec<Box<[u8]>> = req
54 .into_iter()
55 .map(|v| v.as_bytes().to_vec().into())
56 .collect();
57
58 let Some(node_handle) = NodeHandle::try_from_modpath(&node_handle) else {
59 anyhow::bail!("could not find node with that name");
60 };
61
62 let mut node = node_handle.new_erased_node();
63
64 let mut dep_registration_backend = InterrogateDepRegistrationBackend;
65 let mut dep_registration = flowey_core::node::new_import_ctx(&mut dep_registration_backend);
66
67 let mut ctx_backend = InterrogateCtx::new(flow_backend.into(), node_handle);
68
69 println!(
70 "# interrogating with {}",
71 match flow_backend {
72 FlowBackendCli::Ado => "ado",
73 FlowBackendCli::Local => "local",
74 FlowBackendCli::Github => "github",
75 }
76 );
77
78 node.imports(&mut dep_registration);
79
80 let mut ctx = flowey_core::node::new_node_ctx(&mut ctx_backend);
81 node.emit(raw_json_reqs.clone(), &mut ctx)?;
82
83 Ok(())
84 }
85}
86
87struct InterrogateDepRegistrationBackend;
88
89impl flowey_core::node::ImportCtxBackend for InterrogateDepRegistrationBackend {
90 fn on_possible_dep(&mut self, node_handle: NodeHandle) {
91 println!("[dep?] {}", node_handle.modpath())
92 }
93}
94
95struct InterrogateCtx {
96 flow_backend: FlowBackend,
97 current_node: NodeHandle,
98 idx_tracker: usize,
99 var_tracker: usize,
100}
101
102impl InterrogateCtx {
103 fn new(flow_backend: FlowBackend, current_node: NodeHandle) -> Self {
104 Self {
105 flow_backend,
106 current_node,
107 idx_tracker: 0,
108 var_tracker: 0,
109 }
110 }
111}
112
113impl flowey_core::node::NodeCtxBackend for InterrogateCtx {
114 fn on_emit_rust_step(
115 &mut self,
116 label: &str,
117 _can_merge: bool,
118 _code: Box<
119 dyn for<'a> FnOnce(&'a mut RustRuntimeServices<'_>) -> anyhow::Result<()> + 'static,
120 >,
121 ) {
122 println!("[step][rust][{}] # {}", self.idx_tracker, label);
123 self.idx_tracker += 1;
124 }
125
126 fn on_emit_ado_step(
127 &mut self,
128 label: &str,
129 yaml_snippet: Box<
130 dyn for<'a> FnOnce(
131 &'a mut flowey_core::node::user_facing::AdoStepServices<'_>,
132 ) -> String,
133 >,
134 code: Option<
135 Box<
136 dyn for<'a> FnOnce(&'a mut RustRuntimeServices<'_>) -> anyhow::Result<()> + 'static,
137 >,
138 >,
139 _condvar: Option<String>,
140 ) {
141 println!(
142 "[step][yaml] # {}{}",
143 if code.is_some() {
144 "(+inline script) "
145 } else {
146 ""
147 },
148 label
149 );
150 let mut fresh_ado_var = || "<dummy>".into();
151 let mut access = flowey_core::node::steps::ado::new_ado_step_services(&mut fresh_ado_var);
152 let raw_snippet = yaml_snippet(&mut access);
153
154 let snippet: Result<serde_yaml::Value, _> = serde_yaml::from_str(&raw_snippet);
155 match snippet {
156 Ok(snippet) => print!("{}", serde_yaml::to_string(&snippet).unwrap()),
157 Err(e) => {
158 log::error!("invalid snippet: {}", e);
159 println!(">>>");
160 println!("{}", raw_snippet);
161 println!("<<<");
162 }
163 };
164
165 self.idx_tracker += 1;
166 }
167
168 fn on_emit_gh_step(
169 &mut self,
170
171 label: &str,
172 _uses: &str,
173 _with: BTreeMap<String, ClaimedGhParam>,
174 _condvar: Option<String>,
175 _outputs: BTreeMap<String, Vec<GhOutput>>,
176 _permissions: BTreeMap<GhPermission, GhPermissionValue>,
177 _gh_to_rust: Vec<GhToRust>,
178 _rust_to_gh: Vec<RustToGh>,
179 ) {
180 println!("[step][yaml] # {}", label);
181 self.idx_tracker += 1;
182 }
183
184 fn on_emit_side_effect_step(&mut self) {
185 println!("[step][anchor]");
186 }
187
188 fn backend(&mut self) -> FlowBackend {
189 self.flow_backend
190 }
191
192 fn platform(&mut self) -> FlowPlatform {
193 FlowPlatform::host(PipelineBackendHint::Local)
194 }
195
196 fn arch(&mut self) -> FlowArch {
197 if cfg!(target_arch = "x86_64") {
199 FlowArch::X86_64
200 } else if cfg!(target_arch = "aarch64") {
202 FlowArch::Aarch64
203 } else {
204 unreachable!("flowey only runs on X86_64 or Aarch64 at the moment")
205 }
206 }
207
208 fn on_request(&mut self, node_handle: NodeHandle, req: anyhow::Result<Box<[u8]>>) {
209 match req {
210 Ok(data) => {
211 let data = match String::from_utf8(data.into()) {
212 Ok(data) => data,
213 Err(e) => e
214 .into_bytes()
215 .iter()
216 .map(|b| format!("(raw) {:02x}", b))
217 .collect::<Vec<_>>()
218 .join(""),
219 };
220 println!("[req] {} <-- {}", node_handle.modpath(), data)
221 }
222 Err(e) => {
223 log::error!("error serializing inter-node request: {:#}", e)
224 }
225 }
226 }
227
228 fn on_new_var(&mut self) -> String {
229 let v = self.var_tracker;
230 self.var_tracker += 1;
231 format!("<dummy>:{}", v)
232 }
233
234 fn on_claimed_runtime_var(&mut self, var: &str, is_read: bool) {
235 println!(
236 "[var][claim] {} {}",
237 var,
238 if is_read { "(read)" } else { "(write)" },
239 )
240 }
241
242 fn current_node(&self) -> NodeHandle {
243 self.current_node
244 }
245
246 fn persistent_dir_path_var(&mut self) -> Option<String> {
247 Some("<dummy>".into())
248 }
249
250 fn on_unused_read_var(&mut self, _var: &str) {
251 }
253}