runcc/config/
command.rs

1use serde::{Deserialize, Serialize};
2
3use crate::env::match_program_with_envs;
4
5#[non_exhaustive]
6#[derive(Deserialize, Serialize, Debug)]
7pub struct CommandConfig {
8    pub program: String,
9    pub args: Option<Vec<String>>,
10    pub label: Option<String>,
11    pub envs: Option<Vec<(String, String)>>,
12    pub cwd: Option<String>,
13}
14
15#[non_exhaustive]
16#[derive(Debug, Default)]
17pub struct CommandConfigFromScriptOptions {
18    pub windows_call_cmd_with_env: super::WindowsCallCmdWithEnv,
19}
20
21macro_rules! def_into_command_and_label {
22    ($name:ident -> $cmd_type:ty) => {
23        pub fn $name<I, K, V>(self, inherited_envs: Option<I>) -> ($cmd_type, String)
24        where
25            I: IntoIterator<Item = (K, V)>,
26            K: AsRef<std::ffi::OsStr>,
27            V: AsRef<std::ffi::OsStr>,
28        {
29            let Self {
30                program,
31                args,
32                label,
33                envs,
34                cwd,
35            } = self;
36
37            let mut command = <$cmd_type>::new(&program);
38
39            if let Some(cwd) = cwd {
40                command.current_dir(cwd);
41            }
42
43            if let Some(args) = &args {
44                command.args(args);
45            }
46
47            if let Some(envs) = inherited_envs {
48                command.envs(envs);
49            }
50
51            if let Some(envs) = envs {
52                command.envs(envs);
53            }
54
55            let label = label.unwrap_or_else(move || {
56                if let Some(args) = args {
57                    format!("{} {}", program, args.join(" "))
58                } else {
59                    program
60                }
61            });
62
63            (command, label)
64        }
65    };
66}
67
68impl CommandConfig {
69    pub fn from_script(script: &str, options: &CommandConfigFromScriptOptions) -> CommandConfig {
70        let script = script.trim();
71
72        let (program, envs) = match_program_with_envs(script);
73
74        let program = program.to_string();
75        let envs = if let Some(envs) = envs {
76            Some(
77                envs.into_iter()
78                    .map(|(k, v)| (k.to_string(), v.to_string()))
79                    .collect(),
80            )
81        } else {
82            None
83        };
84
85        if cfg!(target_os = "windows") {
86            let with_env = &options.windows_call_cmd_with_env;
87
88            let env_name = with_env.clone().try_into_env_name();
89
90            let (arg, env) = match env_name {
91                Some(env_name) => (format!("%{}%", env_name), Some((env_name, program.clone()))),
92                None => (program.clone(), None),
93            };
94
95            let mut cmd = CommandConfig {
96                program: "cmd".to_string(),
97                args: Some(vec!["/C".to_string(), arg]),
98                label: Some(program.clone()),
99                envs,
100                cwd: None,
101            };
102
103            if let Some(env) = env {
104                cmd.env(env);
105            }
106
107            cmd
108        } else {
109            CommandConfig {
110                program: "sh".to_string(),
111                args: Some(vec!["-c".to_string(), program.clone()]),
112                label: Some(program),
113                envs,
114                cwd: None,
115            }
116        }
117    }
118
119    pub fn from_program_args(program: String, args: Option<Vec<String>>) -> CommandConfig {
120        CommandConfig {
121            program,
122            args,
123            label: None,
124            envs: None,
125            cwd: None,
126        }
127    }
128
129    pub fn label_length(&self) -> usize {
130        match &self.label {
131            None => {
132                self.program.len()
133                    + self
134                        .args
135                        .as_ref()
136                        .map_or(0, |args| args.iter().map(|arg| arg.len() + 1).sum())
137            }
138            Some(label) => label.len(),
139        }
140    }
141
142    pub fn env(&mut self, env: (String, String)) -> &mut Self {
143        self.envs.get_or_insert_with(|| vec![]).push(env);
144        self
145    }
146
147    def_into_command_and_label! {into_command_and_label->std::process::Command}
148
149    def_into_command_and_label! {into_tokio_command_and_label->tokio::process::Command}
150}