gurty-cli 0.1.0

GURT protocol server CLI tool
Documentation
use crate::{
    cli::ServeCommand,
    config::GurtConfig,
    server::FileServerBuilder,
    Result,
};
use async_trait::async_trait;
use colored::Colorize;
use tracing::{error, info};

#[async_trait]
pub trait CommandHandler {
    async fn execute(&self) -> Result<()>;
}

pub struct CommandHandlerBuilder {
    logging_initialized: bool,
    verbose: bool,
}

impl CommandHandlerBuilder {
    pub fn new() -> Self {
        Self {
            logging_initialized: false,
            verbose: false,
        }
    }

    pub fn with_logging(mut self, verbose: bool) -> Self {
        self.verbose = verbose;
        self
    }

    pub fn initialize_logging(mut self) -> Self {
        if !self.logging_initialized {
            let level = if self.verbose {
                tracing::Level::DEBUG
            } else {
                tracing::Level::INFO
            };
            
            tracing_subscriber::fmt()
                .with_max_level(level)
                .init();
            
            self.logging_initialized = true;
        }
        self
    }

    pub fn build_serve_handler(self, serve_cmd: ServeCommand) -> ServeCommandHandler {
        ServeCommandHandler::new(serve_cmd)
    }
}

impl Default for CommandHandlerBuilder {
    fn default() -> Self {
        Self::new()
    }
}

pub struct ServeCommandHandler {
    serve_cmd: ServeCommand,
}

impl ServeCommandHandler {
    pub fn new(serve_cmd: ServeCommand) -> Self {
        Self { serve_cmd }
    }

    fn validate_command(&self) -> Result<()> {
        if !self.serve_cmd.dir.exists() {
            return Err(crate::ServerError::InvalidPath(
                format!("Directory does not exist: {}", self.serve_cmd.dir.display())
            ));
        }

        if !self.serve_cmd.dir.is_dir() {
            return Err(crate::ServerError::InvalidPath(
                format!("Path is not a directory: {}", self.serve_cmd.dir.display())
            ));
        }

        Ok(())
    }

    fn build_server_config(&self) -> Result<GurtConfig> {
        let mut config_builder = GurtConfig::builder();

        if let Some(config_file) = &self.serve_cmd.config {
            config_builder = config_builder.from_file(config_file)?;
        }

        let config = config_builder
            .merge_cli_args(&self.serve_cmd)
            .build()?;

        Ok(config)
    }

    fn display_startup_info(&self, config: &GurtConfig) {
        println!("{}", "GURT Protocol Server".bright_cyan().bold());
        println!("{} {}", "Version".bright_blue(), config.server.protocol_version);
        println!("{} {}", "Listening on".bright_blue(), config.address());
        println!("{} {}", "Serving from".bright_blue(), config.server.base_directory.display());
        
        if config.tls.is_some() {
            println!("{}", "TLS encryption enabled".bright_green());
        }

        if let Some(logging) = &config.logging {
            println!("{} {}", "Log level".bright_blue(), logging.level);
            if logging.log_requests {
                println!("{}", "Request logging enabled".bright_green());
            }
        }

        if let Some(security) = &config.security {
            println!("{} {} req/min", "Rate limit".bright_blue(), security.rate_limit_requests);
            if !security.deny_files.is_empty() {
                println!("{} {} patterns", "File restrictions".bright_blue(), security.deny_files.len());
            }
        }

        if let Some(headers) = &config.headers {
            if !headers.is_empty() {
                println!("{} {} headers", "Custom headers".bright_blue(), headers.len());
            }
        }

        println!("{} {}", "Max connections".bright_blue(), config.server.max_connections);
        println!("{} {}", "Max message size".bright_blue(), config.server.max_message_size);
        println!();
    }

    async fn start_server(&self, config: &GurtConfig) -> Result<()> {
        let server = FileServerBuilder::new(config.clone()).build()?;
        
        info!("Starting GURT server on {}", config.address());
        
        if let Err(e) = server.listen(&config.address()).await {
            error!("Server error: {}", e);
            std::process::exit(1);
        }

        Ok(())
    }
}

#[async_trait]
impl CommandHandler for ServeCommandHandler {
    async fn execute(&self) -> Result<()> {
        self.validate_command()?;
        
        let config = self.build_server_config()?;
        
        self.display_startup_info(&config);
        self.start_server(&config).await
    }
}