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");
}
}
}