use std::collections::BTreeMap;
use std::ffi::OsString;
use crate::internal::core::Core;
use crate::internal::listing;
use crate::{Command, EnvironmentEntry, Error, TmuxText};
#[derive(Clone, Copy)]
pub(crate) enum Scope<'target> {
Global,
Session(&'target str),
}
impl Scope<'_> {
fn apply(self, command: Command) -> Command {
match self {
Self::Global => command.arg("-g"),
Self::Session(target) => command.arg("-t").arg(target),
}
}
}
pub(crate) async fn set(
core: &Core,
scope: Scope<'_>,
name: &str,
value: OsString,
) -> Result<(), Error> {
listing::mutate(
core,
"set-environment",
scope
.apply(Command::new("set-environment"))
.arg(OsString::from(name))
.sensitive_arg(value),
)
.await
}
pub(crate) async fn hide(core: &Core, scope: Scope<'_>, name: &str) -> Result<(), Error> {
listing::mutate(
core,
"set-environment",
scope
.apply(Command::new("set-environment"))
.arg("-r")
.arg(OsString::from(name)),
)
.await
}
pub(crate) async fn unset(core: &Core, scope: Scope<'_>, name: &str) -> Result<(), Error> {
listing::mutate(
core,
"set-environment",
scope
.apply(Command::new("set-environment"))
.arg("-u")
.arg(OsString::from(name)),
)
.await
}
pub(crate) async fn get(
core: &Core,
scope: Scope<'_>,
name: &str,
) -> Result<Option<EnvironmentEntry>, Error> {
let result = core
.execute(
scope
.apply(Command::new("show-environment"))
.arg(OsString::from(name)),
)
.await?;
if !result.success() {
return Ok(None);
}
let stdout = result.stdout();
let line = stdout.strip_suffix(b"\n").unwrap_or(stdout);
if line.first() == Some(&b'-') {
return Ok(Some(EnvironmentEntry::Removed));
}
let Some(position) = line.iter().position(|byte| *byte == b'=') else {
return Ok(None);
};
Ok(Some(EnvironmentEntry::Set(TmuxText::from(
line[position + 1..].to_vec(),
))))
}
pub(crate) async fn all(
core: &Core,
scope: Scope<'_>,
) -> Result<BTreeMap<String, EnvironmentEntry>, Error> {
let result = core
.execute(scope.apply(Command::new("show-environment")))
.await?;
if !result.success() {
return Err(Error::refused(
"show-environment",
result.exit_code(),
result.stderr_lossy().into_owned(),
None,
));
}
let candidates: Vec<String> = result
.stdout_lossy()
.lines()
.filter_map(|line| {
line.strip_prefix('-')
.map_or_else(|| line.split_once('=').map(|(name, _)| name), Some)
})
.filter(|name| !name.is_empty())
.map(ToOwned::to_owned)
.collect();
let mut environment = BTreeMap::new();
for name in candidates {
if let Some(entry) = get(core, scope, &name).await? {
environment.insert(name, entry);
}
}
Ok(environment)
}