use std::cell::RefCell;
use std::fmt::{self, Debug, Display};
use std::io;
use std::rc::Rc;
#[derive(Clone)]
pub struct ShellWriter<W: io::Write> {
writer: Rc<RefCell<W>>,
prefix: String,
}
impl<W: io::Write> ShellWriter<W> {
#[must_use]
pub fn new<P: Display>(writer: W, prefix: P) -> Self {
Self {
writer: Rc::new(RefCell::new(writer)),
prefix: prefix.to_string(),
}
}
fn write_raw<K: Display, V: Display>(&self, var: K, raw: V) {
writeln!(self.writer.borrow_mut(), "{}{}={}", self.prefix, var, raw)
.unwrap();
}
pub fn write_var<K: Display, V: Display>(&self, var: K, value: V) {
self.write_raw(var, shell_quote(value));
}
pub fn write_var_debug<K: Display, V: Debug>(&self, var: K, value: V) {
self.write_raw(var, shell_quote_debug(value));
}
pub fn write_vars<V: ShellVars>(&self, vars: &V) {
vars.write_to_shell(self);
}
#[must_use]
pub fn group<G: Display>(&self, group: G) -> Self {
Self {
writer: self.writer.clone(),
prefix: format!("{}{}_", self.prefix, group),
}
}
#[must_use]
pub fn group_n<P: Display, N: Display>(&self, prefix: P, n: N) -> Self {
self.group(format!("{prefix}{n}"))
}
}
impl ShellWriter<io::Stdout> {
#[must_use]
pub fn with_prefix<P: Display>(prefix: P) -> Self {
Self::new(io::stdout(), prefix)
}
}
impl Default for ShellWriter<io::Stdout> {
fn default() -> Self {
Self::new(io::stdout(), "")
}
}
impl<W: io::Write + Debug> Debug for ShellWriter<W> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("ShellWriter")
.field("writer", &self.writer)
.field("prefix", &self.prefix)
.finish()
}
}
pub trait ShellVars {
fn write_to_shell<W: io::Write>(&self, out: &ShellWriter<W>);
}
pub fn shell_quote<V: Display>(value: V) -> String {
shell_words::quote(&value.to_string()).into()
}
pub fn shell_quote_debug<V: Debug>(value: V) -> String {
shell_words::quote(&format!("{value:?}")).into()
}