zbus/address/transport/
unixexec.rs1use std::{
2 borrow::BorrowMut, ffi::OsString, fmt::Display, os::unix::ffi::OsStrExt, path::PathBuf,
3 process::Stdio,
4};
5
6use crate::process::Command;
7
8use super::encode_percents;
9
10#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct Unixexec {
15 path: PathBuf,
16 arg0: Option<OsString>,
17 args: Vec<OsString>,
18}
19
20impl Unixexec {
21 pub fn new(path: PathBuf, arg0: Option<OsString>, args: Vec<OsString>) -> Self {
23 Self { path, arg0, args }
24 }
25
26 pub(super) fn from_options(opts: std::collections::HashMap<&str, &str>) -> crate::Result<Self> {
27 let Some(path) = opts.get("path") else {
28 return Err(crate::Error::Address(
29 "unixexec address is missing `path`".to_owned(),
30 ));
31 };
32
33 let arg0 = opts.get("argv0").map(OsString::from);
34
35 let mut args: Vec<OsString> = Vec::new();
36 let mut arg_index = 1;
37 while let Some(arg) = opts.get(format!("argv{arg_index}").as_str()) {
38 args.push(OsString::from(arg));
39 arg_index += 1;
40 }
41
42 Ok(Self::new(PathBuf::from(path), arg0, args))
43 }
44
45 pub fn path(&self) -> &PathBuf {
51 &self.path
52 }
53
54 pub fn arg0(&self) -> Option<&OsString> {
60 self.arg0.as_ref()
61 }
62
63 pub fn args(&self) -> &[OsString] {
67 self.args.as_ref()
68 }
69
70 pub(super) async fn connect(&self) -> crate::Result<crate::connection::socket::Command> {
71 Command::for_unixexec(self)
72 .stdin(Stdio::piped())
73 .stdout(Stdio::piped())
74 .stderr(Stdio::inherit())
75 .spawn()?
76 .borrow_mut()
77 .try_into()
78 }
79}
80
81impl Display for Unixexec {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 f.write_str("unixexec:path=")?;
84 encode_percents(f, self.path.as_os_str().as_bytes())?;
85
86 if let Some(arg0) = self.arg0.as_ref() {
87 f.write_str(",argv0=")?;
88 encode_percents(f, arg0.as_bytes())?;
89 }
90
91 for (index, arg) in self.args.iter().enumerate() {
92 write!(f, ",argv{}=", index + 1)?;
93 encode_percents(f, arg.as_bytes())?;
94 }
95
96 Ok(())
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use crate::address::{transport::Transport, Address};
103
104 #[test]
105 fn connect() {
106 let addr: Address = "unixexec:path=echo,argv1=hello,argv2=world"
107 .try_into()
108 .unwrap();
109 let unixexec = match addr.transport() {
110 Transport::Unixexec(unixexec) => unixexec,
111 _ => unreachable!(),
112 };
113 crate::utils::block_on(unixexec.connect()).unwrap();
114 }
115}