use crate::parsers::ARG_ADD_FD;
use std::str::FromStr;
use bon::Builder;
use proptest_derive::Arbitrary;
use crate::parsers::DELIM_COMMA;
use crate::shell_string::ShellString;
use crate::to_command::ToCommand;
const KEY_FD: &str = "fd=";
const KEY_SET: &str = "set=";
const KEY_OPAQUE: &str = "opaque=";
#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
pub struct AddFd {
pub fd: usize,
pub set: usize,
pub opaque: Option<ShellString>,
}
impl AddFd {
pub fn new(fd: usize, set: usize) -> Self {
Self { fd, set, opaque: None }
}
}
impl ToCommand for AddFd {
fn command(&self) -> String {
ARG_ADD_FD.to_string()
}
fn to_args(&self) -> Vec<String> {
let mut args = vec![format!("{}{}", KEY_FD, self.fd)];
args.push(format!("{}{}", KEY_SET, self.set));
if let Some(opaque) = &self.opaque {
args.push(format!("{}{}", KEY_OPAQUE, opaque.as_ref()));
}
vec![args.join(DELIM_COMMA)]
}
}
impl FromStr for AddFd {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut fd = None;
let mut set = None;
let mut opaque = None;
for part in s.split(DELIM_COMMA) {
let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid add-fd option: {part}"))?;
match key {
"fd" => fd = Some(value.parse::<usize>().map_err(|e| e.to_string())?),
"set" => set = Some(value.parse::<usize>().map_err(|e| e.to_string())?),
"opaque" => opaque = Some(ShellString::from_str(value)?),
other => return Err(format!("unsupported add-fd option: {other}")),
}
}
Ok(AddFd {
fd: fd.ok_or_else(|| "missing fd= for -add-fd".to_string())?,
set: set.ok_or_else(|| "missing set= for -add-fd".to_string())?,
opaque,
})
}
}