Skip to main content

gurty_cli/
command_handler.rs

1use crate::{
2    cli::ServeCommand,
3    config::GurtConfig,
4    server::FileServerBuilder,
5    Result,
6};
7use async_trait::async_trait;
8use colored::Colorize;
9use tracing::{error, info};
10
11#[async_trait]
12pub trait CommandHandler {
13    async fn execute(&self) -> Result<()>;
14}
15
16pub struct CommandHandlerBuilder {
17    logging_initialized: bool,
18    verbose: bool,
19}
20
21impl CommandHandlerBuilder {
22    pub fn new() -> Self {
23        Self {
24            logging_initialized: false,
25            verbose: false,
26        }
27    }
28
29    pub fn with_logging(mut self, verbose: bool) -> Self {
30        self.verbose = verbose;
31        self
32    }
33
34    pub fn initialize_logging(mut self) -> Self {
35        if !self.logging_initialized {
36            let level = if self.verbose {
37                tracing::Level::DEBUG
38            } else {
39                tracing::Level::INFO
40            };
41            
42            tracing_subscriber::fmt()
43                .with_max_level(level)
44                .init();
45            
46            self.logging_initialized = true;
47        }
48        self
49    }
50
51    pub fn build_serve_handler(self, serve_cmd: ServeCommand) -> ServeCommandHandler {
52        ServeCommandHandler::new(serve_cmd)
53    }
54}
55
56impl Default for CommandHandlerBuilder {
57    fn default() -> Self {
58        Self::new()
59    }
60}
61
62pub struct ServeCommandHandler {
63    serve_cmd: ServeCommand,
64}
65
66impl ServeCommandHandler {
67    pub fn new(serve_cmd: ServeCommand) -> Self {
68        Self { serve_cmd }
69    }
70
71    fn validate_command(&self) -> Result<()> {
72        if !self.serve_cmd.dir.exists() {
73            return Err(crate::ServerError::InvalidPath(
74                format!("Directory does not exist: {}", self.serve_cmd.dir.display())
75            ));
76        }
77
78        if !self.serve_cmd.dir.is_dir() {
79            return Err(crate::ServerError::InvalidPath(
80                format!("Path is not a directory: {}", self.serve_cmd.dir.display())
81            ));
82        }
83
84        Ok(())
85    }
86
87    fn build_server_config(&self) -> Result<GurtConfig> {
88        let mut config_builder = GurtConfig::builder();
89
90        if let Some(config_file) = &self.serve_cmd.config {
91            config_builder = config_builder.from_file(config_file)?;
92        }
93
94        let config = config_builder
95            .merge_cli_args(&self.serve_cmd)
96            .build()?;
97
98        Ok(config)
99    }
100
101    fn display_startup_info(&self, config: &GurtConfig) {
102        println!("{}", "GURT Protocol Server".bright_cyan().bold());
103        println!("{} {}", "Version".bright_blue(), config.server.protocol_version);
104        println!("{} {}", "Listening on".bright_blue(), config.address());
105        println!("{} {}", "Serving from".bright_blue(), config.server.base_directory.display());
106        
107        if config.tls.is_some() {
108            println!("{}", "TLS encryption enabled".bright_green());
109        }
110
111        if let Some(logging) = &config.logging {
112            println!("{} {}", "Log level".bright_blue(), logging.level);
113            if logging.log_requests {
114                println!("{}", "Request logging enabled".bright_green());
115            }
116        }
117
118        if let Some(security) = &config.security {
119            println!("{} {} req/min", "Rate limit".bright_blue(), security.rate_limit_requests);
120            if !security.deny_files.is_empty() {
121                println!("{} {} patterns", "File restrictions".bright_blue(), security.deny_files.len());
122            }
123        }
124
125        if let Some(headers) = &config.headers {
126            if !headers.is_empty() {
127                println!("{} {} headers", "Custom headers".bright_blue(), headers.len());
128            }
129        }
130
131        println!("{} {}", "Max connections".bright_blue(), config.server.max_connections);
132        println!("{} {}", "Max message size".bright_blue(), config.server.max_message_size);
133        println!();
134    }
135
136    async fn start_server(&self, config: &GurtConfig) -> Result<()> {
137        let server = FileServerBuilder::new(config.clone()).build()?;
138        
139        info!("Starting GURT server on {}", config.address());
140        
141        if let Err(e) = server.listen(&config.address()).await {
142            error!("Server error: {}", e);
143            std::process::exit(1);
144        }
145
146        Ok(())
147    }
148}
149
150#[async_trait]
151impl CommandHandler for ServeCommandHandler {
152    async fn execute(&self) -> Result<()> {
153        self.validate_command()?;
154        
155        let config = self.build_server_config()?;
156        
157        self.display_startup_info(&config);
158        self.start_server(&config).await
159    }
160}