use crate::Codex;
use crate::command::CodexCommand;
use crate::error::Result;
use crate::exec::{self, CommandOutput};
fn push_config(
args: &mut Vec<String>,
config_overrides: &[String],
enabled: &[String],
disabled: &[String],
) {
for value in config_overrides {
args.push("-c".into());
args.push(value.clone());
}
for value in enabled {
args.push("--enable".into());
args.push(value.clone());
}
for value in disabled {
args.push("--disable".into());
args.push(value.clone());
}
}
#[derive(Debug, Clone)]
pub struct PluginAddCommand {
selector: String,
marketplace: Option<String>,
config_overrides: Vec<String>,
enabled_features: Vec<String>,
disabled_features: Vec<String>,
retry_policy: Option<crate::retry::RetryPolicy>,
}
impl PluginAddCommand {
#[must_use]
pub fn new(selector: impl Into<String>) -> Self {
Self {
selector: selector.into(),
marketplace: None,
config_overrides: Vec::new(),
enabled_features: Vec::new(),
disabled_features: Vec::new(),
retry_policy: None,
}
}
#[must_use]
pub fn marketplace(mut self, name: impl Into<String>) -> Self {
self.marketplace = Some(name.into());
self
}
#[must_use]
pub fn config(mut self, key_value: impl Into<String>) -> Self {
self.config_overrides.push(key_value.into());
self
}
#[must_use]
pub fn enable(mut self, feature: impl Into<String>) -> Self {
self.enabled_features.push(feature.into());
self
}
#[must_use]
pub fn disable(mut self, feature: impl Into<String>) -> Self {
self.disabled_features.push(feature.into());
self
}
#[must_use]
pub fn retry(mut self, policy: crate::retry::RetryPolicy) -> Self {
self.retry_policy = Some(policy);
self
}
}
impl CodexCommand for PluginAddCommand {
type Output = CommandOutput;
fn args(&self) -> Vec<String> {
let mut args = vec!["plugin".to_string(), "add".to_string()];
push_config(
&mut args,
&self.config_overrides,
&self.enabled_features,
&self.disabled_features,
);
if let Some(name) = &self.marketplace {
args.push("--marketplace".into());
args.push(name.clone());
}
args.push(self.selector.clone());
args
}
async fn execute(&self, codex: &Codex) -> Result<CommandOutput> {
exec::run_codex_with_retry(codex, self.args(), self.retry_policy.as_ref()).await
}
}
#[derive(Debug, Clone)]
pub struct PluginListCommand {
marketplace: Option<String>,
json: bool,
available: bool,
config_overrides: Vec<String>,
enabled_features: Vec<String>,
disabled_features: Vec<String>,
retry_policy: Option<crate::retry::RetryPolicy>,
}
impl PluginListCommand {
#[must_use]
pub fn new() -> Self {
Self {
marketplace: None,
json: false,
available: false,
config_overrides: Vec::new(),
enabled_features: Vec::new(),
disabled_features: Vec::new(),
retry_policy: None,
}
}
#[must_use]
pub fn marketplace(mut self, name: impl Into<String>) -> Self {
self.marketplace = Some(name.into());
self
}
#[must_use]
pub fn json(mut self) -> Self {
self.json = true;
self
}
#[must_use]
pub fn available(mut self) -> Self {
self.available = true;
self
}
#[must_use]
pub fn config(mut self, key_value: impl Into<String>) -> Self {
self.config_overrides.push(key_value.into());
self
}
#[must_use]
pub fn enable(mut self, feature: impl Into<String>) -> Self {
self.enabled_features.push(feature.into());
self
}
#[must_use]
pub fn disable(mut self, feature: impl Into<String>) -> Self {
self.disabled_features.push(feature.into());
self
}
#[must_use]
pub fn retry(mut self, policy: crate::retry::RetryPolicy) -> Self {
self.retry_policy = Some(policy);
self
}
}
impl Default for PluginListCommand {
fn default() -> Self {
Self::new()
}
}
impl CodexCommand for PluginListCommand {
type Output = CommandOutput;
fn args(&self) -> Vec<String> {
let mut args = vec!["plugin".to_string(), "list".to_string()];
push_config(
&mut args,
&self.config_overrides,
&self.enabled_features,
&self.disabled_features,
);
if let Some(name) = &self.marketplace {
args.push("--marketplace".into());
args.push(name.clone());
}
if self.json {
args.push("--json".into());
}
if self.available {
args.push("--available".into());
}
args
}
async fn execute(&self, codex: &Codex) -> Result<CommandOutput> {
exec::run_codex_with_retry(codex, self.args(), self.retry_policy.as_ref()).await
}
}
#[derive(Debug, Clone)]
pub struct PluginRemoveCommand {
selector: String,
marketplace: Option<String>,
config_overrides: Vec<String>,
enabled_features: Vec<String>,
disabled_features: Vec<String>,
retry_policy: Option<crate::retry::RetryPolicy>,
}
impl PluginRemoveCommand {
#[must_use]
pub fn new(selector: impl Into<String>) -> Self {
Self {
selector: selector.into(),
marketplace: None,
config_overrides: Vec::new(),
enabled_features: Vec::new(),
disabled_features: Vec::new(),
retry_policy: None,
}
}
#[must_use]
pub fn marketplace(mut self, name: impl Into<String>) -> Self {
self.marketplace = Some(name.into());
self
}
#[must_use]
pub fn config(mut self, key_value: impl Into<String>) -> Self {
self.config_overrides.push(key_value.into());
self
}
#[must_use]
pub fn enable(mut self, feature: impl Into<String>) -> Self {
self.enabled_features.push(feature.into());
self
}
#[must_use]
pub fn disable(mut self, feature: impl Into<String>) -> Self {
self.disabled_features.push(feature.into());
self
}
#[must_use]
pub fn retry(mut self, policy: crate::retry::RetryPolicy) -> Self {
self.retry_policy = Some(policy);
self
}
}
impl CodexCommand for PluginRemoveCommand {
type Output = CommandOutput;
fn args(&self) -> Vec<String> {
let mut args = vec!["plugin".to_string(), "remove".to_string()];
push_config(
&mut args,
&self.config_overrides,
&self.enabled_features,
&self.disabled_features,
);
if let Some(name) = &self.marketplace {
args.push("--marketplace".into());
args.push(name.clone());
}
args.push(self.selector.clone());
args
}
async fn execute(&self, codex: &Codex) -> Result<CommandOutput> {
exec::run_codex_with_retry(codex, self.args(), self.retry_policy.as_ref()).await
}
}
#[derive(Debug, Clone)]
pub struct PluginMarketplaceAddCommand {
source: String,
git_ref: Option<String>,
config_overrides: Vec<String>,
enabled_features: Vec<String>,
disabled_features: Vec<String>,
retry_policy: Option<crate::retry::RetryPolicy>,
}
impl PluginMarketplaceAddCommand {
#[must_use]
pub fn new(source: impl Into<String>) -> Self {
Self {
source: source.into(),
git_ref: None,
config_overrides: Vec::new(),
enabled_features: Vec::new(),
disabled_features: Vec::new(),
retry_policy: None,
}
}
#[must_use]
pub fn git_ref(mut self, git_ref: impl Into<String>) -> Self {
self.git_ref = Some(git_ref.into());
self
}
#[must_use]
pub fn config(mut self, key_value: impl Into<String>) -> Self {
self.config_overrides.push(key_value.into());
self
}
#[must_use]
pub fn enable(mut self, feature: impl Into<String>) -> Self {
self.enabled_features.push(feature.into());
self
}
#[must_use]
pub fn disable(mut self, feature: impl Into<String>) -> Self {
self.disabled_features.push(feature.into());
self
}
#[must_use]
pub fn retry(mut self, policy: crate::retry::RetryPolicy) -> Self {
self.retry_policy = Some(policy);
self
}
}
impl CodexCommand for PluginMarketplaceAddCommand {
type Output = CommandOutput;
fn args(&self) -> Vec<String> {
let mut args = vec![
"plugin".to_string(),
"marketplace".to_string(),
"add".to_string(),
];
push_config(
&mut args,
&self.config_overrides,
&self.enabled_features,
&self.disabled_features,
);
if let Some(git_ref) = &self.git_ref {
args.push("--ref".into());
args.push(git_ref.clone());
}
args.push(self.source.clone());
args
}
async fn execute(&self, codex: &Codex) -> Result<CommandOutput> {
exec::run_codex_with_retry(codex, self.args(), self.retry_policy.as_ref()).await
}
}
#[derive(Debug, Clone)]
pub struct PluginMarketplaceListCommand {
json: bool,
config_overrides: Vec<String>,
enabled_features: Vec<String>,
disabled_features: Vec<String>,
retry_policy: Option<crate::retry::RetryPolicy>,
}
impl PluginMarketplaceListCommand {
#[must_use]
pub fn new() -> Self {
Self {
json: false,
config_overrides: Vec::new(),
enabled_features: Vec::new(),
disabled_features: Vec::new(),
retry_policy: None,
}
}
#[must_use]
pub fn json(mut self) -> Self {
self.json = true;
self
}
#[must_use]
pub fn config(mut self, key_value: impl Into<String>) -> Self {
self.config_overrides.push(key_value.into());
self
}
#[must_use]
pub fn enable(mut self, feature: impl Into<String>) -> Self {
self.enabled_features.push(feature.into());
self
}
#[must_use]
pub fn disable(mut self, feature: impl Into<String>) -> Self {
self.disabled_features.push(feature.into());
self
}
#[must_use]
pub fn retry(mut self, policy: crate::retry::RetryPolicy) -> Self {
self.retry_policy = Some(policy);
self
}
}
impl Default for PluginMarketplaceListCommand {
fn default() -> Self {
Self::new()
}
}
impl CodexCommand for PluginMarketplaceListCommand {
type Output = CommandOutput;
fn args(&self) -> Vec<String> {
let mut args = vec![
"plugin".to_string(),
"marketplace".to_string(),
"list".to_string(),
];
push_config(
&mut args,
&self.config_overrides,
&self.enabled_features,
&self.disabled_features,
);
if self.json {
args.push("--json".into());
}
args
}
async fn execute(&self, codex: &Codex) -> Result<CommandOutput> {
exec::run_codex_with_retry(codex, self.args(), self.retry_policy.as_ref()).await
}
}
#[derive(Debug, Clone)]
pub struct PluginMarketplaceUpgradeCommand {
name: Option<String>,
json: bool,
config_overrides: Vec<String>,
enabled_features: Vec<String>,
disabled_features: Vec<String>,
retry_policy: Option<crate::retry::RetryPolicy>,
}
impl PluginMarketplaceUpgradeCommand {
#[must_use]
pub fn new() -> Self {
Self {
name: None,
json: false,
config_overrides: Vec::new(),
enabled_features: Vec::new(),
disabled_features: Vec::new(),
retry_policy: None,
}
}
#[must_use]
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
#[must_use]
pub fn json(mut self) -> Self {
self.json = true;
self
}
#[must_use]
pub fn config(mut self, key_value: impl Into<String>) -> Self {
self.config_overrides.push(key_value.into());
self
}
#[must_use]
pub fn enable(mut self, feature: impl Into<String>) -> Self {
self.enabled_features.push(feature.into());
self
}
#[must_use]
pub fn disable(mut self, feature: impl Into<String>) -> Self {
self.disabled_features.push(feature.into());
self
}
#[must_use]
pub fn retry(mut self, policy: crate::retry::RetryPolicy) -> Self {
self.retry_policy = Some(policy);
self
}
}
impl Default for PluginMarketplaceUpgradeCommand {
fn default() -> Self {
Self::new()
}
}
impl CodexCommand for PluginMarketplaceUpgradeCommand {
type Output = CommandOutput;
fn args(&self) -> Vec<String> {
let mut args = vec![
"plugin".to_string(),
"marketplace".to_string(),
"upgrade".to_string(),
];
push_config(
&mut args,
&self.config_overrides,
&self.enabled_features,
&self.disabled_features,
);
if self.json {
args.push("--json".into());
}
if let Some(name) = &self.name {
args.push(name.clone());
}
args
}
async fn execute(&self, codex: &Codex) -> Result<CommandOutput> {
exec::run_codex_with_retry(codex, self.args(), self.retry_policy.as_ref()).await
}
}
#[derive(Debug, Clone)]
pub struct PluginMarketplaceRemoveCommand {
name: String,
json: bool,
config_overrides: Vec<String>,
enabled_features: Vec<String>,
disabled_features: Vec<String>,
retry_policy: Option<crate::retry::RetryPolicy>,
}
impl PluginMarketplaceRemoveCommand {
#[must_use]
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
json: false,
config_overrides: Vec::new(),
enabled_features: Vec::new(),
disabled_features: Vec::new(),
retry_policy: None,
}
}
#[must_use]
pub fn json(mut self) -> Self {
self.json = true;
self
}
#[must_use]
pub fn config(mut self, key_value: impl Into<String>) -> Self {
self.config_overrides.push(key_value.into());
self
}
#[must_use]
pub fn enable(mut self, feature: impl Into<String>) -> Self {
self.enabled_features.push(feature.into());
self
}
#[must_use]
pub fn disable(mut self, feature: impl Into<String>) -> Self {
self.disabled_features.push(feature.into());
self
}
#[must_use]
pub fn retry(mut self, policy: crate::retry::RetryPolicy) -> Self {
self.retry_policy = Some(policy);
self
}
}
impl CodexCommand for PluginMarketplaceRemoveCommand {
type Output = CommandOutput;
fn args(&self) -> Vec<String> {
let mut args = vec![
"plugin".to_string(),
"marketplace".to_string(),
"remove".to_string(),
];
push_config(
&mut args,
&self.config_overrides,
&self.enabled_features,
&self.disabled_features,
);
if self.json {
args.push("--json".into());
}
args.push(self.name.clone());
args
}
async fn execute(&self, codex: &Codex) -> Result<CommandOutput> {
exec::run_codex_with_retry(codex, self.args(), self.retry_policy.as_ref()).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn plugin_add_args() {
let args = PluginAddCommand::new("hello@official").args();
assert_eq!(args, vec!["plugin", "add", "hello@official"]);
}
#[test]
fn plugin_add_args_with_marketplace_and_config() {
let args = PluginAddCommand::new("hello")
.marketplace("official")
.config("foo=bar")
.args();
assert_eq!(
args,
vec![
"plugin",
"add",
"-c",
"foo=bar",
"--marketplace",
"official",
"hello",
]
);
}
#[test]
fn plugin_list_args() {
let args = PluginListCommand::new().json().available().args();
assert_eq!(args, vec!["plugin", "list", "--json", "--available"]);
}
#[test]
fn plugin_remove_args() {
let args = PluginRemoveCommand::new("hello")
.marketplace("official")
.args();
assert_eq!(
args,
vec!["plugin", "remove", "--marketplace", "official", "hello"]
);
}
#[test]
fn marketplace_add_args() {
let args = PluginMarketplaceAddCommand::new("owner/repo")
.git_ref("main")
.args();
assert_eq!(
args,
vec![
"plugin",
"marketplace",
"add",
"--ref",
"main",
"owner/repo",
]
);
}
#[test]
fn marketplace_list_args() {
let args = PluginMarketplaceListCommand::new().json().args();
assert_eq!(args, vec!["plugin", "marketplace", "list", "--json"]);
}
#[test]
fn marketplace_upgrade_args_all() {
let args = PluginMarketplaceUpgradeCommand::new().args();
assert_eq!(args, vec!["plugin", "marketplace", "upgrade"]);
}
#[test]
fn marketplace_upgrade_args_named() {
let args = PluginMarketplaceUpgradeCommand::new()
.name("official")
.args();
assert_eq!(args, vec!["plugin", "marketplace", "upgrade", "official"]);
}
#[test]
fn marketplace_remove_args() {
let args = PluginMarketplaceRemoveCommand::new("official")
.json()
.args();
assert_eq!(
args,
vec!["plugin", "marketplace", "remove", "--json", "official"]
);
}
}