use crate::adapters::{
HttpDataPublisher, LinuxSystemInfoProvider, MacOSSystemInfoProvider, UnixCommandExecutor,
};
use crate::domain::{HardwareCollectionService, ReportConfig};
use crate::ports::{
CommandExecutor, ConfigurationProvider, DataPublisher, HardwareReportingService,
SystemInfoProvider,
};
use std::error::Error;
use std::sync::Arc;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct ContainerConfig {
pub command_timeout: Duration,
pub retry_count: u32,
pub verbose: bool,
pub http_timeout: Duration,
pub skip_tls_verify: bool,
}
impl Default for ContainerConfig {
fn default() -> Self {
Self {
command_timeout: Duration::from_secs(30),
retry_count: 2,
verbose: false,
http_timeout: Duration::from_secs(30),
skip_tls_verify: false,
}
}
}
pub struct SimpleConfigurationProvider {
config: ReportConfig,
}
impl SimpleConfigurationProvider {
pub fn new(config: ReportConfig) -> Self {
Self { config }
}
}
#[async_trait::async_trait]
impl ConfigurationProvider for SimpleConfigurationProvider {
async fn get_report_config(&self) -> Result<ReportConfig, crate::domain::DomainError> {
Ok(self.config.clone())
}
async fn get_publish_config(
&self,
) -> Result<Option<crate::domain::PublishConfig>, crate::domain::DomainError> {
Ok(None) }
async fn get_output_format(
&self,
) -> Result<crate::ports::OutputFormat, crate::domain::DomainError> {
Ok(crate::ports::OutputFormat::Both)
}
async fn get_command_timeout(&self) -> Result<u64, crate::domain::DomainError> {
Ok(self.config.command_timeout)
}
async fn is_verbose_enabled(&self) -> Result<bool, crate::domain::DomainError> {
Ok(self.config.verbose)
}
async fn get_labels(
&self,
) -> Result<std::collections::HashMap<String, String>, crate::domain::DomainError> {
Ok(std::collections::HashMap::new())
}
}
pub struct ServiceContainer {
config: ContainerConfig,
}
impl ServiceContainer {
pub fn new(config: ContainerConfig) -> Self {
Self { config }
}
pub fn with_defaults() -> Self {
Self::new(ContainerConfig::default())
}
pub fn create_command_executor(&self) -> Arc<dyn CommandExecutor> {
Arc::new(UnixCommandExecutor::new(
self.config.command_timeout,
self.config.retry_count,
self.config.verbose,
))
}
pub fn create_system_info_provider(
&self,
) -> Result<Arc<dyn SystemInfoProvider>, Box<dyn Error>> {
let command_executor = self.create_command_executor();
let provider: Arc<dyn SystemInfoProvider> = if cfg!(target_os = "macos") {
Arc::new(MacOSSystemInfoProvider::new(command_executor))
} else if cfg!(target_os = "linux") {
Arc::new(LinuxSystemInfoProvider::new(command_executor))
} else {
return Err("Unsupported operating system".into());
};
Ok(provider)
}
pub fn create_data_publisher(&self) -> Result<Arc<dyn DataPublisher>, Box<dyn Error>> {
let http_publisher =
HttpDataPublisher::new(self.config.http_timeout, self.config.skip_tls_verify)?;
Ok(Arc::new(http_publisher))
}
pub fn create_configuration_provider(
&self,
report_config: ReportConfig,
) -> Arc<dyn ConfigurationProvider> {
Arc::new(SimpleConfigurationProvider::new(report_config))
}
pub fn create_hardware_reporting_service(
&self,
report_config: Option<ReportConfig>,
) -> Result<Arc<dyn HardwareReportingService>, Box<dyn Error>> {
let system_provider = self.create_system_info_provider()?;
let data_publisher = self.create_data_publisher()?;
let config_provider = self.create_configuration_provider(report_config.unwrap_or_default());
let service =
HardwareCollectionService::new(system_provider, data_publisher, config_provider);
Ok(Arc::new(service))
}
pub fn get_platform_name(&self) -> &'static str {
if cfg!(target_os = "macos") {
"macOS"
} else if cfg!(target_os = "linux") {
"Linux"
} else {
"Unknown"
}
}
pub async fn validate_dependencies(&self) -> Result<Vec<String>, Box<dyn Error>> {
let system_provider = self.create_system_info_provider()?;
let missing = system_provider
.get_missing_dependencies()
.await
.map_err(|e| format!("Failed to check dependencies: {e}"))?;
Ok(missing)
}
pub async fn check_privileges(&self) -> Result<bool, Box<dyn Error>> {
let system_provider = self.create_system_info_provider()?;
let has_privileges = system_provider
.has_required_privileges()
.await
.map_err(|e| format!("Failed to check privileges: {e}"))?;
Ok(has_privileges)
}
}
pub struct ContainerConfigBuilder {
config: ContainerConfig,
}
impl ContainerConfigBuilder {
pub fn new() -> Self {
Self {
config: ContainerConfig::default(),
}
}
pub fn command_timeout(mut self, timeout: Duration) -> Self {
self.config.command_timeout = timeout;
self
}
pub fn retry_count(mut self, count: u32) -> Self {
self.config.retry_count = count;
self
}
pub fn verbose(mut self, verbose: bool) -> Self {
self.config.verbose = verbose;
self
}
pub fn http_timeout(mut self, timeout: Duration) -> Self {
self.config.http_timeout = timeout;
self
}
pub fn skip_tls_verify(mut self, skip: bool) -> Self {
self.config.skip_tls_verify = skip;
self
}
pub fn build(self) -> ContainerConfig {
self.config
}
}
impl Default for ContainerConfigBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_container_creation() {
let container = ServiceContainer::with_defaults();
assert_eq!(
container.get_platform_name(),
if cfg!(target_os = "macos") {
"macOS"
} else {
"Linux"
}
);
}
#[test]
fn test_config_builder() {
let config = ContainerConfigBuilder::new()
.command_timeout(Duration::from_secs(60))
.retry_count(3)
.verbose(true)
.build();
assert_eq!(config.command_timeout, Duration::from_secs(60));
assert_eq!(config.retry_count, 3);
assert!(config.verbose);
}
#[test]
fn test_command_executor_creation() {
let container = ServiceContainer::with_defaults();
let executor = container.create_command_executor();
assert!(Arc::strong_count(&executor) >= 1);
}
#[tokio::test]
async fn test_system_info_provider_creation() {
let container = ServiceContainer::with_defaults();
let result = container.create_system_info_provider();
if cfg!(target_os = "macos") || cfg!(target_os = "linux") {
assert!(result.is_ok());
}
}
#[test]
fn test_data_publisher_creation() {
let container = ServiceContainer::with_defaults();
let result = container.create_data_publisher();
assert!(result.is_ok());
}
#[test]
fn test_complete_service_creation() {
let container = ServiceContainer::with_defaults();
let result = container.create_hardware_reporting_service(None);
if cfg!(target_os = "macos") || cfg!(target_os = "linux") {
assert!(result.is_ok());
}
}
}