zbus/address/transport/
unixexec.rs1use std::{
2 borrow::BorrowMut, ffi::OsString, fmt::Display, os::unix::ffi::OsStrExt, path::PathBuf,
3 process::Stdio, sync::Arc,
4};
5
6use crate::{Address, 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(
71 &self,
72 address: &Address,
73 ) -> crate::Result<crate::connection::socket::Command> {
74 let mut child = Command::for_unixexec(self)
75 .stdin(Stdio::piped())
76 .stdout(Stdio::piped())
77 .stderr(Stdio::inherit())
78 .spawn()
79 .map_err(|e| crate::Error::Connection(Arc::new(e), address.clone()))?;
80
81 child.borrow_mut().try_into()
82 }
83}
84
85impl Display for Unixexec {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 f.write_str("unixexec:path=")?;
88 encode_percents(f, self.path.as_os_str().as_bytes())?;
89
90 if let Some(arg0) = self.arg0.as_ref() {
91 f.write_str(",argv0=")?;
92 encode_percents(f, arg0.as_bytes())?;
93 }
94
95 for (index, arg) in self.args.iter().enumerate() {
96 write!(f, ",argv{}=", index + 1)?;
97 encode_percents(f, arg.as_bytes())?;
98 }
99
100 Ok(())
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use crate::address::{Address, transport::Transport};
107
108 #[test]
109 fn connect() {
110 let addr: Address = "unixexec:path=echo,argv1=hello,argv2=world"
111 .try_into()
112 .unwrap();
113 let unixexec = match addr.transport() {
114 Transport::Unixexec(unixexec) => unixexec,
115 _ => unreachable!(),
116 };
117 crate::utils::block_on(unixexec.connect(&addr)).unwrap();
118 }
119}