Skip to main content

ros2kit/
launch.rs

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/// A declared argument from a ROS 2 launch file.
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct LaunchArg {
13    /// Argument name as declared in the launch file.
14    pub name: String,
15    /// Default value for the argument, or empty if none is specified.
16    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
35/// Writes the embedded Python helper script to the cache directory and returns its path.
36pub fn helper_path() -> Result<PathBuf> {
37    ensure_helper_script()
38}
39
40#[derive(Deserialize)]
41struct RawArg {
42    name: String,
43    default: String,
44}
45
46/// Invokes the helper script to extract declared arguments from a ROS 2 launch file.
47pub 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}