libtmux 0.1.0-alpha.17

Async typed tmux client and object model (alpha)
Documentation
//! Operations that set tmux options.

use std::ffi::{OsStr, OsString};
use std::fmt;

use super::Resolver;
use super::{Chainable, Effects, Op, Operation, Safety, SessionTarget, WindowTarget};
use crate::Command;
#[cfg(feature = "serde")]
use crate::plan::ProducerIdentity;
use crate::plan::SlotUse;

/// Set a tmux option.
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct SetOption {
    scope: OptionScope,
    #[cfg_attr(
        feature = "serde",
        serde(
            serialize_with = "crate::plan::wire::argument",
            deserialize_with = "crate::plan::wire::parse_argument"
        )
    )]
    #[cfg_attr(feature = "schema", schemars(with = "crate::plan::wire::Argument"))]
    name: OsString,
    #[cfg_attr(
        feature = "serde",
        serde(
            serialize_with = "crate::plan::wire::argument",
            deserialize_with = "crate::plan::wire::parse_argument"
        )
    )]
    #[cfg_attr(feature = "schema", schemars(with = "crate::plan::wire::Argument"))]
    value: OsString,
}

/// Where a [`SetOption`] applies.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
enum OptionScope {
    Global,
    Session(SessionTarget),
    Window(WindowTarget),
}

impl SetOption {
    /// Where this step tells tmux to keep the option, when it names a place.
    ///
    /// `None` for a global write: tmux keeps a `-g` write in the global table
    /// belonging to whatever scope the option has, so "global" is true of
    /// wherever it lands and there is nothing to be wrong about.
    pub(crate) fn declared_scope(&self) -> Option<(&OsStr, crate::OptionScope)> {
        let scope = match &self.scope {
            OptionScope::Global => return None,
            OptionScope::Session(_) => crate::OptionScope::Session,
            OptionScope::Window(_) => crate::OptionScope::Window,
        };
        Some((self.name.as_os_str(), scope))
    }

    /// Set a server-global option.
    #[must_use]
    pub fn global(name: impl Into<OsString>, value: impl Into<OsString>) -> Self {
        Self {
            scope: OptionScope::Global,
            name: name.into(),
            value: value.into(),
        }
    }

    /// Set an option on one session.
    #[must_use]
    pub fn session(
        target: impl Into<SessionTarget>,
        name: impl Into<OsString>,
        value: impl Into<OsString>,
    ) -> Self {
        Self {
            scope: OptionScope::Session(target.into()),
            name: name.into(),
            value: value.into(),
        }
    }

    /// Set an option on one window.
    #[must_use]
    pub fn window(
        target: impl Into<WindowTarget>,
        name: impl Into<OsString>,
        value: impl Into<OsString>,
    ) -> Self {
        Self {
            scope: OptionScope::Window(target.into()),
            name: name.into(),
            value: value.into(),
        }
    }

    pub(in crate::plan) fn target(&self) -> Option<SlotUse> {
        match &self.scope {
            OptionScope::Global => None,
            OptionScope::Session(target) => target.slot(),
            OptionScope::Window(target) => target.slot(),
        }
    }

    #[cfg(feature = "serde")]
    pub(in crate::plan) fn rebind_slots(&mut self, producers: &[Option<ProducerIdentity>]) {
        match &mut self.scope {
            OptionScope::Global => {}
            OptionScope::Session(target) => target.rebind(producers),
            OptionScope::Window(target) => target.rebind(producers),
        }
    }

    pub(crate) fn render(&self, resolve: Resolver<'_>) -> Option<Command> {
        let command = match &self.scope {
            OptionScope::Global => Command::new("set-option").arg("-g"),
            OptionScope::Session(target) => Command::new("set-option")
                .arg("-t")
                .arg(target.token(resolve)?),
            OptionScope::Window(target) => Command::new("set-option")
                .arg("-w")
                .arg("-t")
                .arg(target.token(resolve)?),
        };
        Some(
            command
                .arg("--")
                .arg(self.name.clone())
                .sensitive_arg(self.value.clone()),
        )
    }
}

impl fmt::Debug for SetOption {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("SetOption")
            .field("scope", &self.scope)
            .field("name", &self.name)
            .finish_non_exhaustive()
    }
}

operation!(
    SetOption,
    creates = (),
    effects = Effects {
        idempotent: true,
        ..Effects::MUTATING
    },
    safety = Safety::Mutating,
    chainable
);

/// Set a variable in a session's environment.
///
/// Applied before anything runs in the session, so a command started later
/// sees the environment the caller described rather than the one tmux
/// happened to inherit.
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct SetEnvironment {
    pub(crate) target: SessionTarget,
    #[cfg_attr(
        feature = "serde",
        serde(
            serialize_with = "crate::plan::wire::argument",
            deserialize_with = "crate::plan::wire::parse_argument"
        )
    )]
    #[cfg_attr(feature = "schema", schemars(with = "crate::plan::wire::Argument"))]
    name: OsString,
    #[cfg_attr(
        feature = "serde",
        serde(
            serialize_with = "crate::plan::wire::argument",
            deserialize_with = "crate::plan::wire::parse_argument"
        )
    )]
    #[cfg_attr(feature = "schema", schemars(with = "crate::plan::wire::Argument"))]
    value: OsString,
}

impl SetEnvironment {
    /// Set `name` in this session's environment.
    #[must_use]
    pub fn new(
        target: impl Into<SessionTarget>,
        name: impl Into<OsString>,
        value: impl Into<OsString>,
    ) -> Self {
        Self {
            target: target.into(),
            name: name.into(),
            value: value.into(),
        }
    }

    pub(crate) fn render(&self, resolve: Resolver<'_>) -> Option<Command> {
        Some(
            Command::new("set-environment")
                .arg("-t")
                .arg(self.target.token(resolve)?)
                .arg("--")
                .arg(self.name.clone())
                .sensitive_arg(self.value.clone()),
        )
    }
}

impl fmt::Debug for SetEnvironment {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("SetEnvironment")
            .field("target", &self.target)
            .field("name", &self.name)
            .finish_non_exhaustive()
    }
}

operation!(
    SetEnvironment,
    creates = (),
    effects = Effects {
        idempotent: true,
        ..Effects::MUTATING
    },
    safety = Safety::Mutating,
    chainable
);