pub const AUTOMATIC_VARIABLES: &[&str] = &[
"$",
"?",
"^",
"_",
"args",
"ConsoleFileName",
"EnabledExperimentalFeatures",
"Error",
"Event",
"EventArgs",
"EventSubscriber",
"ExecutionContext",
"false",
"foreach",
"HOME",
"Host",
"input",
"IsCoreCLR",
"IsLinux",
"IsMacOS",
"IsWindows",
"LASTEXITCODE",
"Matches",
"MyInvocation",
"NestedPromptLevel",
"null",
"PID",
"PROFILE",
"PSBoundParameters",
"PSCmdlet",
"PSCommandPath",
"PSCulture",
"PSDebugContext",
"PSHOME",
"PSItem",
"PSScriptRoot",
"PSSenderInfo",
"PSStyle",
"PSUICulture",
"PSVersionTable",
"PWD",
"Sender",
"ShellId",
"StackTrace",
"switch",
"this",
"true",
];
pub const PREFERENCE_VARIABLES: &[&str] = &[
"ConfirmPreference",
"DebugPreference",
"ErrorActionPreference",
"ErrorView",
"FormatEnumerationLimit",
"InformationPreference",
"LogCommandHealthEvent",
"LogCommandLifecycleEvent",
"LogEngineHealthEvent",
"LogEngineLifecycleEvent",
"LogProviderHealthEvent",
"LogProviderLifecycleEvent",
"MaximumHistoryCount",
"OFS",
"OutputEncoding",
"ProgressPreference",
"PSDefaultParameterValues",
"PSEmailServer",
"PSModuleAutoLoadingPreference",
"PSNativeCommandArgumentPassing",
"PSNativeCommandUseErrorActionPreference",
"PSSessionApplicationName",
"PSSessionConfigurationName",
"PSSessionOption",
"Transcript",
"VerbosePreference",
"WarningPreference",
"WhatIfPreference",
];
fn strip_sigil(raw: &str) -> &str {
let s = raw.strip_prefix('@').unwrap_or(raw);
let s = s.strip_prefix('$').unwrap_or(s);
s.strip_prefix('{')
.and_then(|inner| inner.strip_suffix('}'))
.unwrap_or(s)
}
pub fn variable_name(raw: &str) -> &str {
match strip_sigil(raw).rsplit_once(':') {
Some((_, name)) => name,
None => strip_sigil(raw),
}
}
pub fn variable_scope(raw: &str) -> Option<&str> {
strip_sigil(raw).rsplit_once(':').map(|(scope, _)| scope)
}
pub fn is_automatic_variable(name: &str) -> bool {
let bare = variable_name(name);
AUTOMATIC_VARIABLES
.iter()
.any(|a| a.eq_ignore_ascii_case(bare))
}
pub fn is_preference_variable(name: &str) -> bool {
let bare = variable_name(name);
PREFERENCE_VARIABLES
.iter()
.any(|p| p.eq_ignore_ascii_case(bare))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn automatic_word_variables() {
for raw in [
"$args",
"$PSItem",
"$PSScriptRoot",
"$MyInvocation",
"$IsWindows",
"$null",
"$true",
"$false",
] {
assert!(is_automatic_variable(raw), "{raw} should be automatic");
}
}
#[test]
fn automatic_punctuation_variables() {
assert!(is_automatic_variable("$_"));
assert!(is_automatic_variable("$?"));
assert!(is_automatic_variable("$$"));
assert!(is_automatic_variable("$^"));
}
#[test]
fn accepts_names_without_a_sigil() {
assert!(is_automatic_variable("PSItem"));
assert!(is_automatic_variable("_"));
}
#[test]
fn classification_is_case_insensitive() {
assert!(is_automatic_variable("$True"));
assert!(is_automatic_variable("$psitem"));
assert!(is_automatic_variable("$PSSCRIPTROOT"));
}
#[test]
fn user_variables_are_not_automatic() {
for raw in ["$x", "$LogDir", "$myArgs", "$arguments", "$path"] {
assert!(!is_automatic_variable(raw), "{raw} should not be automatic");
}
}
#[test]
fn scope_qualifier_is_stripped_before_classifying() {
assert!(is_automatic_variable("$script:_"));
assert!(!is_automatic_variable("$script:Config"));
}
#[test]
fn preference_variables() {
assert!(is_preference_variable("$ErrorActionPreference"));
assert!(is_preference_variable("$VerbosePreference"));
assert!(!is_preference_variable("$x"));
assert!(!is_automatic_variable("$ErrorActionPreference"));
}
#[test]
fn variable_name_strips_sigil_and_scope() {
assert_eq!(variable_name("$x"), "x");
assert_eq!(variable_name("$script:Config"), "Config");
assert_eq!(variable_name("$env:PATH"), "PATH");
assert_eq!(variable_name("@args"), "args");
assert_eq!(variable_name("@$args"), "args");
assert_eq!(variable_name("${x}"), "x");
assert_eq!(variable_name("$_"), "_");
assert_eq!(variable_name("$$"), "$");
}
#[test]
fn variable_scope_reads_the_qualifier() {
assert_eq!(variable_scope("$script:Config"), Some("script"));
assert_eq!(variable_scope("$env:PATH"), Some("env"));
assert_eq!(variable_scope("$x"), None);
assert_eq!(variable_scope("$_"), None);
}
}