1use std::path::{Path, PathBuf};
2use std::process::Stdio;
3
4use anyhow::{Context, Result};
5use serde::{Deserialize, Serialize};
6use tokio::process::Command;
7
8const HELPER_SCRIPT: &str = include_str!("../assets/helper.py");
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct LaunchArg {
13 pub name: String,
15 pub default_value: String,
17}
18
19fn helper_script_path() -> PathBuf {
20 dirs::cache_dir()
21 .unwrap_or_else(|| PathBuf::from("."))
22 .join("ros2kit")
23 .join("launch_helper.py")
24}
25
26fn ensure_helper_script() -> Result<PathBuf> {
27 let path = helper_script_path();
28 if let Some(parent) = path.parent() {
29 std::fs::create_dir_all(parent)?;
30 }
31 std::fs::write(&path, HELPER_SCRIPT)?;
32 Ok(path)
33}
34
35pub fn helper_path() -> Result<PathBuf> {
37 ensure_helper_script()
38}
39
40#[derive(Deserialize)]
41struct RawArg {
42 name: String,
43 default: String,
44}
45
46pub async fn parse_launch_args(python: &Path, launch_file_path: &Path) -> Result<Vec<LaunchArg>> {
48 let script = ensure_helper_script()?;
49
50 let output = Command::new(python)
51 .args([
52 script.as_os_str(),
53 "show-args".as_ref(),
54 launch_file_path.as_os_str(),
55 ])
56 .stdout(Stdio::piped())
57 .stderr(Stdio::piped())
58 .output()
59 .await
60 .context("Failed to run launch helper")?;
61
62 if !output.status.success() {
63 let stderr = String::from_utf8_lossy(&output.stderr);
64 anyhow::bail!("Launch helper failed: {stderr}");
65 }
66
67 let stdout = String::from_utf8_lossy(&output.stdout);
68 let raw_args: Vec<RawArg> =
69 serde_json::from_str(&stdout).context("Failed to parse launch args JSON")?;
70
71 Ok(raw_args
72 .into_iter()
73 .map(|a| LaunchArg {
74 name: a.name,
75 default_value: a.default,
76 })
77 .collect())
78}