#[derive(Debug, Clone)]
pub struct TrustFlagConfig {
pub enabled: bool,
pub flag_template: String,
pub value_template: String,
pub aliases: Vec<TrustFlagAlias>,
pub show_in_help: bool,
pub help_template: String,
}
impl Default for TrustFlagConfig {
fn default() -> Self {
Self {
enabled: true,
flag_template: "--trust-{target}".into(),
value_template: "{name}".into(),
aliases: vec![],
show_in_help: true,
help_template: "Trust {target} '{name}' for this execution".into(),
}
}
}
impl TrustFlagConfig {
pub fn new() -> Self {
Self::default()
}
pub fn disabled() -> Self {
Self {
enabled: false,
..Self::default()
}
}
pub fn with_flag_template(mut self, template: impl Into<String>) -> Self {
self.flag_template = template.into();
self
}
pub fn with_value_template(mut self, template: impl Into<String>) -> Self {
self.value_template = template.into();
self
}
pub fn with_alias(mut self, flag: impl Into<String>, effect: TrustEffect) -> Self {
self.aliases.push(TrustFlagAlias {
flag: flag.into(),
description: effect.default_description(),
effect,
});
self
}
pub fn with_alias_desc(
mut self,
flag: impl Into<String>,
description: impl Into<String>,
effect: TrustEffect,
) -> Self {
self.aliases.push(TrustFlagAlias {
flag: flag.into(),
description: description.into(),
effect,
});
self
}
pub fn hidden(mut self) -> Self {
self.show_in_help = false;
self
}
pub fn generate_flag(&self, target: TrustTarget) -> String {
self.flag_template.replace("{target}", target.as_str())
}
pub fn generate_value(&self, name: &str) -> String {
self.value_template.replace("{name}", name)
}
pub fn generate_help(&self, target: TrustTarget, name: &str) -> String {
self.help_template
.replace("{target}", target.as_str())
.replace("{name}", name)
}
pub fn parse_args(&self, args: &[String]) -> TrustDirectives {
if !self.enabled {
return TrustDirectives::default();
}
let mut directives = TrustDirectives::default();
for arg in args {
for alias in &self.aliases {
if arg == &alias.flag {
match &alias.effect {
TrustEffect::TrustAll => directives.trust_all = true,
TrustEffect::TrustSession => directives.trust_session = true,
TrustEffect::TrustNamed { target, name } => match target {
TrustTarget::Plugin => {
directives.trusted_plugins.push(name.clone());
}
TrustTarget::Command => {
directives.trusted_commands.push(name.clone());
}
},
}
}
}
let plugin_flag = self.generate_flag(TrustTarget::Plugin);
let command_flag = self.generate_flag(TrustTarget::Command);
if let Some(value) = arg.strip_prefix(&format!("{}=", plugin_flag)) {
directives.trusted_plugins.push(value.to_string());
} else if let Some(value) = arg.strip_prefix(&format!("{}=", command_flag)) {
directives.trusted_commands.push(value.to_string());
}
}
directives
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TrustTarget {
Plugin,
Command,
}
impl TrustTarget {
fn as_str(&self) -> &'static str {
match self {
Self::Plugin => "plugin",
Self::Command => "command",
}
}
}
#[derive(Debug, Clone)]
pub struct TrustFlagAlias {
pub flag: String,
pub description: String,
pub effect: TrustEffect,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TrustEffect {
TrustNamed { target: TrustTarget, name: String },
TrustAll,
TrustSession,
}
impl TrustEffect {
fn default_description(&self) -> String {
match self {
Self::TrustNamed { target, name } => {
format!("Trust {} '{}'", target.as_str(), name)
}
Self::TrustAll => "Trust all plugins (dangerous)".into(),
Self::TrustSession => "Trust permissions for this session only".into(),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct TrustDirectives {
pub trusted_plugins: Vec<String>,
pub trusted_commands: Vec<String>,
pub trust_all: bool,
pub trust_session: bool,
}
impl TrustDirectives {
pub fn is_plugin_trusted(&self, name: &str) -> bool {
self.trust_all || self.trusted_plugins.iter().any(|p| p == name)
}
pub fn is_command_trusted(&self, name: &str) -> bool {
self.trust_all || self.trusted_commands.iter().any(|c| c == name)
}
pub fn has_any(&self) -> bool {
self.trust_all
|| self.trust_session
|| !self.trusted_plugins.is_empty()
|| !self.trusted_commands.is_empty()
}
}
pub struct TrustFlagPresets;
impl TrustFlagPresets {
pub fn standard() -> TrustFlagConfig {
TrustFlagConfig::default()
}
pub fn allow_style() -> TrustFlagConfig {
TrustFlagConfig::new().with_flag_template("--allow-{target}")
}
pub fn short_style() -> TrustFlagConfig {
TrustFlagConfig::new()
.with_flag_template("-t{target}")
.with_alias("-ta", TrustEffect::TrustAll)
}
pub fn k8s_style() -> TrustFlagConfig {
TrustFlagConfig::new()
.with_flag_template("--trust")
.with_value_template("{target}={name}")
}
pub fn disabled() -> TrustFlagConfig {
TrustFlagConfig::disabled()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = TrustFlagConfig::default();
assert!(config.enabled);
assert_eq!(config.generate_flag(TrustTarget::Plugin), "--trust-plugin");
assert_eq!(
config.generate_flag(TrustTarget::Command),
"--trust-command"
);
}
#[test]
fn test_custom_template() {
let config = TrustFlagConfig::new().with_flag_template("--allow-{target}");
assert_eq!(config.generate_flag(TrustTarget::Plugin), "--allow-plugin");
}
#[test]
fn test_parse_args() {
let config = TrustFlagConfig::default();
let args = vec![
"--trust-plugin=hello".into(),
"--trust-command=db:migrate".into(),
"other-arg".into(),
];
let directives = config.parse_args(&args);
assert!(directives.is_plugin_trusted("hello"));
assert!(directives.is_command_trusted("db:migrate"));
assert!(!directives.is_plugin_trusted("other"));
}
#[test]
fn test_alias() {
let config = TrustFlagConfig::new().with_alias("--yolo", TrustEffect::TrustAll);
let args = vec!["--yolo".into()];
let directives = config.parse_args(&args);
assert!(directives.trust_all);
assert!(directives.is_plugin_trusted("any-plugin"));
}
#[test]
fn test_disabled_config() {
let config = TrustFlagConfig::disabled();
let args = vec!["--trust-plugin=hello".into()];
let directives = config.parse_args(&args);
assert!(!directives.is_plugin_trusted("hello"));
}
#[test]
fn test_presets() {
let allow = TrustFlagPresets::allow_style();
assert_eq!(allow.generate_flag(TrustTarget::Plugin), "--allow-plugin");
let short = TrustFlagPresets::short_style();
assert_eq!(short.generate_flag(TrustTarget::Plugin), "-tplugin");
}
}