use async_trait::async_trait;
use clap::Command;
use cuenv_core::Result;
use cuenv_core::manifest::Base;
use std::any::Any;
use std::path::Path;
use crate::commands::CommandExecutor;
use crate::commands::sync::provider::{SyncMode, SyncOptions, SyncResult};
pub trait Provider: Send + Sync + 'static {
fn name(&self) -> &'static str;
fn description(&self) -> &'static str;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
#[async_trait]
pub trait SyncCapability: Provider {
fn build_sync_command(&self) -> Command;
async fn sync_path(
&self,
path: &Path,
package: &str,
options: &SyncOptions,
executor: &CommandExecutor,
) -> Result<SyncResult>;
async fn sync_workspace(
&self,
package: &str,
options: &SyncOptions,
executor: &CommandExecutor,
) -> Result<SyncResult>;
fn has_config(&self, manifest: &Base) -> bool;
fn parse_sync_args(&self, matches: &clap::ArgMatches) -> SyncOptions {
let mode = if matches.get_flag("dry-run") {
SyncMode::DryRun
} else if matches.get_flag("check") {
SyncMode::Check
} else {
SyncMode::Write
};
SyncOptions {
mode,
show_diff: matches
.try_get_one::<bool>("diff")
.ok()
.flatten()
.copied()
.unwrap_or(false),
ci_provider: matches
.try_get_one::<String>("provider")
.ok()
.flatten()
.cloned(),
update_tools: None,
}
}
}
#[async_trait]
pub trait RuntimeCapability: Provider {
async fn execute_task(&self, task_name: &str, executor: &CommandExecutor) -> Result<String>;
fn can_handle(&self, task_name: &str) -> bool;
}
#[async_trait]
pub trait SecretCapability: Provider {
async fn resolve(&self, reference: &str) -> Result<String>;
fn can_resolve(&self, reference: &str) -> bool;
}
#[cfg(test)]
mod tests {
use super::*;
struct TestProvider;
impl Provider for TestProvider {
fn name(&self) -> &'static str {
"test"
}
fn description(&self) -> &'static str {
"Test provider"
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[test]
fn test_provider_name() {
let provider = TestProvider;
assert_eq!(provider.name(), "test");
}
#[test]
fn test_provider_description() {
let provider = TestProvider;
assert_eq!(provider.description(), "Test provider");
}
#[test]
fn test_provider_as_any() {
let provider = TestProvider;
let any = provider.as_any();
assert!(any.is::<TestProvider>());
}
#[test]
fn test_provider_as_any_mut() {
let mut provider = TestProvider;
let any = provider.as_any_mut();
assert!(any.is::<TestProvider>());
}
#[test]
fn test_provider_as_any_wrong_type() {
let provider = TestProvider;
let any = provider.as_any();
assert!(!any.is::<String>());
}
#[test]
fn test_provider_downcast() {
let provider = TestProvider;
let any = provider.as_any();
let downcasted = any.downcast_ref::<TestProvider>();
assert!(downcasted.is_some());
}
#[test]
fn test_sync_mode_debug() {
assert_eq!(format!("{:?}", SyncMode::Write), "Write");
assert_eq!(format!("{:?}", SyncMode::DryRun), "DryRun");
assert_eq!(format!("{:?}", SyncMode::Check), "Check");
}
#[test]
fn test_sync_mode_clone() {
let mode = SyncMode::DryRun;
let cloned = mode;
assert!(matches!(cloned, SyncMode::DryRun));
}
#[test]
fn test_sync_mode_eq() {
assert_eq!(SyncMode::Write, SyncMode::Write);
assert_ne!(SyncMode::Write, SyncMode::DryRun);
}
#[test]
fn test_sync_options_default() {
let options = SyncOptions {
mode: SyncMode::Write,
show_diff: false,
ci_provider: None,
update_tools: None,
};
assert!(!options.show_diff);
assert!(options.ci_provider.is_none());
}
#[test]
fn test_sync_options_with_provider() {
let options = SyncOptions {
mode: SyncMode::Check,
show_diff: true,
ci_provider: Some("github".to_string()),
update_tools: None,
};
assert_eq!(options.ci_provider, Some("github".to_string()));
assert!(options.show_diff);
}
#[test]
fn test_sync_options_dry_run() {
let options = SyncOptions {
mode: SyncMode::DryRun,
show_diff: false,
ci_provider: None,
update_tools: None,
};
assert!(matches!(options.mode, SyncMode::DryRun));
}
#[test]
fn test_sync_options_clone() {
let options = SyncOptions {
mode: SyncMode::Write,
show_diff: true,
ci_provider: Some("buildkite".to_string()),
update_tools: Some(vec!["bun".to_string()]),
};
let cloned = options.clone();
assert_eq!(cloned.ci_provider, Some("buildkite".to_string()));
assert_eq!(cloned.update_tools, Some(vec!["bun".to_string()]));
}
#[test]
fn test_sync_result_success() {
let result = SyncResult::success("test.yaml created");
assert!(!result.had_error);
assert!(result.output.contains("test.yaml"));
}
#[test]
fn test_sync_result_error() {
let result = SyncResult::error("failed to sync");
assert!(result.had_error);
assert!(result.output.contains("failed"));
}
#[test]
fn test_sync_result_clone() {
let result = SyncResult::success("cloned result");
let cloned = result.clone();
assert_eq!(cloned.output, "cloned result");
assert!(!cloned.had_error);
}
#[test]
fn test_sync_result_debug() {
let result = SyncResult::success("debug test");
let debug_str = format!("{:?}", result);
assert!(debug_str.contains("debug test"));
}
}