using System.Text; #pragma warning disable IDE0130 // Namespace does not match folder structure namespace FireflyClient #pragma warning restore IDE0130 // Namespace does not match folder structure { /// /// Command-line interface for Firefly /// public class Program { static async Task Main(string[] args) { Console.WriteLine("Firefly Client"); Console.WriteLine("Enter commands (type EXIT to quit)"); Console.WriteLine("Use double quotes for values with spaces: HSET email:1 subject \"Hello World\""); Console.WriteLine("Type HELP for basic commands or HELP EMAIL for email examples"); // Parse command line arguments string host = "127.0.0.1"; int port = 6379; string password = string.Empty; for (int i = 0; i < args.Length; i++) { if ((args[i] == "--host" || args[i] == "-h") && i + 1 < args.Length) { host = args[++i]; } else if ((args[i] == "--port" || args[i] == "-p") && i + 1 < args.Length && int.TryParse(args[i + 1], out int parsedPort)) { port = parsedPort; i++; } else if ((args[i] == "--password" || args[i] == "--pass") && i + 1 < args.Length) { password = args[++i]; } else if (args[i] == "--help" || args[i] == "-?") { PrintClientHelp(); return; } } try { await RunInteractiveClientAsync(host, port, password); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } static void PrintClientHelp() { Console.WriteLine("\nFirefly Client Usage:"); Console.WriteLine(" --host, -h Server hostname or IP (default: 127.0.0.1)"); Console.WriteLine(" --port, -p Server port (default: 6379)"); Console.WriteLine(" --password, --pass Server password"); Console.WriteLine(" --help, -? Show this help message\n"); Console.WriteLine("Example: FireflyClient --host localhost --port 6380 --password secret123\n"); } static Task RunInteractiveClientAsync(string host, int port, string password) { using var client = new FireflyClient(host, port); Console.WriteLine($"Connecting to server at {host}:{port}..."); Console.WriteLine("Connected to server!"); // If password wasn't provided as a command line argument, prompt for it if (string.IsNullOrEmpty(password)) { Console.Write("Enter password (leave empty if no password required): "); password = Console.ReadLine() ?? string.Empty; } // Authenticate if password was provided if (!string.IsNullOrEmpty(password)) { bool success = client.Authenticate(password); // Check if authentication failed if (!success) { Console.WriteLine("Authentication failed. Disconnecting."); return Task.CompletedTask; } } while (true) { // Get command from user Console.Write("> "); string input = Console.ReadLine() ?? string.Empty; if (string.IsNullOrEmpty(input) || input.Equals("EXIT", StringComparison.OrdinalIgnoreCase)) { // Send QUIT command before exiting try { string response = client.ExecuteCommand("QUIT"); Console.WriteLine($"Server: {response.TrimEnd('\r', '\n')}"); } catch (Exception ex) { Console.WriteLine($"Error while quitting: {ex.Message}"); } break; } // Check for help commands if (input.Equals("HELP", StringComparison.OrdinalIgnoreCase)) { PrintBasicCommands(); continue; } // Handle clear screen command else if (input.Equals("CLS", StringComparison.OrdinalIgnoreCase) || input.Equals("CLEAR", StringComparison.OrdinalIgnoreCase)) { Console.Clear(); continue; } try { // Parse the command string[] parts = SplitCommandLine(input); if (parts.Length == 0) continue; string command = parts[0]; string[] args = [.. parts.Skip(1)]; // Execute the command string response = client.ExecuteCommand(command, args); FormatAndPrintResponse(response); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } Console.WriteLine("Disconnected from server."); return Task.CompletedTask; } // Helper method to split command line respecting quotes static string[] SplitCommandLine(string commandLine) { var result = new List(); bool inQuotes = false; StringBuilder currentArg = new(); for (int i = 0; i < commandLine.Length; i++) { char c = commandLine[i]; if (c == '"') { inQuotes = !inQuotes; // Don't include the quote character } else if (c == ' ' && !inQuotes) { // End of argument if (currentArg.Length > 0) { result.Add(currentArg.ToString()); currentArg.Clear(); } } else { currentArg.Append(c); } } // Add the last argument if any if (currentArg.Length > 0) { result.Add(currentArg.ToString()); } return [.. result]; } static void FormatAndPrintResponse(string response) { // Remove trailing whitespace response = response.TrimEnd(); if (response.StartsWith('*') && response.Contains("\r\n")) { // Format array responses for better readability string[] parts = response.Split("\r\n"); if (parts.Length > 1) { Console.WriteLine("Server: Array response:"); for (int i = 1; i < parts.Length; i++) { if (!string.IsNullOrEmpty(parts[i])) { // For simple string and bulk string responses in an array if (parts[i].StartsWith('+') || parts[i].StartsWith('$')) { Console.WriteLine($"{i}) {parts[i][1..]}"); } else if (!parts[i].StartsWith('*')) // Skip the array count line { Console.WriteLine($"{i}) {parts[i]}"); } } } return; } } // Default formatting Console.WriteLine($"Server: {response}"); } // Helper method: Print basic commands static void PrintBasicCommands() { Console.WriteLine("\n==== Basic Firefly Commands ===="); Console.WriteLine("\n## String Operations ##"); Console.WriteLine("SET key value # Set a string value"); Console.WriteLine("GET key # Get a string value"); Console.WriteLine("DEL key # Delete a key"); Console.WriteLine("SEXISTS key # Check if string key exists"); Console.WriteLine("\n## List Operations ##"); Console.WriteLine("LPUSH key value # Add value to the beginning of a list"); Console.WriteLine("RPUSH key value # Add value to the end of a list"); Console.WriteLine("LPOP key # Remove and return the first element"); Console.WriteLine("RPOP key # Remove and return the last element"); Console.WriteLine("LRANGE key start end # Get a range of elements (0 -1 for all)"); Console.WriteLine("LINDEX key index # Get element at specific position"); Console.WriteLine("LGET key [index] # Get entire list or element at index"); Console.WriteLine("LEXISTS key # Check if list key exists"); Console.WriteLine("\n## Hash Operations ##"); Console.WriteLine("HSET key field value # Set a field in a hash"); Console.WriteLine("HGET key field # Get a field from a hash"); Console.WriteLine("HDEL key field # Delete a field from a hash"); Console.WriteLine("HEXISTS key field # Check if field exists in hash"); Console.WriteLine("HGETALL key # Get all fields and values in a hash"); Console.WriteLine("HMSET key field1 val1... # Set multiple fields at once"); Console.WriteLine("HASKEY key # Check if hash key exists"); Console.WriteLine("\n## Server Operations ##"); Console.WriteLine("PING # Test connection"); Console.WriteLine("ECHO message # Echo a message"); Console.WriteLine("SAVE # Save database to disk"); Console.WriteLine("BGSAVE # Save database in background"); Console.WriteLine("QUIT # Gracefully disconnect from server"); Console.WriteLine("\n## Help Commands ##"); Console.WriteLine("HELP # Show this help message"); Console.WriteLine("HELP EMAIL # Show email storage examples"); Console.WriteLine("CLS, CLEAR # Clear the console screen"); Console.WriteLine("EXIT # Exit the client"); } } }