//! sl0p fork: the branding seam. //! //! sl0ppij is a fork of zellij that must be installable and runnable *beside* a //! stock zellij: different binary name, different config dir, different socket //! dir, different cache. Rather than renaming things all over the tree (which //! would turn every future rebase onto upstream into a conflict festival), every //! user-visible name is defined exactly once, here, and referenced from the //! handful of places that used to hold a literal. //! //! Rebase rule: if a conflict lands in this file, the fix is always "keep our //! constant, take their surrounding code". //! //! What is deliberately NOT renamed: //! - crate names (`zellij-utils`, `zellij-tile`, ...). They are internal, and //! renaming them would break the plugin API and every intra-workspace dep //! for zero user-visible gain. The *binary* is renamed via `[[bin]]` in the //! root Cargo.toml instead. //! - the `$ZELLIJ*` environment variables. They stay, because the wider //! ecosystem (shell prompts, "am I in a multiplexer" checks, our own //! pi-ext) looks for them. We additionally export `$SL0PPIJ*` equivalents, //! which take precedence when both are set. /// Program name. Drives the binary name, config dir, socket dir prefix and log /// file name. pub const APP: &str = "sl0ppij"; /// The upstream project we are forked from. Only used for compatibility /// fallbacks, never for anything we create. pub const UPSTREAM_APP: &str = "zellij"; pub const ABOUT: &str = "A terminal workspace with batteries included (sl0p fork)"; // --- filesystem ------------------------------------------------------------ /// `~/.config/sl0ppij` -- checked before the XDG dir, as upstream does. pub const HOME_CONFIG_LOCATION: &str = ".config/sl0ppij"; /// System-wide config dir. pub const SYSTEM_CONFIG_DIR: &str = "/etc/sl0ppij"; pub const SYSTEM_CONFIG_DIR_WINDOWS: &str = "C:\\ProgramData\\sl0ppij"; pub const SYSTEM_DATA_DIR_PREFIX_WINDOWS: &str = "C:\\ProgramData\\sl0ppij"; /// Appended to the data dir prefix, e.g. `/usr` + `share/sl0ppij`. pub const SYSTEM_DATA_SUBDIR: &str = "share/sl0ppij"; /// Arguments to `directories::ProjectDirs::from`. On Linux only the last one /// matters (`~/.config/sl0ppij`, `~/.cache/sl0ppij`); the qualifier and /// organisation shape the macOS/Windows paths. pub const PROJ_QUALIFIER: &str = "foo"; pub const PROJ_ORGANIZATION: &str = "sl0p"; pub const PROJ_APPLICATION: &str = "sl0ppij"; /// `$TMPDIR/sl0ppij-` pub const TMP_DIR_PREFIX: &str = "sl0ppij"; pub const LOG_DIR_NAME: &str = "sl0ppij-log"; pub const LOG_FILE_NAME: &str = "sl0ppij.log"; pub const OLD_LOG_FILE_NAME: &str = "sl0ppij.log.old"; // --- plugin url scheme ----------------------------------------------------- /// Scheme for builtin plugins: `sl0ppij:tab-bar`. `zellij:tab-bar` is still /// accepted when parsing so upstream layouts and configs keep working. pub const PLUGIN_SCHEME: &str = "sl0ppij"; pub const PLUGIN_SCHEME_COMPAT: &str = "zellij"; // --- environment variables ------------------------------------------------- // // Each pair is (ours, upstream's). Both are exported when we set them; ours // wins when both are present on read. pub const ENV_MARKER: &str = "SL0PPIJ"; pub const ENV_MARKER_COMPAT: &str = "ZELLIJ"; pub const ENV_SESSION_NAME: &str = "SL0PPIJ_SESSION_NAME"; pub const ENV_SESSION_NAME_COMPAT: &str = "ZELLIJ_SESSION_NAME"; pub const ENV_SOCKET_DIR: &str = "SL0PPIJ_SOCKET_DIR"; pub const ENV_SOCKET_DIR_COMPAT: &str = "ZELLIJ_SOCKET_DIR"; pub const ENV_PANE_ID: &str = "SL0PPIJ_PANE_ID"; pub const ENV_PANE_ID_COMPAT: &str = "ZELLIJ_PANE_ID"; pub const ENV_CONFIG_FILE: &str = "SL0PPIJ_CONFIG_FILE"; pub const ENV_CONFIG_FILE_COMPAT: &str = "ZELLIJ_CONFIG_FILE"; pub const ENV_CONFIG_DIR: &str = "SL0PPIJ_CONFIG_DIR"; pub const ENV_CONFIG_DIR_COMPAT: &str = "ZELLIJ_CONFIG_DIR"; pub const ENV_LAYOUT_DIR: &str = "SL0PPIJ_LAYOUT_DIR"; pub const ENV_LAYOUT_DIR_COMPAT: &str = "ZELLIJ_LAYOUT_DIR"; /// Every (ours, upstream) pair, for bulk operations. pub const ENV_PAIRS: &[(&str, &str)] = &[ (ENV_MARKER, ENV_MARKER_COMPAT), (ENV_SESSION_NAME, ENV_SESSION_NAME_COMPAT), (ENV_SOCKET_DIR, ENV_SOCKET_DIR_COMPAT), (ENV_PANE_ID, ENV_PANE_ID_COMPAT), (ENV_CONFIG_FILE, ENV_CONFIG_FILE_COMPAT), (ENV_CONFIG_DIR, ENV_CONFIG_DIR_COMPAT), (ENV_LAYOUT_DIR, ENV_LAYOUT_DIR_COMPAT), ]; /// Read our variable, falling back to the upstream-named one. pub fn env_var(primary: &str, compat: &str) -> Result { match std::env::var(primary) { Ok(value) => Ok(value), Err(_) => std::env::var(compat), } } /// Set both spellings, so children see whichever they look for. pub fn set_env_var(primary: &str, compat: &str, value: &str) { std::env::set_var(primary, value); std::env::set_var(compat, value); } /// Normalise the environment we inherited, once, at startup. /// /// For every pair, if only the upstream spelling is set, copy it to ours. After /// this runs, code that only reads the `SL0PPIJ*` name still honours a user who /// exported `ZELLIJ_CONFIG_DIR` -- which matters most for the clap-derived /// arguments, since clap can only be told about one variable name. pub fn import_compat_env() { for (primary, compat) in ENV_PAIRS { if std::env::var_os(primary).is_none() { if let Some(value) = std::env::var_os(compat) { std::env::set_var(primary, value); } } } } #[cfg(test)] mod tests { use super::*; /// The whole point of the fork rename: nothing we create on disk may land in /// a path a stock zellij would also use. #[test] fn no_shipped_path_collides_with_upstream() { for path in [ HOME_CONFIG_LOCATION, SYSTEM_CONFIG_DIR, SYSTEM_CONFIG_DIR_WINDOWS, SYSTEM_DATA_DIR_PREFIX_WINDOWS, SYSTEM_DATA_SUBDIR, PROJ_APPLICATION, TMP_DIR_PREFIX, LOG_DIR_NAME, LOG_FILE_NAME, OLD_LOG_FILE_NAME, APP, ] { assert!( !path.to_lowercase().contains(UPSTREAM_APP), "{:?} still contains {:?} -- it would collide with a stock zellij", path, UPSTREAM_APP ); } } /// Ours must win when both are set, and the upstream name must still work /// on its own. #[test] fn env_var_precedence() { let primary = "SL0PPIJ_TEST_PRECEDENCE"; let compat = "ZELLIJ_TEST_PRECEDENCE"; std::env::remove_var(primary); std::env::set_var(compat, "upstream"); assert_eq!(env_var(primary, compat).unwrap(), "upstream"); std::env::set_var(primary, "ours"); assert_eq!(env_var(primary, compat).unwrap(), "ours"); std::env::remove_var(primary); std::env::remove_var(compat); assert!(env_var(primary, compat).is_err()); } #[test] fn set_env_var_sets_both_spellings() { let primary = "SL0PPIJ_TEST_BOTH"; let compat = "ZELLIJ_TEST_BOTH"; set_env_var(primary, compat, "v"); assert_eq!(std::env::var(primary).unwrap(), "v"); assert_eq!(std::env::var(compat).unwrap(), "v"); std::env::remove_var(primary); std::env::remove_var(compat); } /// Every pair must be (SL0PPIJ*, ZELLIJ*) and differ only in the prefix. #[test] fn env_pairs_are_consistent() { for (primary, compat) in ENV_PAIRS { assert!(primary.starts_with(ENV_MARKER), "{primary} misprefixed"); assert!(compat.starts_with(ENV_MARKER_COMPAT), "{compat} misprefixed"); assert_eq!( primary.strip_prefix(ENV_MARKER), compat.strip_prefix(ENV_MARKER_COMPAT), "{primary} and {compat} are not the same variable" ); } } }