add project

This commit is contained in:
GuilhermeStrice
2025-07-09 19:31:34 +01:00
parent 8d2e88edf4
commit f37078157d
44 changed files with 7680 additions and 0 deletions

View File

@ -0,0 +1,302 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
namespace RedisManager.Commands
{
/// <summary>
/// Contains command line options and implementations for advanced Redis string operations.
/// Provides functionality for APPEND, INCR, DECR, INCRBY, DECRBY, INCRBYFLOAT, GETRANGE, SETRANGE, STRLEN, MGET, and MSET commands.
/// </summary>
[Verb("append", HelpText = "Append a value to a key.")]
public class AppendOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
public string Key { get; set; }
[Value(1, MetaName = "value", Required = true, HelpText = "Value to append.")]
public string Value { get; set; }
}
[Verb("incr", HelpText = "Increment the integer value of a key by one.")]
public class IncrOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
public string Key { get; set; }
}
[Verb("decr", HelpText = "Decrement the integer value of a key by one.")]
public class DecrOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
public string Key { get; set; }
}
[Verb("incrby", HelpText = "Increment the integer value of a key by a given amount.")]
public class IncrByOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
public string Key { get; set; }
[Value(1, MetaName = "amount", Required = true, HelpText = "Amount to increment by.")]
public long Amount { get; set; }
}
[Verb("decrby", HelpText = "Decrement the integer value of a key by a given amount.")]
public class DecrByOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
public string Key { get; set; }
[Value(1, MetaName = "amount", Required = true, HelpText = "Amount to decrement by.")]
public long Amount { get; set; }
}
[Verb("incrbyfloat", HelpText = "Increment the float value of a key by a given amount.")]
public class IncrByFloatOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
public string Key { get; set; }
[Value(1, MetaName = "amount", Required = true, HelpText = "Amount to increment by.")]
public double Amount { get; set; }
}
[Verb("getrange", HelpText = "Get a substring of the string stored at a key.")]
public class GetRangeOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
public string Key { get; set; }
[Value(1, MetaName = "start", Required = true, HelpText = "Start offset.")]
public long Start { get; set; }
[Value(2, MetaName = "end", Required = true, HelpText = "End offset.")]
public long End { get; set; }
}
[Verb("setrange", HelpText = "Overwrite part of a string at key starting at the specified offset.")]
public class SetRangeOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
public string Key { get; set; }
[Value(1, MetaName = "offset", Required = true, HelpText = "Offset.")]
public long Offset { get; set; }
[Value(2, MetaName = "value", Required = true, HelpText = "Value to set.")]
public string Value { get; set; }
}
[Verb("strlen", HelpText = "Get the length of the value stored in a key.")]
public class StrLenOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
public string Key { get; set; }
}
[Verb("mget", HelpText = "Get the values of all the given keys.")]
public class MGetOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "Keys.")]
public IEnumerable<string> Keys { get; set; }
}
[Verb("mset", HelpText = "Set multiple keys to multiple values.")]
public class MSetOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Option("pairs", Required = true, HelpText = "Comma-separated key=value pairs.")]
public string Pairs { get; set; }
}
public static class AdvancedStringCommands
{
/// <summary>
/// Executes the APPEND command to append a value to a key.
/// </summary>
/// <param name="opts">The AppendOptions containing instance, key, and value.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunAppend(AppendOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var len = db.StringAppend(opts.Key, opts.Value);
Console.WriteLine(Output.Green(len.ToString()));
return 0;
}
/// <summary>
/// Executes the INCR command to increment the integer value of a key by one.
/// </summary>
/// <param name="opts">The IncrOptions containing instance and key.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunIncr(IncrOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var val = db.StringIncrement(opts.Key);
Console.WriteLine(Output.Green(val.ToString()));
return 0;
}
/// <summary>
/// Executes the DECR command to decrement the integer value of a key by one.
/// </summary>
/// <param name="opts">The DecrOptions containing instance and key.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunDecr(DecrOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var val = db.StringDecrement(opts.Key);
Console.WriteLine(Output.Green(val.ToString()));
return 0;
}
/// <summary>
/// Executes the INCRBY command to increment the integer value of a key by a given amount.
/// </summary>
/// <param name="opts">The IncrByOptions containing instance, key, and amount.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunIncrBy(IncrByOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var val = db.StringIncrement(opts.Key, opts.Amount);
Console.WriteLine(Output.Green(val.ToString()));
return 0;
}
/// <summary>
/// Executes the DECRBY command to decrement the integer value of a key by a given amount.
/// </summary>
/// <param name="opts">The DecrByOptions containing instance, key, and amount.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunDecrBy(DecrByOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var val = db.StringDecrement(opts.Key, opts.Amount);
Console.WriteLine(Output.Green(val.ToString()));
return 0;
}
/// <summary>
/// Executes the INCRBYFLOAT command to increment the float value of a key by a given amount.
/// </summary>
/// <param name="opts">The IncrByFloatOptions containing instance, key, and amount.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunIncrByFloat(IncrByFloatOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var val = db.StringIncrement(opts.Key, opts.Amount);
Console.WriteLine(Output.Green(val.ToString()));
return 0;
}
/// <summary>
/// Executes the GETRANGE command to get a substring of the string stored at a key.
/// </summary>
/// <param name="opts">The GetRangeOptions containing instance, key, start, and end.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunGetRange(GetRangeOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var val = db.StringGetRange(opts.Key, opts.Start, opts.End);
Console.WriteLine(Output.Green(val.ToString()));
return 0;
}
/// <summary>
/// Executes the SETRANGE command to overwrite part of a string at key starting at the specified offset.
/// </summary>
/// <param name="opts">The SetRangeOptions containing instance, key, offset, and value.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSetRange(SetRangeOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var len = db.StringSetRange(opts.Key, opts.Offset, opts.Value);
Console.WriteLine(Output.Green(len.ToString()));
return 0;
}
/// <summary>
/// Executes the STRLEN command to get the length of the value stored in a key.
/// </summary>
/// <param name="opts">The StrLenOptions containing instance and key.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunStrLen(StrLenOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var len = db.StringLength(opts.Key);
Console.WriteLine(Output.Green(len.ToString()));
return 0;
}
/// <summary>
/// Executes the MGET command to get the values of all the given keys.
/// </summary>
/// <param name="opts">The MGetOptions containing instance and keys.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunMGet(MGetOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
var vals = db.StringGet(keys);
for (int i = 0; i < keys.Length; i++)
{
Console.WriteLine(Output.Green($"{keys[i]}: {vals[i]}"));
}
return 0;
}
/// <summary>
/// Executes the MSET command to set multiple keys to multiple values.
/// </summary>
/// <param name="opts">The MSetOptions containing instance and pairs.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunMSet(MSetOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var pairs = opts.Pairs.Split(',').Select(p => p.Split('=', 2)).Where(p => p.Length == 2).Select(p => new KeyValuePair<RedisKey, RedisValue>(p[0].Trim(), p[1].Trim())).ToArray();
db.StringSet(pairs);
Console.WriteLine(Output.Green("OK"));
return 0;
}
}
}

207
Commands/BitCommands.cs Normal file
View File

@ -0,0 +1,207 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
using RedisManager.Commands;
namespace RedisManager.Commands
{
[Verb("bitcount", HelpText = "Count set bits in a string.")]
public class BitCountOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
public string Key { get; set; }
[Option("start", Default = 0L, HelpText = "Start byte.")]
public long Start { get; set; }
[Option("end", Default = -1L, HelpText = "End byte.")]
public long End { get; set; }
}
[Verb("bitfield", HelpText = "Perform arbitrary bitfield integer operations on strings.")]
public class BitFieldOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
public string Key { get; set; }
[Value(1, MetaName = "operations", Min = 1, Required = true, HelpText = "Bitfield operations.")]
public IEnumerable<string> Operations { get; set; }
}
[Verb("bitop", HelpText = "Perform bitwise operations between strings.")]
public class BitOpOptions : InstanceOptions
{
[Value(0, MetaName = "operation", Required = true, HelpText = "Operation (AND, OR, XOR, NOT).")]
public string Operation { get; set; }
[Value(1, MetaName = "dest-key", Required = true, HelpText = "Destination key.")]
public string DestKey { get; set; }
[Value(2, MetaName = "source-keys", Min = 1, Required = true, HelpText = "Source keys.")]
public IEnumerable<string> SourceKeys { get; set; }
}
[Verb("bitpos", HelpText = "Find first bit set or clear in a string.")]
public class BitPosOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
public string Key { get; set; }
[Value(1, MetaName = "bit", Required = true, HelpText = "Bit value (0 or 1).")]
public int Bit { get; set; }
[Option("start", Default = 0L, HelpText = "Start byte.")]
public long Start { get; set; }
[Option("end", Default = -1L, HelpText = "End byte.")]
public long End { get; set; }
}
[Verb("getbit", HelpText = "Returns the bit value at offset in the string value stored at key.")]
public class GetBitOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
public string Key { get; set; }
[Value(1, MetaName = "offset", Required = true, HelpText = "Bit offset.")]
public long Offset { get; set; }
}
[Verb("setbit", HelpText = "Sets or clears the bit at offset in the string value stored at key.")]
public class SetBitOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
public string Key { get; set; }
[Value(1, MetaName = "offset", Required = true, HelpText = "Bit offset.")]
public long Offset { get; set; }
[Value(2, MetaName = "bit", Required = true, HelpText = "Bit value (0 or 1).")]
public int Bit { get; set; }
}
public static class BitCommands
{
/// <summary>
/// Executes the BITCOUNT command to count set bits in a string.
/// </summary>
/// <param name="opts">The BitCountOptions containing instance, key, start, and end.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunBitCount(BitCountOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var args = new List<object> { opts.Key };
if (opts.Start != 0 || opts.End != -1) { args.Add(opts.Start); args.Add(opts.End); }
var result = db.Execute("BITCOUNT", args.ToArray());
Console.WriteLine(Output.Green($"Bit count: {result}"));
return 0;
}
/// <summary>
/// Executes the BITFIELD command to perform arbitrary bitfield integer operations on strings.
/// </summary>
/// <param name="opts">The BitFieldOptions containing instance, key, and operations.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunBitField(BitFieldOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var args = new List<object> { opts.Key };
args.AddRange(opts.Operations);
var result = db.Execute("BITFIELD", args.ToArray());
RedisResult[] results;
if (result is Array)
results = (RedisResult[])result;
else
results = new RedisResult[] { result };
Console.WriteLine(Output.Green("Bitfield results:"));
var operationsList = opts.Operations.ToList();
for (int i = 0; results != null && i < results.Length; i++)
{
var res = results[i];
if (res != null)
{
Console.WriteLine(Output.Cyan($"{operationsList[i]}: {res}"));
}
else
{
Console.WriteLine(Output.Red($"{operationsList[i]}: null"));
}
}
return 0;
}
/// <summary>
/// Executes the BITOP command to perform bitwise operations between strings.
/// </summary>
/// <param name="opts">The BitOpOptions containing instance, operation, destination key, and source keys.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunBitOp(BitOpOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var args = new List<object> { opts.Operation, opts.DestKey };
args.AddRange(opts.SourceKeys);
var result = db.Execute("BITOP", args.ToArray());
Console.WriteLine(Output.Green($"BITOP result: {result}"));
return 0;
}
/// <summary>
/// Executes the BITPOS command to find the first bit set or clear in a string.
/// </summary>
/// <param name="opts">The BitPosOptions containing instance, key, bit, start, and end.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunBitPos(BitPosOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var args = new List<object> { opts.Key, opts.Bit };
if (opts.Start != 0 || opts.End != -1) { args.Add(opts.Start); args.Add(opts.End); }
var result = db.Execute("BITPOS", args.ToArray());
Console.WriteLine(Output.Green($"First {opts.Bit} bit at position: {result}"));
return 0;
}
/// <summary>
/// Executes the GETBIT command to return the bit value at offset in the string value stored at key.
/// </summary>
/// <param name="opts">The GetBitOptions containing instance, key, and offset.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunGetBit(GetBitOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var result = db.Execute("GETBIT", new object[] { opts.Key, opts.Offset });
Console.WriteLine(Output.Green($"Bit at offset {opts.Offset}: {result}"));
return 0;
}
/// <summary>
/// Executes the SETBIT command to set or clear the bit at offset in the string value stored at key.
/// </summary>
/// <param name="opts">The SetBitOptions containing instance, key, offset, and bit value.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSetBit(SetBitOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var result = db.Execute("SETBIT", new object[] { opts.Key, opts.Offset, opts.Bit });
Console.WriteLine(Output.Green($"Set bit at offset {opts.Offset} to {opts.Bit}: {result}"));
return 0;
}
}
}

View File

@ -0,0 +1,160 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
namespace RedisManager.Commands
{
/// <summary>
/// Contains command line options and implementations for Redis connection management operations.
/// Provides functionality for AUTH, QUIT, CLIENT LIST, CLIENT KILL, CLIENT GETNAME, and CLIENT SETNAME commands.
/// </summary>
[Verb("auth", HelpText = "Authenticate to the server.")]
public class AuthOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "password", Required = true, HelpText = "Password.")]
public string Password { get; set; }
}
[Verb("quit", HelpText = "Close the connection.")]
public class QuitOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
}
[Verb("clientlist", HelpText = "Get the list of client connections.")]
public class ClientListOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
}
[Verb("clientkill", HelpText = "Kill a client connection.")]
public class ClientKillOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "client_id", Required = true, HelpText = "Client ID.")]
public string ClientId { get; set; }
}
[Verb("clientgetname", HelpText = "Get the current connection name.")]
public class ClientGetNameOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
}
[Verb("clientsetname", HelpText = "Set the current connection name.")]
public class ClientSetNameOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "name", Required = true, HelpText = "Connection name.")]
public string Name { get; set; }
}
public static class ConnectionCommands
{
/// <summary>
/// Executes the AUTH command to authenticate to the server.
/// </summary>
/// <param name="opts">The AuthOptions containing instance and password.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunAuth(AuthOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.Execute("AUTH", opts.Password);
Console.WriteLine(Output.Green("OK"));
return 0;
}
/// <summary>
/// Executes the QUIT command to close the connection.
/// </summary>
/// <param name="opts">The QuitOptions containing instance.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunQuit(QuitOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.Execute("QUIT");
Console.WriteLine(Output.Green("OK"));
return 0;
}
/// <summary>
/// Executes the CLIENT LIST command to get the list of client connections.
/// </summary>
/// <param name="opts">The ClientListOptions containing instance.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunClientList(ClientListOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var result = db.Execute("CLIENT", "LIST");
Console.WriteLine(Output.Green(result.ToString()));
return 0;
}
/// <summary>
/// Executes the CLIENT KILL command to kill a client connection.
/// </summary>
/// <param name="opts">The ClientKillOptions containing instance and client ID.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunClientKill(ClientKillOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.Execute("CLIENT", "KILL", opts.ClientId);
Console.WriteLine(Output.Green("OK"));
return 0;
}
/// <summary>
/// Executes the CLIENT GETNAME command to get the current connection name.
/// </summary>
/// <param name="opts">The ClientGetNameOptions containing instance.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunClientGetName(ClientGetNameOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var result = db.Execute("CLIENT", "GETNAME");
Console.WriteLine(Output.Green(result.ToString()));
return 0;
}
/// <summary>
/// Executes the CLIENT SETNAME command to set the current connection name.
/// </summary>
/// <param name="opts">The ClientSetNameOptions containing instance and name.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunClientSetName(ClientSetNameOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.Execute("CLIENT", "SETNAME", opts.Name);
Console.WriteLine(Output.Green("OK"));
return 0;
}
}
}

View File

@ -0,0 +1,121 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
using RedisManager.Commands;
namespace RedisManager.Commands
{
[Verb("select", HelpText = "Change the selected database.")]
public class SelectOptions : InstanceOptions
{
[Value(0, MetaName = "database", Required = true, HelpText = "Database number.")]
public int Database { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("flushdb", HelpText = "Remove all keys from the current database.")]
public class FlushDbOptions : InstanceOptions
{
[Option("yes", Required = false, HelpText = "Skip confirmation prompt.")]
public bool Yes { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("flushall", HelpText = "Remove all keys from all databases.")]
public class FlushAllOptions : InstanceOptions
{
[Option("yes", Required = false, HelpText = "Skip confirmation prompt.")]
public bool Yes { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("dbsize", HelpText = "Return the number of keys in the current database.")]
public class DbSizeOptions : InstanceOptions
{
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
public static class DatabaseCommands
{
public static int RunSelect(SelectOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase(opts.Database);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Database", "Status" }, new List<string[]> { new[] { opts.Database.ToString(), "Selected" } });
else
Console.WriteLine(Output.Green("OK"));
return 0;
}
public static int RunFlushDb(FlushDbOptions opts, Config config)
{
if (!opts.Yes)
{
Console.WriteLine(Output.Yellow("Are you sure you want to flush the current database? (y/N)"));
var response = Console.ReadLine()?.ToLower();
if (response != "y" && response != "yes")
{
Console.WriteLine(Output.Red("Operation cancelled."));
return 1;
}
}
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.Execute("FLUSHDB");
if (opts.Table)
RedisUtils.PrintTable(new[] { "Operation", "Status" }, new List<string[]> { new[] { "FLUSHDB", "OK" } });
else
Console.WriteLine(Output.Green("OK"));
return 0;
}
public static int RunFlushAll(FlushAllOptions opts, Config config)
{
if (!opts.Yes)
{
Console.WriteLine(Output.Yellow("Are you sure you want to flush ALL databases? (y/N)"));
var response = Console.ReadLine()?.ToLower();
if (response != "y" && response != "yes")
{
Console.WriteLine(Output.Red("Operation cancelled."));
return 1;
}
}
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.Execute("FLUSHALL");
if (opts.Table)
RedisUtils.PrintTable(new[] { "Operation", "Status" }, new List<string[]> { new[] { "FLUSHALL", "OK" } });
else
Console.WriteLine(Output.Green("OK"));
return 0;
}
public static int RunDbSize(DbSizeOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var size = db.Execute("DBSIZE");
if (opts.Table)
RedisUtils.PrintTable(new[] { "Database", "Size" }, new List<string[]> { new[] { "Current", size.ToString() } });
else
Console.WriteLine(Output.Green(size.ToString()));
return 0;
}
}
}

242
Commands/GeoCommands.cs Normal file
View File

@ -0,0 +1,242 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
namespace RedisManager.Commands
{
/// <summary>
/// Contains command line options and implementations for Redis geospatial operations.
/// Provides functionality for GEOADD, GEORADIUS, GEODIST, GEOHASH, and GEOPOS commands.
/// </summary>
[Verb("geoadd", HelpText = "Add one or more geospatial items to a key.")]
public class GeoAddOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
public string Key { get; set; }
[Value(1, MetaName = "members", Min = 3, Required = true, HelpText = "Members with longitude,latitude,name format.")]
public IEnumerable<string> Members { get; set; }
}
[Verb("georadius", HelpText = "Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point.")]
public class GeoRadiusOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
public string Key { get; set; }
[Value(1, MetaName = "longitude", Required = true, HelpText = "Longitude.")]
public double Longitude { get; set; }
[Value(2, MetaName = "latitude", Required = true, HelpText = "Latitude.")]
public double Latitude { get; set; }
[Value(3, MetaName = "radius", Required = true, HelpText = "Radius.")]
public double Radius { get; set; }
[Option("unit", Default = "m", HelpText = "Unit (m, km, mi, ft).")]
public string Unit { get; set; }
[Option("withcoord", HelpText = "Include coordinates in output.")]
public bool WithCoord { get; set; }
[Option("withdist", HelpText = "Include distance in output.")]
public bool WithDist { get; set; }
[Option("withhash", HelpText = "Include hash in output.")]
public bool WithHash { get; set; }
[Option("count", HelpText = "Limit results count.")]
public int Count { get; set; }
[Option("asc", HelpText = "Sort ascending by distance.")]
public bool Asc { get; set; }
[Option("desc", HelpText = "Sort descending by distance.")]
public bool Desc { get; set; }
}
[Verb("geodist", HelpText = "Return the distance between two members of a geospatial index.")]
public class GeoDistOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
public string Key { get; set; }
[Value(1, MetaName = "member1", Required = true, HelpText = "First member.")]
public string Member1 { get; set; }
[Value(2, MetaName = "member2", Required = true, HelpText = "Second member.")]
public string Member2 { get; set; }
[Option("unit", Default = "m", HelpText = "Unit (m, km, mi, ft).")]
public string Unit { get; set; }
}
[Verb("geohash", HelpText = "Returns members of a geospatial index as standard geohash strings.")]
public class GeoHashOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
public string Key { get; set; }
[Value(1, MetaName = "members", Min = 1, Required = true, HelpText = "Members.")]
public IEnumerable<string> Members { get; set; }
}
[Verb("geopos", HelpText = "Returns longitude and latitude of members of a geospatial index.")]
public class GeoPosOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
public string Key { get; set; }
[Value(1, MetaName = "members", Min = 1, Required = true, HelpText = "Members.")]
public IEnumerable<string> Members { get; set; }
}
public static class GeoCommands
{
/// <summary>
/// Executes the GEOADD command to add one or more geospatial items to a key.
/// </summary>
/// <param name="opts">The GeoAddOptions containing instance, key, and members.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunGeoAdd(GeoAddOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var args = new List<object> { opts.Key };
var membersList = opts.Members.ToList();
for (int i = 0; i + 2 < membersList.Count; i += 3)
{
args.Add(membersList[i]); // longitude
args.Add(membersList[i + 1]); // latitude
args.Add(membersList[i + 2]); // name
}
var result = db.Execute("GEOADD", args.ToArray());
Console.WriteLine(Output.Green($"Added: {result}"));
return 0;
}
/// <summary>
/// Executes the GEORADIUS command to query a geospatial index for members within a radius.
/// </summary>
/// <param name="opts">The GeoRadiusOptions containing instance, key, longitude, latitude, radius, and options.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunGeoRadius(GeoRadiusOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var args = new List<object> { opts.Key, opts.Longitude, opts.Latitude, opts.Radius, opts.Unit };
if (opts.WithCoord) args.Add("WITHCOORD");
if (opts.WithDist) args.Add("WITHDIST");
if (opts.WithHash) args.Add("WITHHASH");
if (opts.Count > 0) { args.Add("COUNT"); args.Add(opts.Count); }
if (opts.Asc) args.Add("ASC");
if (opts.Desc) args.Add("DESC");
var result = db.Execute("GEORADIUS", args.ToArray());
Console.WriteLine(Output.Green($"Raw result: {result}"));
return 0;
}
/// <summary>
/// Executes the GEODIST command to return the distance between two members of a geospatial index.
/// </summary>
/// <param name="opts">The GeoDistOptions containing instance, key, member1, member2, and unit.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunGeoDist(GeoDistOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var result = db.Execute("GEODIST", new object[] { opts.Key, opts.Member1, opts.Member2, opts.Unit });
Console.WriteLine(Output.Green($"Distance: {result} {opts.Unit}"));
return 0;
}
/// <summary>
/// Executes the GEOHASH command to return geohash strings for members of a geospatial index.
/// </summary>
/// <param name="opts">The GeoHashOptions containing instance, key, and members.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunGeoHash(GeoHashOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var args = new List<object> { opts.Key };
args.AddRange(opts.Members);
var result = db.Execute("GEOHASH", args.ToArray());
RedisResult[] hashes;
if (result is Array)
hashes = (RedisResult[])result;
else
hashes = new RedisResult[] { result };
var membersList = opts.Members.ToList();
for (int i = 0; i < membersList.Count; i++)
{
var hash = hashes != null && i < hashes.Length ? hashes[i]?.ToString() : null;
if (!string.IsNullOrEmpty(hash))
{
Console.WriteLine(Output.Green($"{membersList[i]}: {hash}"));
}
else
{
Console.WriteLine(Output.Red($"{membersList[i]}: not found"));
}
}
return 0;
}
/// <summary>
/// Executes the GEOPOS command to return longitude and latitude of members of a geospatial index.
/// </summary>
/// <param name="opts">The GeoPosOptions containing instance, key, and members.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunGeoPos(GeoPosOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var args = new List<object> { opts.Key };
args.AddRange(opts.Members);
var result = db.Execute("GEOPOS", args.ToArray());
RedisResult[] positions;
if (result is Array)
positions = (RedisResult[])result;
else
positions = new RedisResult[] { result };
var membersList = opts.Members.ToList();
for (int i = 0; i < membersList.Count; i++)
{
var pos = positions != null && i < positions.Length ? positions[i]?.ToString() : null;
if (!string.IsNullOrEmpty(pos))
{
Console.WriteLine(Output.Green($"{membersList[i]}: {pos}"));
}
else
{
Console.WriteLine(Output.Red($"{membersList[i]}: not found"));
}
}
return 0;
}
private static GeoUnit GetGeoUnit(string unit)
{
return unit.ToLower() switch
{
"km" => GeoUnit.Kilometers,
"mi" => GeoUnit.Miles,
"ft" => GeoUnit.Feet,
_ => GeoUnit.Meters
};
}
}
}

583
Commands/HashCommands.cs Normal file
View File

@ -0,0 +1,583 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
namespace RedisManager.Commands
{
/// <summary>
/// Contains command line options and implementations for Redis hash operations.
/// Provides functionality for HGET, HSET, HDEL, HGETALL, HKEYS, HVALS, HLEN, HEXISTS, HINCRBY, HINCRBYFLOAT, HMSET, HMGET, HSETNX, HSTRLEN, and HSCAN commands.
/// </summary>
[Verb("hget", HelpText = "Get value of a field in a hash.")]
public class HGetOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
public string Field { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hset", HelpText = "Set value of a field in a hash.")]
public class HSetOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
public string Field { get; set; }
[Value(2, MetaName = "value", Required = true, HelpText = "Value.")]
public string Value { get; set; }
[Option("ttl", Required = false, HelpText = "Time to live in seconds.")]
public int? TTL { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hdel", HelpText = "Delete a field in a hash.")]
public class HDelOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
public string Field { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hgetall", HelpText = "Get all fields and values in a hash.")]
public class HGetAllOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hkeys", HelpText = "List all fields in a hash.")]
public class HKeysOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hvals", HelpText = "List all values in a hash.")]
public class HValsOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hlen", HelpText = "Get the number of fields in a hash.")]
public class HLenOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hexists", HelpText = "Check if a field exists in a hash.")]
public class HExistsOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
public string Field { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hincrby", HelpText = "Increment field value in hash by integer.")]
public class HIncrByOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
public string Field { get; set; }
[Value(2, MetaName = "increment", Required = true, HelpText = "Increment value.")]
public long Increment { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hincrbyfloat", HelpText = "Increment field value in hash by float.")]
public class HIncrByFloatOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
public string Field { get; set; }
[Value(2, MetaName = "increment", Required = true, HelpText = "Increment value.")]
public double Increment { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hmset", HelpText = "Set multiple fields in a hash.")]
public class HMSetOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Value(1, MetaName = "fields", Min = 1, Required = true, HelpText = "Field-value pairs (field1 value1 field2 value2...).")]
public IEnumerable<string> Fields { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hmget", HelpText = "Get multiple field values from a hash.")]
public class HMGetOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Value(1, MetaName = "fields", Min = 1, Required = true, HelpText = "Field names to get.")]
public IEnumerable<string> Fields { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hsetnx", HelpText = "Set field value in hash only if field does not exist.")]
public class HSetNxOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
public string Field { get; set; }
[Value(2, MetaName = "value", Required = true, HelpText = "Value.")]
public string Value { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hstrlen", HelpText = "Get the string length of a hash field value.")]
public class HStrLenOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
public string Field { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("hscan", HelpText = "Incrementally iterate hash fields.")]
public class HScanOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
public string Hash { get; set; }
[Value(1, MetaName = "cursor", Required = true, HelpText = "Cursor position.")]
public long Cursor { get; set; }
[Option("match", Required = false, HelpText = "Pattern to match.")]
public string Match { get; set; }
[Option("count", Required = false, HelpText = "Count hint.")]
public long? Count { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
public static class HashCommands
{
/// <summary>
/// Executes the HGET command to get the value of a field in a hash.
/// </summary>
/// <param name="opts">The HGetOptions containing instance, hash, and field.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHGet(HGetOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var value = db.HashGet(opts.Hash, opts.Field);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Hash", "Field", "Value" }, new List<string[]> { new[] { opts.Hash, opts.Field, value.IsNull ? "[null]" : value.ToString() } });
else if (value.IsNull)
Console.WriteLine(Output.Yellow($"[null]"));
else
Console.WriteLine(Output.Green(value.ToString()));
return 0;
}
/// <summary>
/// Executes the HSET command to set the value of a field in a hash.
/// </summary>
/// <param name="opts">The HSetOptions containing instance, hash, field, and value.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHSet(HSetOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var ok = db.HashSet(opts.Hash, opts.Field, opts.Value);
if (opts.TTL.HasValue)
db.KeyExpire(opts.Hash, TimeSpan.FromSeconds(opts.TTL.Value));
if (opts.Table)
RedisUtils.PrintTable(new[] { "Hash", "Field", "Value", "Result" }, new List<string[]> { new[] { opts.Hash, opts.Field, opts.Value, ok ? "OK" : "Failed" } });
else if (ok)
Console.WriteLine(Output.Green("OK"));
else
Console.WriteLine(Output.Red("Failed to set field."));
return 0;
}
/// <summary>
/// Executes the HDEL command to delete a field from a hash.
/// </summary>
/// <param name="opts">The HDelOptions containing instance, hash, and field.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHDel(HDelOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var ok = db.HashDelete(opts.Hash, opts.Field);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Hash", "Field", "Result" }, new List<string[]> { new[] { opts.Hash, opts.Field, ok ? "Deleted" : "Not found" } });
else if (ok)
Console.WriteLine(Output.Green("OK"));
else
Console.WriteLine(Output.Yellow("Field not found."));
return 0;
}
/// <summary>
/// Executes the HGETALL command to get all fields and values in a hash.
/// </summary>
/// <param name="opts">The HGetAllOptions containing instance and hash.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHGetAll(HGetAllOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var entries = db.HashGetAll(opts.Hash);
if (opts.Table)
{
var rows = entries.Select(e => new[] { e.Name.ToString(), e.Value.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Field", "Value" }, rows);
}
else
foreach (var entry in entries)
Console.WriteLine(Output.Green($"{entry.Name}: {entry.Value}"));
return 0;
}
/// <summary>
/// Executes the HKEYS command to list all fields in a hash.
/// </summary>
/// <param name="opts">The HKeysOptions containing instance and hash.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHKeys(HKeysOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = db.HashKeys(opts.Hash);
if (opts.Table)
{
var rows = keys.Select(k => new[] { k.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Field" }, rows);
}
else
foreach (var key in keys)
Console.WriteLine(Output.Green(key.ToString()));
return 0;
}
/// <summary>
/// Executes the HVALS command to list all values in a hash.
/// </summary>
/// <param name="opts">The HValsOptions containing instance and hash.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHVals(HValsOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var values = db.HashValues(opts.Hash);
if (opts.Table)
{
var rows = values.Select(v => new[] { v.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Value" }, rows);
}
else
foreach (var value in values)
Console.WriteLine(Output.Green(value.ToString()));
return 0;
}
/// <summary>
/// Executes the HLEN command to get the number of fields in a hash.
/// </summary>
/// <param name="opts">The HLenOptions containing instance and hash.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHLen(HLenOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var len = db.HashLength(opts.Hash);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Hash", "Length" }, new List<string[]> { new[] { opts.Hash, len.ToString() } });
else
Console.WriteLine(Output.Green(len.ToString()));
return 0;
}
/// <summary>
/// Executes the HEXISTS command to check if a field exists in a hash.
/// </summary>
/// <param name="opts">The HExistsOptions containing instance, hash, and field.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHExists(HExistsOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var exists = db.HashExists(opts.Hash, opts.Field);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Hash", "Field", "Exists" }, new List<string[]> { new[] { opts.Hash, opts.Field, exists ? "Yes" : "No" } });
else
Console.WriteLine(Output.Green(exists ? "1" : "0"));
return 0;
}
/// <summary>
/// Executes the HINCRBY command to increment a field value in a hash by an integer.
/// </summary>
/// <param name="opts">The HIncrByOptions containing instance, hash, field, and increment.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHIncrBy(HIncrByOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var result = db.HashIncrement(opts.Hash, opts.Field, opts.Increment);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Hash", "Field", "Increment", "Result" }, new List<string[]> { new[] { opts.Hash, opts.Field, opts.Increment.ToString(), result.ToString() } });
else
Console.WriteLine(Output.Green(result.ToString()));
return 0;
}
/// <summary>
/// Executes the HINCRBYFLOAT command to increment a field value in a hash by a float.
/// </summary>
/// <param name="opts">The HIncrByFloatOptions containing instance, hash, field, and increment.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHIncrByFloat(HIncrByFloatOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var result = db.HashIncrement(opts.Hash, opts.Field, opts.Increment);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Hash", "Field", "Increment", "Result" }, new List<string[]> { new[] { opts.Hash, opts.Field, opts.Increment.ToString(), result.ToString() } });
else
Console.WriteLine(Output.Green(result.ToString()));
return 0;
}
/// <summary>
/// Executes the HMSET command to set multiple fields in a hash.
/// </summary>
/// <param name="opts">The HMSetOptions containing instance, hash, and field-value pairs.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHMSet(HMSetOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var fieldsList = opts.Fields.ToList();
if (fieldsList.Count % 2 != 0)
{
Console.WriteLine(Output.Red("Error: Fields must be provided as field-value pairs."));
return 1;
}
var entries = new HashEntry[fieldsList.Count / 2];
for (int i = 0; i < fieldsList.Count; i += 2)
{
entries[i / 2] = new HashEntry(fieldsList[i], fieldsList[i + 1]);
}
db.HashSet(opts.Hash, entries);
if (opts.Table)
{
var rows = new List<string[]>();
for (int i = 0; i < entries.Length; i++)
{
rows.Add(new[] { opts.Hash, entries[i].Name.ToString(), entries[i].Value.ToString() });
}
RedisUtils.PrintTable(new[] { "Hash", "Field", "Value" }, rows);
}
else
Console.WriteLine(Output.Green("OK"));
return 0;
}
/// <summary>
/// Executes the HMGET command to get multiple field values from a hash.
/// </summary>
/// <param name="opts">The HMGetOptions containing instance, hash, and fields.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHMGet(HMGetOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var fields = opts.Fields.Select(f => (RedisValue)f).ToArray();
var values = db.HashGet(opts.Hash, fields);
if (opts.Table)
{
var rows = new List<string[]>();
var fieldsList = opts.Fields.ToList();
for (int i = 0; i < fieldsList.Count; i++)
{
var value = values[i].IsNull ? "[null]" : values[i].ToString();
rows.Add(new[] { opts.Hash, fieldsList[i], value });
}
RedisUtils.PrintTable(new[] { "Hash", "Field", "Value" }, rows);
}
else
{
var fieldsList = opts.Fields.ToList();
for (int i = 0; i < fieldsList.Count; i++)
{
var value = values[i].IsNull ? "[null]" : values[i].ToString();
Console.WriteLine(Output.Green($"{fieldsList[i]}: {value}"));
}
}
return 0;
}
/// <summary>
/// Executes the HSETNX command to set a field value in a hash only if the field does not exist.
/// </summary>
/// <param name="opts">The HSetNxOptions containing instance, hash, field, and value.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHSetNx(HSetNxOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var result = db.HashSet(opts.Hash, opts.Field, opts.Value, When.NotExists);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Hash", "Field", "Value", "Result" }, new List<string[]> { new[] { opts.Hash, opts.Field, opts.Value, result ? "Set" : "Not set (field exists)" } });
else
Console.WriteLine(Output.Green(result ? "1" : "0"));
return 0;
}
/// <summary>
/// Executes the HSTRLEN command to get the string length of a hash field value.
/// </summary>
/// <param name="opts">The HStrLenOptions containing instance, hash, and field.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHStrLen(HStrLenOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var length = db.HashStringLength(opts.Hash, opts.Field);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Hash", "Field", "Length" }, new List<string[]> { new[] { opts.Hash, opts.Field, length.ToString() } });
else
Console.WriteLine(Output.Green(length.ToString()));
return 0;
}
/// <summary>
/// Executes the HSCAN command to incrementally iterate hash fields.
/// </summary>
/// <param name="opts">The HScanOptions containing instance, hash, and scan options.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunHScan(HScanOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var pattern = opts.Match;
var pageSize = opts.Count.HasValue ? (int)opts.Count.Value : 10;
var scanResult = db.HashScan(opts.Hash, pattern, pageSize);
if (opts.Table)
{
var rows = scanResult.Select(e => new[] { e.Name.ToString(), e.Value.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Field", "Value" }, rows);
Console.WriteLine(Output.Yellow($"Scanned {rows.Count} entries"));
}
else
{
int count = 0;
foreach (var entry in scanResult)
{
Console.WriteLine(Output.Green($"{entry.Name}: {entry.Value}"));
count++;
}
Console.WriteLine(Output.Yellow($"Scanned {count} entries"));
}
return 0;
}
}
}

View File

@ -0,0 +1,117 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
namespace RedisManager.Commands
{
[Verb("pfadd", HelpText = "Add elements to a HyperLogLog.")]
public class PFAddOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "HyperLogLog key.")]
public string Key { get; set; }
[Value(1, MetaName = "elements", Min = 1, Required = true, HelpText = "Elements to add.")]
public IEnumerable<string> Elements { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("pfcount", HelpText = "Get the approximated cardinality of HyperLogLog(s).")]
public class PFCountOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "HyperLogLog key(s).")]
public IEnumerable<string> Keys { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("pfmerge", HelpText = "Merge multiple HyperLogLogs into a single one.")]
public class PFMergeOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "destkey", Required = true, HelpText = "Destination key.")]
public string DestKey { get; set; }
[Value(1, MetaName = "sourcekeys", Min = 1, Required = true, HelpText = "Source HyperLogLog keys.")]
public IEnumerable<string> SourceKeys { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
public static class HyperLogLogCommands
{
public static int RunPFAdd(PFAddOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var elements = opts.Elements.ToArray();
// Use PFADD command directly
var batch = db.CreateBatch();
var tasks = elements.Select(e => batch.ExecuteAsync("PFADD", opts.Key, e)).ToArray();
batch.Execute();
var count = tasks.Count(t => (bool)t.Result);
if (opts.Table)
{
var rows = elements.Select(e => new[] { e, "Added" }).ToList();
RedisUtils.PrintTable(new[] { "Element", "Result" }, rows);
RedisUtils.PrintTable(new[] { "Key", "Added Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
}
else
Console.WriteLine(Output.Green($"Added {count} new element(s)"));
return 0;
}
public static int RunPFCount(PFCountOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = opts.Keys.ToArray();
// Use PFCOUNT command directly
var count = db.Execute("PFCOUNT", keys);
if (opts.Table)
{
if (keys.Length == 1)
RedisUtils.PrintTable(new[] { "Key", "Cardinality" }, new List<string[]> { new[] { keys[0], count.ToString() } });
else
RedisUtils.PrintTable(new[] { "Keys", "Combined Cardinality" }, new List<string[]> { new[] { string.Join(", ", keys), count.ToString() } });
}
else
Console.WriteLine(Output.Green(count.ToString()));
return 0;
}
public static int RunPFMerge(PFMergeOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var sourceKeys = opts.SourceKeys.ToArray();
// Use PFMERGE command directly
db.Execute("PFMERGE", opts.DestKey, sourceKeys);
if (opts.Table)
{
var rows = sourceKeys.Select(k => new[] { k, "Merged" }).ToList();
RedisUtils.PrintTable(new[] { "Source Key", "Status" }, rows);
RedisUtils.PrintTable(new[] { "Destination Key", "Status" }, new List<string[]> { new[] { opts.DestKey, "OK" } });
}
else
Console.WriteLine(Output.Green("OK"));
return 0;
}
}
}

View File

@ -0,0 +1,126 @@
using CommandLine;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using RedisManager.Utils;
namespace RedisManager.Commands
{
[Verb("list-instances", HelpText = "List all configured Redis instances.")]
public class ListInstancesOptions { }
[Verb("add-instance", HelpText = "Add a new Redis instance.")]
public class AddInstanceOptions
{
[Option('n', "name", Required = true, HelpText = "Instance name.")]
public string Name { get; set; }
[Option('h', "host", Required = true, HelpText = "Redis host.")]
public string Host { get; set; }
[Option('p', "port", Required = false, HelpText = "Redis port.")]
public int Port { get; set; } = 6379;
[Option('w', "password", Required = false, HelpText = "Redis password.")]
public string Password { get; set; }
}
[Verb("update-instance", HelpText = "Update an existing Redis instance.")]
public class UpdateInstanceOptions
{
[Option('n', "name", Required = true, HelpText = "Instance name.")]
public string Name { get; set; }
[Option('h', "host", Required = false, HelpText = "Redis host.")]
public string Host { get; set; }
[Option('p', "port", Required = false, HelpText = "Redis port.")]
public int Port { get; set; } = 6379;
[Option('w', "password", Required = false, HelpText = "Redis password.")]
public string Password { get; set; }
}
[Verb("delete-instance", HelpText = "Delete a Redis instance.")]
public class DeleteInstanceOptions
{
[Option('n', "name", Required = true, HelpText = "Instance name.")]
public string Name { get; set; }
}
public static class InstanceCommands
{
public static int RunListInstances(Config config)
{
if (config.Instances.Count == 0)
{
Console.WriteLine(Output.Yellow("No instances configured."));
return 0;
}
var rows = config.Instances.Select(i => new[] { i.Name, i.Host, i.Port.ToString(), i.Password ?? "" }).ToList();
RedisUtils.PrintTable(new[] { "Name", "Host", "Port", "Password" }, rows);
return 0;
}
public static int RunAddInstance(AddInstanceOptions opts, Config config)
{
if (config.Instances.Any(i => i.Name == opts.Name))
{
Console.WriteLine(Output.Red($"Instance '{opts.Name}' already exists."));
return 1;
}
var instance = new InstanceConfig
{
Name = opts.Name,
Host = opts.Host,
Port = opts.Port,
Password = opts.Password
};
config.Instances.Add(instance);
SaveConfig(config, "redismanager.json");
Console.WriteLine(Output.Green($"Instance '{opts.Name}' added successfully."));
return 0;
}
public static int RunUpdateInstance(UpdateInstanceOptions opts, Config config)
{
var instance = config.Instances.Find(i => i.Name == opts.Name);
if (instance == null)
{
Console.WriteLine(Output.Red($"Instance '{opts.Name}' not found."));
return 1;
}
if (!string.IsNullOrEmpty(opts.Host))
instance.Host = opts.Host;
if (opts.Port != 6379)
instance.Port = opts.Port;
if (!string.IsNullOrEmpty(opts.Password))
instance.Password = opts.Password;
SaveConfig(config, "redismanager.json");
Console.WriteLine(Output.Green($"Instance '{opts.Name}' updated successfully."));
return 0;
}
public static int RunDeleteInstance(DeleteInstanceOptions opts, Config config)
{
var instance = config.Instances.Find(i => i.Name == opts.Name);
if (instance == null)
{
Console.WriteLine(Output.Red($"Instance '{opts.Name}' not found."));
return 1;
}
config.Instances.Remove(instance);
SaveConfig(config, "redismanager.json");
Console.WriteLine(Output.Green($"Instance '{opts.Name}' deleted successfully."));
return 0;
}
private static void SaveConfig(Config config, string path)
{
var json = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(path, json);
}
}
}

View File

@ -0,0 +1,13 @@
using CommandLine;
namespace RedisManager.Commands
{
/// <summary>
/// Base options for commands that require a Redis instance.
/// </summary>
public class InstanceOptions
{
[Option('i', "instance", Required = true, HelpText = "Name of the Redis instance to use.")]
public string Instance { get; set; }
}
}

227
Commands/KeyCommands.cs Normal file
View File

@ -0,0 +1,227 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
namespace RedisManager.Commands
{
[Verb("exists", HelpText = "Check if key(s) exist.")]
public class ExistsOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "Keys to check.")]
public IEnumerable<string> Keys { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("expire", HelpText = "Set a timeout on a key.")]
public class ExpireOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key to expire.")]
public string Key { get; set; }
[Value(1, MetaName = "seconds", Required = true, HelpText = "Expire time in seconds.")]
public int Seconds { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("ttl", HelpText = "Get the time to live for a key.")]
public class TtlOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key to check.")]
public string Key { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("persist", HelpText = "Remove the expiration from a key.")]
public class PersistOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key to persist.")]
public string Key { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("rename", HelpText = "Rename a key.")]
public class RenameOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Current key name.")]
public string Key { get; set; }
[Value(1, MetaName = "newkey", Required = true, HelpText = "New key name.")]
public string NewKey { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("type", HelpText = "Get the type of a key.")]
public class TypeOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Key to check.")]
public string Key { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("keys", HelpText = "Find all keys matching pattern.")]
public class KeysOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "pattern", Required = true, HelpText = "Pattern to match.")]
public string Pattern { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("scan", HelpText = "Incrementally iterate the keyspace.")]
public class ScanOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Option("cursor", Required = false, HelpText = "Cursor to start from.")]
public ulong Cursor { get; set; } = 0;
[Option("pattern", Required = false, HelpText = "Pattern to match.")]
public string Pattern { get; set; }
[Option("count", Required = false, HelpText = "Count of keys to return.")]
public int? Count { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
public static class KeyCommands
{
public static int RunExists(ExistsOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
var count = db.KeyExists(keys);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Keys", "Exist Count" }, new List<string[]> { new[] { string.Join(", ", opts.Keys), count.ToString() } });
else
Console.WriteLine(Output.Green(count.ToString()));
return 0;
}
public static int RunExpire(ExpireOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var ok = db.KeyExpire(opts.Key, TimeSpan.FromSeconds(opts.Seconds));
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Expire Set" }, new List<string[]> { new[] { opts.Key, ok ? "Yes" : "No" } });
else
Console.WriteLine(Output.Green(ok ? "1" : "0"));
return 0;
}
public static int RunTtl(TtlOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var ttl = db.KeyTimeToLive(opts.Key);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "TTL (s)" }, new List<string[]> { new[] { opts.Key, ttl?.TotalSeconds.ToString() ?? "-1" } });
else
Console.WriteLine(Output.Green(ttl?.TotalSeconds.ToString() ?? "-1"));
return 0;
}
public static int RunPersist(PersistOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var ok = db.KeyPersist(opts.Key);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Persisted" }, new List<string[]> { new[] { opts.Key, ok ? "Yes" : "No" } });
else
Console.WriteLine(Output.Green(ok ? "1" : "0"));
return 0;
}
public static int RunRename(RenameOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.KeyRename(opts.Key, opts.NewKey);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Old Key", "New Key", "Status" }, new List<string[]> { new[] { opts.Key, opts.NewKey, "Renamed" } });
else
Console.WriteLine(Output.Green("OK"));
return 0;
}
public static int RunType(TypeOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var type = db.KeyType(opts.Key);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Type" }, new List<string[]> { new[] { opts.Key, type.ToString() } });
else
Console.WriteLine(Output.Green(type.ToString()));
return 0;
}
public static int RunKeys(KeysOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = db.Execute("KEYS", opts.Pattern).ToString().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
if (opts.Table)
{
var rows = keys.Select(k => new[] { k }).ToList();
RedisUtils.PrintTable(new[] { "Key" }, rows);
}
else
{
foreach (var key in keys)
Console.WriteLine(Output.Green(key));
}
return 0;
}
public static int RunScan(ScanOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var result = db.Execute("SCAN", opts.Cursor.ToString(),
string.IsNullOrEmpty(opts.Pattern) ? null : "MATCH",
string.IsNullOrEmpty(opts.Pattern) ? null : opts.Pattern,
opts.Count.HasValue ? "COUNT" : null,
opts.Count.HasValue ? opts.Count.Value.ToString() : null);
var arr = (RedisResult[])result;
var nextCursor = arr[0].ToString();
var keys = ((RedisResult[])arr[1]).Select(x => x.ToString()).ToArray();
if (opts.Table)
{
var rows = keys.Select(k => new[] { k }).ToList();
RedisUtils.PrintTable(new[] { "Key" }, rows);
RedisUtils.PrintTable(new[] { "Next Cursor" }, new List<string[]> { new[] { nextCursor } });
}
else
{
foreach (var key in keys)
Console.WriteLine(Output.Green(key));
Console.WriteLine(Output.Yellow($"Next Cursor: {nextCursor}"));
}
return 0;
}
}
}

528
Commands/ListCommands.cs Normal file
View File

@ -0,0 +1,528 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
using RedisManager.Commands;
namespace RedisManager.Commands
{
[Verb("lpush", HelpText = "Push elements to the left of a list.")]
public class LPushOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
public string Key { get; set; }
[Value(1, MetaName = "values", Min = 1, Required = true, HelpText = "Values to push.")]
public IEnumerable<string> Values { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("rpush", HelpText = "Push elements to the right of a list.")]
public class RPushOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
public string Key { get; set; }
[Value(1, MetaName = "values", Min = 1, Required = true, HelpText = "Values to push.")]
public IEnumerable<string> Values { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("lpop", HelpText = "Pop element from the left of a list.")]
public class LPopOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
public string Key { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("rpop", HelpText = "Pop element from the right of a list.")]
public class RPopOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
public string Key { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("lrange", HelpText = "Get range of elements from a list.")]
public class LRangeOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
public string Key { get; set; }
[Value(1, MetaName = "start", Required = true, HelpText = "Start index.")]
public long Start { get; set; }
[Value(2, MetaName = "stop", Required = true, HelpText = "Stop index.")]
public long Stop { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("llen", HelpText = "Get the length of a list.")]
public class LLenOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
public string Key { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("lindex", HelpText = "Get element at index in a list.")]
public class LIndexOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
public string Key { get; set; }
[Value(1, MetaName = "index", Required = true, HelpText = "Element index.")]
public long Index { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("lset", HelpText = "Set element at index in a list.")]
public class LSetOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
public string Key { get; set; }
[Value(1, MetaName = "index", Required = true, HelpText = "Element index.")]
public long Index { get; set; }
[Value(2, MetaName = "value", Required = true, HelpText = "New value.")]
public string Value { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("lrem", HelpText = "Remove elements from a list.")]
public class LRemOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
public string Key { get; set; }
[Value(1, MetaName = "count", Required = true, HelpText = "Number of elements to remove (0=all, positive=from left, negative=from right).")]
public long Count { get; set; }
[Value(2, MetaName = "value", Required = true, HelpText = "Value to remove.")]
public string Value { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("linsert", HelpText = "Insert element before or after pivot in a list.")]
public class LInsertOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
public string Key { get; set; }
[Value(1, MetaName = "position", Required = true, HelpText = "Position: BEFORE or AFTER.")]
public string Position { get; set; }
[Value(2, MetaName = "pivot", Required = true, HelpText = "Pivot element.")]
public string Pivot { get; set; }
[Value(3, MetaName = "value", Required = true, HelpText = "Value to insert.")]
public string Value { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("ltrim", HelpText = "Trim list to specified range.")]
public class LTrimOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
public string Key { get; set; }
[Value(1, MetaName = "start", Required = true, HelpText = "Start index.")]
public long Start { get; set; }
[Value(2, MetaName = "stop", Required = true, HelpText = "Stop index.")]
public long Stop { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("blpop", HelpText = "Blocking pop from the left of a list.")]
public class BLPopOptions : InstanceOptions
{
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "List keys.")]
public IEnumerable<string> Keys { get; set; }
[Value(1, MetaName = "timeout", Required = true, HelpText = "Timeout in seconds.")]
public int Timeout { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("brpop", HelpText = "Blocking pop from the right of a list.")]
public class BRPopOptions : InstanceOptions
{
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "List keys.")]
public IEnumerable<string> Keys { get; set; }
[Value(1, MetaName = "timeout", Required = true, HelpText = "Timeout in seconds.")]
public int Timeout { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("rpoplpush", HelpText = "Pop from right of source, push to left of destination.")]
public class RPopLPushOptions : InstanceOptions
{
[Value(0, MetaName = "source", Required = true, HelpText = "Source list key.")]
public string Source { get; set; }
[Value(1, MetaName = "destination", Required = true, HelpText = "Destination list key.")]
public string Destination { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
public static class ListCommands
{
/// <summary>
/// Executes the LPUSH command to push elements to the left of a list.
/// </summary>
/// <param name="opts">The LPushOptions containing instance, key, and values.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunLPush(LPushOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var values = opts.Values.ToArray();
var length = db.ListLeftPush(opts.Key, values.Select(v => (RedisValue)v).ToArray());
if (opts.Table)
{
var rows = values.Select(v => new[] { v, "Pushed" }).ToList();
RedisUtils.PrintTable(new[] { "Value", "Status" }, rows);
RedisUtils.PrintTable(new[] { "Key", "New Length" }, new List<string[]> { new[] { opts.Key, length.ToString() } });
}
else
Console.WriteLine(Output.Green($"Pushed {values.Length} element(s), new length: {length}"));
return 0;
}
/// <summary>
/// Executes the RPUSH command to push elements to the right of a list.
/// </summary>
/// <param name="opts">The RPushOptions containing instance, key, and values.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunRPush(RPushOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var values = opts.Values.ToArray();
var length = db.ListRightPush(opts.Key, values.Select(v => (RedisValue)v).ToArray());
if (opts.Table)
{
var rows = values.Select(v => new[] { v, "Pushed" }).ToList();
RedisUtils.PrintTable(new[] { "Value", "Status" }, rows);
RedisUtils.PrintTable(new[] { "Key", "New Length" }, new List<string[]> { new[] { opts.Key, length.ToString() } });
}
else
Console.WriteLine(Output.Green($"Pushed {values.Length} element(s), new length: {length}"));
return 0;
}
/// <summary>
/// Executes the LPOP command to pop an element from the left of a list.
/// </summary>
/// <param name="opts">The LPopOptions containing instance and key.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunLPop(LPopOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var value = db.ListLeftPop(opts.Key);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Popped Value" }, new List<string[]> { new[] { opts.Key, value.ToString() } });
else
Console.WriteLine(Output.Green(value.ToString()));
return 0;
}
/// <summary>
/// Executes the RPOP command to pop an element from the right of a list.
/// </summary>
/// <param name="opts">The RPopOptions containing instance and key.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunRPop(RPopOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var value = db.ListRightPop(opts.Key);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Popped Value" }, new List<string[]> { new[] { opts.Key, value.ToString() } });
else
Console.WriteLine(Output.Green(value.ToString()));
return 0;
}
/// <summary>
/// Executes the LRANGE command to get a range of elements from a list.
/// </summary>
/// <param name="opts">The LRangeOptions containing instance, key, start, and stop.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunLRange(LRangeOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var values = db.ListRange(opts.Key, opts.Start, opts.Stop);
if (opts.Table)
{
var rows = values.Select((v, i) => new[] { (opts.Start + i).ToString(), v.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Index", "Value" }, rows);
}
else
foreach (var value in values)
Console.WriteLine(Output.Green(value.ToString()));
return 0;
}
/// <summary>
/// Executes the LLEN command to get the length of a list.
/// </summary>
/// <param name="opts">The LLenOptions containing instance and key.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunLLen(LLenOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var length = db.ListLength(opts.Key);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Length" }, new List<string[]> { new[] { opts.Key, length.ToString() } });
else
Console.WriteLine(Output.Green(length.ToString()));
return 0;
}
/// <summary>
/// Executes the LINDEX command to get the element at a specific index in a list.
/// </summary>
/// <param name="opts">The LIndexOptions containing instance, key, and index.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunLIndex(LIndexOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var value = db.ListGetByIndex(opts.Key, opts.Index);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Index", "Value" }, new List<string[]> { new[] { opts.Key, opts.Index.ToString(), value.ToString() } });
else
Console.WriteLine(Output.Green(value.ToString()));
return 0;
}
/// <summary>
/// Executes the LSET command to set the element at a specific index in a list.
/// </summary>
/// <param name="opts">The LSetOptions containing instance, key, index, and value.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunLSet(LSetOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.ListSetByIndex(opts.Key, opts.Index, opts.Value);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Index", "New Value", "Status" }, new List<string[]> { new[] { opts.Key, opts.Index.ToString(), opts.Value, "OK" } });
else
Console.WriteLine(Output.Green("OK"));
return 0;
}
/// <summary>
/// Executes the LREM command to remove elements from a list.
/// </summary>
/// <param name="opts">The LRemOptions containing instance, key, count, and value.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunLRem(LRemOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var count = db.ListRemove(opts.Key, opts.Value, opts.Count);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Value", "Removed Count" }, new List<string[]> { new[] { opts.Key, opts.Value, count.ToString() } });
else
Console.WriteLine(Output.Green($"Removed {count} element(s)"));
return 0;
}
/// <summary>
/// Executes the LINSERT command to insert an element before or after a pivot in a list.
/// </summary>
/// <param name="opts">The LInsertOptions containing instance, key, position, pivot, and value.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunLInsert(LInsertOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
long result;
if (opts.Position.ToUpper() == "BEFORE")
result = db.ListInsertBefore(opts.Key, opts.Pivot, opts.Value);
else
result = db.ListInsertAfter(opts.Key, opts.Pivot, opts.Value);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Position", "Pivot", "Value", "New Length" }, new List<string[]> { new[] { opts.Key, opts.Position, opts.Pivot, opts.Value, result.ToString() } });
else
Console.WriteLine(Output.Green($"New length: {result}"));
return 0;
}
/// <summary>
/// Executes the LTRIM command to trim a list to a specified range.
/// </summary>
/// <param name="opts">The LTrimOptions containing instance, key, start, and stop.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunLTrim(LTrimOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.ListTrim(opts.Key, opts.Start, opts.Stop);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Start", "Stop", "Status" }, new List<string[]> { new[] { opts.Key, opts.Start.ToString(), opts.Stop.ToString(), "OK" } });
else
Console.WriteLine(Output.Green("OK"));
return 0;
}
/// <summary>
/// Executes the BLPOP command to perform a blocking pop from the left of a list.
/// </summary>
/// <param name="opts">The BLPopOptions containing instance, keys, and timeout.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunBLPop(BLPopOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var args = opts.Keys.Select(k => (RedisKey)k).Cast<object>().ToList();
args.Add(opts.Timeout);
var result = db.Execute("BLPOP", args.ToArray());
if (result == null)
{
Console.WriteLine(Output.Yellow("Timeout - no elements available to pop."));
return 0;
}
var arr = (RedisResult[])result;
if (arr.Length == 2)
{
var key = arr[0]?.ToString();
var value = arr[1]?.ToString();
if (opts.Table)
{
RedisUtils.PrintTable(new[] { "Key", "Popped Value" }, new List<string[]> { new[] { key ?? "", value ?? "" } });
}
else
{
Console.WriteLine(Output.Green($"{key}: {value}"));
}
}
else
{
Console.WriteLine(Output.Yellow("Unexpected result format."));
}
return 0;
}
/// <summary>
/// Executes the BRPOP command to perform a blocking pop from the right of a list.
/// </summary>
/// <param name="opts">The BRPopOptions containing instance, keys, and timeout.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunBRPop(BRPopOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var args = opts.Keys.Select(k => (RedisKey)k).Cast<object>().ToList();
args.Add(opts.Timeout);
var result = db.Execute("BRPOP", args.ToArray());
if (result == null)
{
Console.WriteLine(Output.Yellow("Timeout - no elements available to pop."));
return 0;
}
var arr = (RedisResult[])result;
if (arr.Length == 2)
{
var key = arr[0]?.ToString();
var value = arr[1]?.ToString();
if (opts.Table)
{
RedisUtils.PrintTable(new[] { "Key", "Popped Value" }, new List<string[]> { new[] { key ?? "", value ?? "" } });
}
else
{
Console.WriteLine(Output.Green($"{key}: {value}"));
}
}
else
{
Console.WriteLine(Output.Yellow("Unexpected result format."));
}
return 0;
}
/// <summary>
/// Executes the RPOPLPUSH command to pop from the right of the source and push to the left of the destination list.
/// </summary>
/// <param name="opts">The RPopLPushOptions containing instance, source, and destination.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunRPopLPush(RPopLPushOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var result = db.ListRightPopLeftPush(opts.Source, opts.Destination);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Source", "Destination", "Popped Value" }, new List<string[]> { new[] { opts.Source, opts.Destination, result.ToString() } });
else
Console.WriteLine(Output.Green(result.ToString()));
return 0;
}
}
}

View File

@ -0,0 +1,99 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
namespace RedisManager.Commands
{
[Verb("module-load", HelpText = "Load a Redis module.")]
public class ModuleLoadOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "path", Required = true, HelpText = "Module path.")]
public string Path { get; set; }
[Value(1, MetaName = "args", HelpText = "Module arguments.")]
public IEnumerable<string> Args { get; set; }
}
[Verb("module-unload", HelpText = "Unload a Redis module.")]
public class ModuleUnloadOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "name", Required = true, HelpText = "Module name.")]
public string Name { get; set; }
}
[Verb("module-list", HelpText = "List loaded Redis modules.")]
public class ModuleListOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
}
public static class ModuleCommands
{
public static int RunModuleLoad(ModuleLoadOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
try
{
var args = new List<object> { "LOAD", opts.Path };
if (opts.Args != null)
args.AddRange(opts.Args);
var result = db.Execute("MODULE", args.ToArray());
Console.WriteLine(Output.Green($"Module loaded: {result}"));
return 0;
}
catch (Exception ex)
{
Console.WriteLine(Output.Red($"Failed to load module: {ex.Message}"));
return 1;
}
}
public static int RunModuleUnload(ModuleUnloadOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
try
{
var result = db.Execute("MODULE", new object[] { "UNLOAD", opts.Name });
Console.WriteLine(Output.Green($"Module unloaded: {result}"));
return 0;
}
catch (Exception ex)
{
Console.WriteLine(Output.Red($"Failed to unload module: {ex.Message}"));
return 1;
}
}
public static int RunModuleList(ModuleListOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
try
{
var result = db.Execute("MODULE", new object[] { "LIST" });
Console.WriteLine(Output.Green($"Module list: {result}"));
return 0;
}
catch (Exception ex)
{
Console.WriteLine(Output.Red($"Failed to list modules: {ex.Message}"));
return 1;
}
}
}
}

119
Commands/PubSubCommands.cs Normal file
View File

@ -0,0 +1,119 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Threading;
using RedisManager.Utils;
namespace RedisManager.Commands
{
/// <summary>
/// Contains command line options and implementations for Redis Pub/Sub operations.
/// Provides functionality for PUBLISH, SUBSCRIBE, and UNSUBSCRIBE commands.
/// </summary>
[Verb("publish", HelpText = "Publish a message to a channel.")]
public class PublishOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "channel", Required = true, HelpText = "Channel name.")]
public string Channel { get; set; }
[Value(1, MetaName = "message", Required = true, HelpText = "Message to publish.")]
public string Message { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("subscribe", HelpText = "Subscribe to one or more channels.")]
public class SubscribeOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "channels", Min = 1, Required = true, HelpText = "Channels to subscribe to.")]
public IEnumerable<string> Channels { get; set; }
}
[Verb("unsubscribe", HelpText = "Unsubscribe from one or more channels.")]
public class UnsubscribeOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "channels", Min = 1, Required = true, HelpText = "Channels to unsubscribe from.")]
public IEnumerable<string> Channels { get; set; }
}
public static class PubSubCommands
{
/// <summary>
/// Executes the PUBLISH command to publish a message to a channel.
/// </summary>
/// <param name="opts">The PublishOptions containing instance, channel, and message.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunPublish(PublishOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var sub = redis.GetSubscriber();
var count = sub.Publish(opts.Channel, opts.Message);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Channel", "Receivers" }, new List<string[]> { new[] { opts.Channel, count.ToString() } });
else
Console.WriteLine(Output.Green($"Published to {opts.Channel}, receivers: {count}"));
return 0;
}
/// <summary>
/// Executes the SUBSCRIBE command to subscribe to one or more channels.
/// </summary>
/// <param name="opts">The SubscribeOptions containing instance and channels.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSubscribe(SubscribeOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var sub = redis.GetSubscriber();
var channels = opts.Channels;
Console.WriteLine(Output.Yellow($"Subscribed to: {string.Join(", ", channels)}. Press Ctrl+C to exit."));
using var cts = new CancellationTokenSource();
Console.CancelKeyPress += (s, e) => { e.Cancel = true; cts.Cancel(); };
foreach (var channel in channels)
{
sub.Subscribe(channel, (ch, msg) =>
{
Console.WriteLine(Output.Cyan($"[{ch}] {msg}"));
});
}
try
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(100);
}
}
catch (OperationCanceledException) { }
Console.WriteLine(Output.Yellow("Unsubscribed and exiting."));
return 0;
}
/// <summary>
/// Executes the UNSUBSCRIBE command to unsubscribe from one or more channels.
/// </summary>
/// <param name="opts">The UnsubscribeOptions containing instance and channels.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunUnsubscribe(UnsubscribeOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var sub = redis.GetSubscriber();
foreach (var channel in opts.Channels)
{
sub.Unsubscribe(channel);
Console.WriteLine(Output.Yellow($"Unsubscribed from {channel}"));
}
return 0;
}
}
}

View File

@ -0,0 +1,156 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
namespace RedisManager.Commands
{
/// <summary>
/// Contains command line options and implementations for Redis scripting operations.
/// Provides functionality for EVAL, EVALSHA, SCRIPT LOAD, SCRIPT EXISTS, and SCRIPT FLUSH commands.
/// </summary>
[Verb("eval", HelpText = "Evaluate a Lua script.")]
public class EvalOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "script", Required = true, HelpText = "Lua script.")]
public string Script { get; set; }
[Option("keys", Required = false, HelpText = "Comma-separated keys.")]
public string Keys { get; set; }
[Option("args", Required = false, HelpText = "Comma-separated arguments.")]
public string Args { get; set; }
}
[Verb("evalsha", HelpText = "Evaluate a Lua script by its SHA1 hash.")]
public class EvalShaOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "sha1", Required = true, HelpText = "SHA1 hash.")]
public string Sha1 { get; set; }
[Option("keys", Required = false, HelpText = "Comma-separated keys.")]
public string Keys { get; set; }
[Option("args", Required = false, HelpText = "Comma-separated arguments.")]
public string Args { get; set; }
}
[Verb("scriptload", HelpText = "Load a Lua script into the script cache.")]
public class ScriptLoadOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "script", Required = true, HelpText = "Lua script.")]
public string Script { get; set; }
}
[Verb("scriptexists", HelpText = "Check if scripts exist in the script cache.")]
public class ScriptExistsOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "sha1s", Min = 1, Required = true, HelpText = "SHA1 hashes.")]
public IEnumerable<string> Sha1s { get; set; }
}
[Verb("scriptflush", HelpText = "Remove all scripts from the script cache.")]
public class ScriptFlushOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
}
public static class ScriptingCommands
{
/// <summary>
/// Executes the EVAL command to evaluate a Lua script.
/// </summary>
/// <param name="opts">The EvalOptions containing instance, script, keys, and arguments.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunEval(EvalOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = string.IsNullOrEmpty(opts.Keys) ? Array.Empty<RedisKey>() : opts.Keys.Split(',').Select(k => (RedisKey)k.Trim()).ToArray();
var args = string.IsNullOrEmpty(opts.Args) ? Array.Empty<RedisValue>() : opts.Args.Split(',').Select(a => (RedisValue)a.Trim()).ToArray();
var result = db.ScriptEvaluate(opts.Script, keys, args);
Console.WriteLine(Output.Green(result.ToString()));
return 0;
}
/// <summary>
/// Executes the EVALSHA command to evaluate a Lua script by its SHA1 hash.
/// </summary>
/// <param name="opts">The EvalShaOptions containing instance, sha1, keys, and arguments.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunEvalSha(EvalShaOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = string.IsNullOrEmpty(opts.Keys) ? Array.Empty<RedisKey>() : opts.Keys.Split(',').Select(k => (RedisKey)k.Trim()).ToArray();
var args = string.IsNullOrEmpty(opts.Args) ? Array.Empty<RedisValue>() : opts.Args.Split(',').Select(a => (RedisValue)a.Trim()).ToArray();
var result = db.ScriptEvaluate(opts.Sha1, keys, args);
Console.WriteLine(Output.Green(result.ToString()));
return 0;
}
/// <summary>
/// Executes the SCRIPT LOAD command to load a Lua script into the script cache.
/// </summary>
/// <param name="opts">The ScriptLoadOptions containing instance and script.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunScriptLoad(ScriptLoadOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var server = redis.GetServer(redis.GetEndPoints().First());
var sha1Bytes = server.ScriptLoad(opts.Script);
// Convert byte[] to hex string
var sha1 = BitConverter.ToString(sha1Bytes).Replace("-", string.Empty).ToLower();
Console.WriteLine(Output.Green(sha1));
return 0;
}
/// <summary>
/// Executes the SCRIPT EXISTS command to check if scripts exist in the script cache.
/// </summary>
/// <param name="opts">The ScriptExistsOptions containing instance and SHA1 hashes.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunScriptExists(ScriptExistsOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var server = redis.GetServer(redis.GetEndPoints().First());
foreach (var sha in opts.Sha1s)
{
var exists = server.ScriptExists(sha);
Console.WriteLine(Output.Green($"{sha}: {(exists ? "YES" : "NO")}"));
}
return 0;
}
/// <summary>
/// Executes the SCRIPT FLUSH command to remove all scripts from the script cache.
/// </summary>
/// <param name="opts">The ScriptFlushOptions containing instance.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunScriptFlush(ScriptFlushOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var server = redis.GetServer(redis.GetEndPoints().First());
server.ScriptFlush();
Console.WriteLine(Output.Green("Script cache flushed."));
return 0;
}
}
}

296
Commands/ServerCommands.cs Normal file
View File

@ -0,0 +1,296 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
namespace RedisManager.Commands
{
/// <summary>
/// Contains command line options and implementations for Redis server operations.
/// Provides functionality for INFO, CONFIG, SAVE, BGSAVE, LASTSAVE, TIME, and PING commands.
/// </summary>
[Verb("info", HelpText = "Get information and statistics about the server.")]
public class InfoOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Option("section", Required = false, HelpText = "Info section (server, clients, memory, etc.).")]
public string Section { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("config", HelpText = "Get/set Redis configuration parameters.")]
public class ConfigOptions
{
[Option("list-custom", Required = false, HelpText = "List all custom config parameters for this instance.")]
public bool ListCustom { get; set; }
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Option("get", Required = false, HelpText = "Get configuration parameter.")]
public string Get { get; set; }
[Option("set", Required = false, HelpText = "Set configuration parameter (format: parameter value).")]
public string Set { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("save", HelpText = "Synchronously save the dataset to disk.")]
public class SaveOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("bgsave", HelpText = "Asynchronously save the dataset to disk.")]
public class BgSaveOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("lastsave", HelpText = "Get the timestamp of the last successful save.")]
public class LastSaveOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("time", HelpText = "Return the current server time.")]
public class TimeOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("ping", HelpText = "Ping the server.")]
public class PingOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
public static class ServerCommands
{
/// <summary>
/// Executes the INFO command to get information and statistics about the server.
/// </summary>
/// <param name="opts">The InfoOptions containing instance and section.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunInfo(InfoOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var info = db.Execute("INFO", string.IsNullOrEmpty(opts.Section) ? null : opts.Section).ToString();
if (opts.Table)
{
var lines = info.Split('\n', StringSplitOptions.RemoveEmptyEntries);
var rows = lines.Select(l => l.Split(':', 2)).Where(parts => parts.Length == 2)
.Select(parts => new[] { parts[0], parts[1] }).ToList();
RedisUtils.PrintTable(new[] { "Parameter", "Value" }, rows);
}
else
{
Console.WriteLine(Output.Green(info));
}
return 0;
}
/// <summary>
/// Executes the CONFIG command to get or set Redis configuration parameters.
/// </summary>
/// <param name="opts">The ConfigOptions containing instance, get, set, and table options.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunConfig(ConfigOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
if (opts.ListCustom)
{
if (instance.CustomConfig != null && instance.CustomConfig.Count > 0)
{
RedisUtils.PrintTable(new[] { "Parameter", "Value" }, instance.CustomConfig.Select(kv => new[] { kv.Key, kv.Value }).ToList());
}
else
{
Console.WriteLine(Output.Yellow("No custom config parameters set for this instance."));
}
return 0;
}
if (!string.IsNullOrEmpty(opts.Get))
{
var result = db.Execute("CONFIG", "GET", opts.Get);
if (opts.Table)
{
var arr = (RedisResult[])result;
var rows = new List<string[]>();
for (int i = 0; i < arr.Length; i += 2)
{
if (i + 1 < arr.Length)
rows.Add(new[] { arr[i].ToString(), arr[i + 1].ToString() });
}
RedisUtils.PrintTable(new[] { "Parameter", "Value" }, rows);
}
else
{
var arr = (RedisResult[])result;
for (int i = 0; i < arr.Length; i += 2)
{
if (i + 1 < arr.Length)
Console.WriteLine(Output.Green($"{arr[i]} {arr[i + 1]}"));
}
}
}
else if (!string.IsNullOrEmpty(opts.Set))
{
var parts = opts.Set.Split(' ', 2);
if (parts.Length == 2)
{
db.Execute("CONFIG", "SET", parts[0], parts[1]);
// Persist custom config in InstanceConfig and save config file
var instanceConfig = config.Instances.FirstOrDefault(i => i.Name == opts.Instance);
if (instanceConfig != null)
{
instanceConfig.CustomConfig[parts[0]] = parts[1];
RedisManager.ConfigManager.SaveConfig(config);
}
if (opts.Table)
RedisUtils.PrintTable(new[] { "Operation", "Status" }, new List<string[]> { new[] { "CONFIG SET", "OK" } });
else
Console.WriteLine(Output.Green("OK"));
}
else
{
Console.WriteLine(Output.Red("Invalid format. Use: parameter value"));
return 1;
}
}
else
{
Console.WriteLine(Output.Red("Please specify --get or --set"));
return 1;
}
return 0;
}
/// <summary>
/// Executes the SAVE command to synchronously save the dataset to disk.
/// </summary>
/// <param name="opts">The SaveOptions containing instance and table option.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSave(SaveOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.Execute("SAVE");
if (opts.Table)
RedisUtils.PrintTable(new[] { "Operation", "Status" }, new List<string[]> { new[] { "SAVE", "OK" } });
else
Console.WriteLine(Output.Green("OK"));
return 0;
}
/// <summary>
/// Executes the BGSAVE command to asynchronously save the dataset to disk.
/// </summary>
/// <param name="opts">The BgSaveOptions containing instance and table option.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunBgSave(BgSaveOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.Execute("BGSAVE");
if (opts.Table)
RedisUtils.PrintTable(new[] { "Operation", "Status" }, new List<string[]> { new[] { "BGSAVE", "Background save started" } });
else
Console.WriteLine(Output.Green("Background save started"));
return 0;
}
/// <summary>
/// Executes the LASTSAVE command to get the timestamp of the last successful save.
/// </summary>
/// <param name="opts">The LastSaveOptions containing instance and table option.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunLastSave(LastSaveOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var timestamp = db.Execute("LASTSAVE").ToString();
var dateTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(timestamp));
if (opts.Table)
RedisUtils.PrintTable(new[] { "Last Save", "Timestamp", "DateTime" }, new List<string[]> { new[] { "Last Save", timestamp, dateTime.ToString() } });
else
Console.WriteLine(Output.Green($"{timestamp} ({dateTime})"));
return 0;
}
/// <summary>
/// Executes the TIME command to return the current server time.
/// </summary>
/// <param name="opts">The TimeOptions containing instance and table option.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunTime(TimeOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var result = db.Execute("TIME");
var arr = (RedisResult[])result;
var timestamp = arr[0].ToString();
var microseconds = arr[1].ToString();
var dateTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(timestamp));
if (opts.Table)
RedisUtils.PrintTable(new[] { "Timestamp", "Microseconds", "DateTime" }, new List<string[]> { new[] { timestamp, microseconds, dateTime.ToString() } });
else
Console.WriteLine(Output.Green($"{timestamp} {microseconds} ({dateTime})"));
return 0;
}
/// <summary>
/// Executes the PING command to ping the server.
/// </summary>
/// <param name="opts">The PingOptions containing instance and table option.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunPing(PingOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var response = db.Ping();
if (opts.Table)
RedisUtils.PrintTable(new[] { "Ping", "Response" }, new List<string[]> { new[] { "PING", response.ToString() } });
else
Console.WriteLine(Output.Green(response.ToString()));
return 0;
}
}
}

573
Commands/SetCommands.cs Normal file
View File

@ -0,0 +1,573 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
using RedisManager.Commands;
namespace RedisManager.Commands
{
[Verb("sadd", HelpText = "Add members to a set.")]
public class SAddOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
public string Key { get; set; }
[Value(1, MetaName = "members", Min = 1, Required = true, HelpText = "Members to add.")]
public IEnumerable<string> Members { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("srem", HelpText = "Remove members from a set.")]
public class SRemOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
public string Key { get; set; }
[Value(1, MetaName = "members", Min = 1, Required = true, HelpText = "Members to remove.")]
public IEnumerable<string> Members { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("smembers", HelpText = "Get all members of a set.")]
public class SMembersOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
public string Key { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("scard", HelpText = "Get the number of members in a set.")]
public class SCardOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
public string Key { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("sismember", HelpText = "Check if a member exists in a set.")]
public class SIsMemberOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
public string Key { get; set; }
[Value(1, MetaName = "member", Required = true, HelpText = "Member to check.")]
public string Member { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("srandmember", HelpText = "Get random member(s) from a set.")]
public class SRandMemberOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
public string Key { get; set; }
[Option("count", Required = false, HelpText = "Number of random members to get.")]
public int? Count { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("spop", HelpText = "Remove and return random member(s) from a set.")]
public class SPopOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
public string Key { get; set; }
[Option("count", Required = false, HelpText = "Number of members to pop.")]
public int? Count { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("sinter", HelpText = "Get intersection of multiple sets.")]
public class SInterOptions : InstanceOptions
{
[Value(0, MetaName = "keys", Min = 2, Required = true, HelpText = "Set keys.")]
public IEnumerable<string> Keys { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("sunion", HelpText = "Get union of multiple sets.")]
public class SUnionOptions : InstanceOptions
{
[Value(0, MetaName = "keys", Min = 2, Required = true, HelpText = "Set keys.")]
public IEnumerable<string> Keys { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("sdiff", HelpText = "Get difference of multiple sets.")]
public class SDiffOptions : InstanceOptions
{
[Value(0, MetaName = "keys", Min = 2, Required = true, HelpText = "Set keys.")]
public IEnumerable<string> Keys { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("sinterstore", HelpText = "Store intersection of multiple sets.")]
public class SInterStoreOptions : InstanceOptions
{
[Value(0, MetaName = "destination", Required = true, HelpText = "Destination set key.")]
public string Destination { get; set; }
[Value(1, MetaName = "keys", Min = 2, Required = true, HelpText = "Source set keys.")]
public IEnumerable<string> Keys { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("sunionstore", HelpText = "Store union of multiple sets.")]
public class SUnionStoreOptions : InstanceOptions
{
[Value(0, MetaName = "destination", Required = true, HelpText = "Destination set key.")]
public string Destination { get; set; }
[Value(1, MetaName = "keys", Min = 2, Required = true, HelpText = "Source set keys.")]
public IEnumerable<string> Keys { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("sdiffstore", HelpText = "Store difference of multiple sets.")]
public class SDiffStoreOptions : InstanceOptions
{
[Value(0, MetaName = "destination", Required = true, HelpText = "Destination set key.")]
public string Destination { get; set; }
[Value(1, MetaName = "keys", Min = 2, Required = true, HelpText = "Source set keys.")]
public IEnumerable<string> Keys { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("sscan", HelpText = "Scan set members.")]
public class SScanOptions : InstanceOptions
{
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
public string Key { get; set; }
[Value(1, MetaName = "cursor", Required = true, HelpText = "Cursor position.")]
public int Cursor { get; set; }
[Option("match", Required = false, HelpText = "Pattern to match.")]
public string Match { get; set; }
[Option("count", Required = false, HelpText = "Count hint.")]
public int? Count { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("smove", HelpText = "Move member from one set to another.")]
public class SMoveOptions : InstanceOptions
{
[Value(0, MetaName = "source", Required = true, HelpText = "Source set key.")]
public string Source { get; set; }
[Value(1, MetaName = "destination", Required = true, HelpText = "Destination set key.")]
public string Destination { get; set; }
[Value(2, MetaName = "member", Required = true, HelpText = "Member to move.")]
public string Member { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
public static class SetCommands
{
/// <summary>
/// Executes the SADD command to add members to a set.
/// </summary>
/// <param name="opts">The SAddOptions containing instance, key, and members.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSAdd(SAddOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var members = opts.Members.ToArray();
var count = db.SetAdd(opts.Key, members.Select(m => (RedisValue)m).ToArray());
if (opts.Table)
{
var rows = members.Select(m => new[] { m, "Added" }).ToList();
RedisUtils.PrintTable(new[] { "Member", "Status" }, rows);
RedisUtils.PrintTable(new[] { "Key", "Added Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
}
else
Console.WriteLine(Output.Green($"Added {count} new member(s)"));
return 0;
}
/// <summary>
/// Executes the SREM command to remove members from a set.
/// </summary>
/// <param name="opts">The SRemOptions containing instance, key, and members.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSRem(SRemOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var members = opts.Members.ToArray();
var count = db.SetRemove(opts.Key, members.Select(m => (RedisValue)m).ToArray());
if (opts.Table)
{
var rows = members.Select(m => new[] { m, "Removed" }).ToList();
RedisUtils.PrintTable(new[] { "Member", "Status" }, rows);
RedisUtils.PrintTable(new[] { "Key", "Removed Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
}
else
Console.WriteLine(Output.Green($"Removed {count} member(s)"));
return 0;
}
/// <summary>
/// Executes the SMEMBERS command to get all members of a set.
/// </summary>
/// <param name="opts">The SMembersOptions containing instance and key.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSMembers(SMembersOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var members = db.SetMembers(opts.Key);
if (opts.Table)
{
var rows = members.Select(m => new[] { m.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Member" }, rows);
}
else
foreach (var member in members)
Console.WriteLine(Output.Green(member.ToString()));
return 0;
}
/// <summary>
/// Executes the SCARD command to get the number of members in a set.
/// </summary>
/// <param name="opts">The SCardOptions containing instance and key.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSCard(SCardOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var count = db.SetLength(opts.Key);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Cardinality" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
else
Console.WriteLine(Output.Green(count.ToString()));
return 0;
}
/// <summary>
/// Executes the SISMEMBER command to check if a member exists in a set.
/// </summary>
/// <param name="opts">The SIsMemberOptions containing instance, key, and member.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSIsMember(SIsMemberOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var exists = db.SetContains(opts.Key, opts.Member);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Member", "Exists" }, new List<string[]> { new[] { opts.Key, opts.Member, exists.ToString() } });
else
Console.WriteLine(Output.Green(exists.ToString()));
return 0;
}
/// <summary>
/// Executes the SRANDMEMBER command to get random member(s) from a set.
/// </summary>
/// <param name="opts">The SRandMemberOptions containing instance, key, and count.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSRandMember(SRandMemberOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
if (opts.Count.HasValue)
{
var members = db.SetRandomMembers(opts.Key, opts.Count.Value);
if (opts.Table)
{
var rows = members.Select(m => new[] { m.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Random Member" }, rows);
}
else
foreach (var member in members)
Console.WriteLine(Output.Green(member.ToString()));
}
else
{
var member = db.SetRandomMember(opts.Key);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Random Member" }, new List<string[]> { new[] { opts.Key, member.ToString() } });
else
Console.WriteLine(Output.Green(member.ToString()));
}
return 0;
}
/// <summary>
/// Executes the SPOP command to remove and return random member(s) from a set.
/// </summary>
/// <param name="opts">The SPopOptions containing instance, key, and count.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSPop(SPopOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
if (opts.Count.HasValue)
{
var members = db.SetPop(opts.Key, opts.Count.Value);
if (opts.Table)
{
var rows = members.Select(m => new[] { m.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Popped Member" }, rows);
}
else
foreach (var member in members)
Console.WriteLine(Output.Green(member.ToString()));
}
else
{
var member = db.SetPop(opts.Key);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Popped Member" }, new List<string[]> { new[] { opts.Key, member.ToString() } });
else
Console.WriteLine(Output.Green(member.ToString()));
}
return 0;
}
/// <summary>
/// Executes the SINTER command to get the intersection of multiple sets.
/// </summary>
/// <param name="opts">The SInterOptions containing instance and keys.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSInter(SInterOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
var members = db.SetCombine(SetOperation.Intersect, keys);
if (opts.Table)
{
var rows = members.Select(m => new[] { m.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Intersection Member" }, rows);
}
else
foreach (var member in members)
Console.WriteLine(Output.Green(member.ToString()));
return 0;
}
/// <summary>
/// Executes the SUNION command to get the union of multiple sets.
/// </summary>
/// <param name="opts">The SUnionOptions containing instance and keys.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSUnion(SUnionOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
var members = db.SetCombine(SetOperation.Union, keys);
if (opts.Table)
{
var rows = members.Select(m => new[] { m.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Union Member" }, rows);
}
else
foreach (var member in members)
Console.WriteLine(Output.Green(member.ToString()));
return 0;
}
/// <summary>
/// Executes the SDIFF command to get the difference of multiple sets.
/// </summary>
/// <param name="opts">The SDiffOptions containing instance and keys.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSDiff(SDiffOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
var members = db.SetCombine(SetOperation.Difference, keys);
if (opts.Table)
{
var rows = members.Select(m => new[] { m.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Difference Member" }, rows);
}
else
foreach (var member in members)
Console.WriteLine(Output.Green(member.ToString()));
return 0;
}
/// <summary>
/// Executes the SINTERSTORE command to store the intersection of multiple sets.
/// </summary>
/// <param name="opts">The SInterStoreOptions containing instance, destination, and keys.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSInterStore(SInterStoreOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
var count = db.SetCombineAndStore(SetOperation.Intersect, opts.Destination, keys);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Destination", "Stored Count" }, new List<string[]> { new[] { opts.Destination, count.ToString() } });
else
Console.WriteLine(Output.Green($"Stored {count} member(s) in {opts.Destination}"));
return 0;
}
/// <summary>
/// Executes the SUNIONSTORE command to store the union of multiple sets.
/// </summary>
/// <param name="opts">The SUnionStoreOptions containing instance, destination, and keys.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSUnionStore(SUnionStoreOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
var count = db.SetCombineAndStore(SetOperation.Union, opts.Destination, keys);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Destination", "Stored Count" }, new List<string[]> { new[] { opts.Destination, count.ToString() } });
else
Console.WriteLine(Output.Green($"Stored {count} member(s) in {opts.Destination}"));
return 0;
}
/// <summary>
/// Executes the SDIFFSTORE command to store the difference of multiple sets.
/// </summary>
/// <param name="opts">The SDiffStoreOptions containing instance, destination, and keys.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSDiffStore(SDiffStoreOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
var count = db.SetCombineAndStore(SetOperation.Difference, opts.Destination, keys);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Destination", "Stored Count" }, new List<string[]> { new[] { opts.Destination, count.ToString() } });
else
Console.WriteLine(Output.Green($"Stored {count} member(s) in {opts.Destination}"));
return 0;
}
/// <summary>
/// Executes the SSCAN command to scan set members.
/// </summary>
/// <param name="opts">The SScanOptions containing instance, key, cursor, and scan options.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSScan(SScanOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var args = new List<object> { opts.Cursor };
if (!string.IsNullOrEmpty(opts.Match))
{
args.Add("MATCH");
args.Add(opts.Match);
}
if (opts.Count.HasValue)
{
args.Add("COUNT");
args.Add(opts.Count.Value);
}
var result = db.Execute("SSCAN", opts.Key, args.ToArray());
var arr = (RedisResult[])result;
var newCursor = (int)arr[0];
var members = (RedisResult[])arr[1];
if (opts.Table)
{
var rows = members.Select(m => new[] { m.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Member" }, rows);
RedisUtils.PrintTable(new[] { "Next Cursor" }, new List<string[]> { new[] { newCursor.ToString() } });
}
else
{
foreach (var member in members)
Console.WriteLine(Output.Green(member.ToString()));
Console.WriteLine(Output.Yellow($"Next cursor: {newCursor}"));
}
return 0;
}
/// <summary>
/// Executes the SMOVE command to move a member from one set to another.
/// </summary>
/// <param name="opts">The SMoveOptions containing instance, source, destination, and member.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunSMove(SMoveOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var moved = db.SetMove(opts.Source, opts.Destination, opts.Member);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Source", "Destination", "Member", "Moved" }, new List<string[]> { new[] { opts.Source, opts.Destination, opts.Member, moved.ToString() } });
else
Console.WriteLine(Output.Green(moved.ToString()));
return 0;
}
}
}

View File

@ -0,0 +1,836 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
namespace RedisManager.Commands
{
/// <summary>
/// Contains command line options and implementations for Redis sorted set operations.
/// Provides functionality for ZADD, ZREM, ZRANGE, ZREVRANGE, ZRANGEBYSCORE, ZCARD, ZSCORE, ZRANK, ZREVRANK, ZINCRBY, ZREVRANGEBYSCORE, ZCOUNT, ZUNIONSTORE, ZINTERSTORE, ZSCAN, ZPOPMAX, ZPOPMIN, ZREMRANGEBYRANK, and ZREMRANGEBYSCORE commands.
/// </summary>
[Verb("zadd", HelpText = "Add members to a sorted set.")]
public class ZAddOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "score", Required = true, HelpText = "Score for the member.")]
public double Score { get; set; }
[Value(2, MetaName = "member", Required = true, HelpText = "Member to add.")]
public string Member { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zrem", HelpText = "Remove members from a sorted set.")]
public class ZRemOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "members", Min = 1, Required = true, HelpText = "Members to remove.")]
public IEnumerable<string> Members { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zrange", HelpText = "Get range of members by rank.")]
public class ZRangeOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "start", Required = true, HelpText = "Start rank.")]
public long Start { get; set; }
[Value(2, MetaName = "stop", Required = true, HelpText = "Stop rank.")]
public long Stop { get; set; }
[Option("withscores", Required = false, HelpText = "Include scores in output.")]
public bool WithScores { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zrevrange", HelpText = "Get range of members by rank (reverse order).")]
public class ZRevRangeOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "start", Required = true, HelpText = "Start rank.")]
public long Start { get; set; }
[Value(2, MetaName = "stop", Required = true, HelpText = "Stop rank.")]
public long Stop { get; set; }
[Option("withscores", Required = false, HelpText = "Include scores in output.")]
public bool WithScores { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zrangebyscore", HelpText = "Get range of members by score.")]
public class ZRangeByScoreOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "min", Required = true, HelpText = "Minimum score.")]
public double Min { get; set; }
[Value(2, MetaName = "max", Required = true, HelpText = "Maximum score.")]
public double Max { get; set; }
[Option("withscores", Required = false, HelpText = "Include scores in output.")]
public bool WithScores { get; set; }
[Option("limit", Required = false, HelpText = "Limit results (offset count).")]
public string Limit { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zcard", HelpText = "Get the number of members in a sorted set.")]
public class ZCardOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zscore", HelpText = "Get the score of a member.")]
public class ZScoreOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "member", Required = true, HelpText = "Member name.")]
public string Member { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zrank", HelpText = "Get the rank of a member.")]
public class ZRankOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "member", Required = true, HelpText = "Member name.")]
public string Member { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zrevrank", HelpText = "Get the reverse rank of a member.")]
public class ZRevRankOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "member", Required = true, HelpText = "Member name.")]
public string Member { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zincrby", HelpText = "Increment the score of a member.")]
public class ZIncrByOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "increment", Required = true, HelpText = "Score increment.")]
public double Increment { get; set; }
[Value(2, MetaName = "member", Required = true, HelpText = "Member name.")]
public string Member { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zrevrangebyscore", HelpText = "Get range of members by score (reverse order).")]
public class ZRevRangeByScoreOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "max", Required = true, HelpText = "Maximum score.")]
public double Max { get; set; }
[Value(2, MetaName = "min", Required = true, HelpText = "Minimum score.")]
public double Min { get; set; }
[Option("withscores", Required = false, HelpText = "Include scores in output.")]
public bool WithScores { get; set; }
[Option("limit", Required = false, HelpText = "Limit results (offset count).")]
public string Limit { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zcount", HelpText = "Count members within a score range.")]
public class ZCountOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "min", Required = true, HelpText = "Minimum score.")]
public double Min { get; set; }
[Value(2, MetaName = "max", Required = true, HelpText = "Maximum score.")]
public double Max { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zunionstore", HelpText = "Store union of multiple sorted sets.")]
public class ZUnionStoreOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "destination", Required = true, HelpText = "Destination sorted set key.")]
public string Destination { get; set; }
[Value(1, MetaName = "numkeys", Required = true, HelpText = "Number of source keys.")]
public int NumKeys { get; set; }
[Value(2, MetaName = "keys", Min = 1, Required = true, HelpText = "Source sorted set keys.")]
public IEnumerable<string> Keys { get; set; }
[Option("weights", Required = false, HelpText = "Weights for each source set.")]
public IEnumerable<double> Weights { get; set; }
[Option("aggregate", Required = false, HelpText = "Aggregation method (sum, min, max).")]
public string Aggregate { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zinterstore", HelpText = "Store intersection of multiple sorted sets.")]
public class ZInterStoreOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "destination", Required = true, HelpText = "Destination sorted set key.")]
public string Destination { get; set; }
[Value(1, MetaName = "numkeys", Required = true, HelpText = "Number of source keys.")]
public int NumKeys { get; set; }
[Value(2, MetaName = "keys", Min = 1, Required = true, HelpText = "Source sorted set keys.")]
public IEnumerable<string> Keys { get; set; }
[Option("weights", Required = false, HelpText = "Weights for each source set.")]
public IEnumerable<double> Weights { get; set; }
[Option("aggregate", Required = false, HelpText = "Aggregation method (sum, min, max).")]
public string Aggregate { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zscan", HelpText = "Scan sorted set members.")]
public class ZScanOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "cursor", Required = true, HelpText = "Cursor position.")]
public int Cursor { get; set; }
[Option("match", Required = false, HelpText = "Pattern to match.")]
public string Match { get; set; }
[Option("count", Required = false, HelpText = "Count hint.")]
public int? Count { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zpopmax", HelpText = "Remove and return members with highest scores.")]
public class ZPopMaxOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Option("count", Required = false, HelpText = "Number of members to pop.")]
public int? Count { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zpopmin", HelpText = "Remove and return members with lowest scores.")]
public class ZPopMinOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Option("count", Required = false, HelpText = "Number of members to pop.")]
public int? Count { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zremrangebyrank", HelpText = "Remove members by rank range.")]
public class ZRemRangeByRankOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "start", Required = true, HelpText = "Start rank.")]
public long Start { get; set; }
[Value(2, MetaName = "stop", Required = true, HelpText = "Stop rank.")]
public long Stop { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("zremrangebyscore", HelpText = "Remove members by score range.")]
public class ZRemRangeByScoreOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
public string Key { get; set; }
[Value(1, MetaName = "min", Required = true, HelpText = "Minimum score.")]
public double Min { get; set; }
[Value(2, MetaName = "max", Required = true, HelpText = "Maximum score.")]
public double Max { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
public static class SortedSetCommands
{
/// <summary>
/// Executes the ZADD command to add members to a sorted set.
/// </summary>
/// <param name="opts">The ZAddOptions containing instance, key, score, and member.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZAdd(ZAddOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var count = db.SortedSetAdd(opts.Key, opts.Member, opts.Score);
if (opts.Table)
{
RedisUtils.PrintTable(new[] { "Key", "Added Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
}
else
Console.WriteLine(Output.Green($"Added {count} new member(s)"));
return 0;
}
/// <summary>
/// Executes the ZREM command to remove members from a sorted set.
/// </summary>
/// <param name="opts">The ZRemOptions containing instance, key, and members.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZRem(ZRemOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var members = opts.Members.ToArray();
var count = db.SortedSetRemove(opts.Key, members.Select(m => (RedisValue)m).ToArray());
if (opts.Table)
{
var rows = members.Select(m => new[] { m, "Removed" }).ToList();
RedisUtils.PrintTable(new[] { "Member", "Status" }, rows);
RedisUtils.PrintTable(new[] { "Key", "Removed Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
}
else
Console.WriteLine(Output.Green($"Removed {count} member(s)"));
return 0;
}
/// <summary>
/// Executes the ZRANGE command to get a range of members by rank.
/// </summary>
/// <param name="opts">The ZRangeOptions containing instance, key, start, and stop.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZRange(ZRangeOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
if (opts.WithScores)
{
var entries = db.SortedSetRangeByRankWithScores(opts.Key, opts.Start, opts.Stop, Order.Ascending);
if (opts.Table)
{
var rows = entries.Select(e => new string[] { e.Element.ToString(), e.Score.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Member", "Score" }, rows);
}
else
{
foreach (var e in entries)
Console.WriteLine(Output.Green($"{e.Element} {e.Score}"));
}
}
else
{
var members = db.SortedSetRangeByRank(opts.Key, opts.Start, opts.Stop, Order.Ascending);
if (opts.Table)
{
var rows = members.Select(m => new[] { m.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Member" }, rows);
}
else
{
foreach (var member in members)
Console.WriteLine(Output.Green(member.ToString()));
}
}
return 0;
}
/// <summary>
/// Executes the ZREVRANGE command to get a range of members by rank in reverse order.
/// </summary>
/// <param name="opts">The ZRevRangeOptions containing instance, key, start, and stop.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZRevRange(ZRevRangeOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
if (opts.WithScores)
{
var entries = db.SortedSetRangeByRankWithScores(opts.Key, opts.Start, opts.Stop, Order.Descending);
if (opts.Table)
{
var rows = entries.Select(e => new string[] { e.Element.ToString(), e.Score.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Member", "Score" }, rows);
}
else
{
foreach (var e in entries)
Console.WriteLine(Output.Green($"{e.Element} {e.Score}"));
}
}
else
{
var members = db.SortedSetRangeByRank(opts.Key, opts.Start, opts.Stop, Order.Descending);
if (opts.Table)
{
var rows = members.Select(m => new[] { m.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Member" }, rows);
}
else
{
foreach (var member in members)
Console.WriteLine(Output.Green(member.ToString()));
}
}
return 0;
}
/// <summary>
/// Executes the ZRANGEBYSCORE command to get a range of members by score.
/// </summary>
/// <param name="opts">The ZRangeByScoreOptions containing instance, key, min, and max.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZRangeByScore(ZRangeByScoreOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
if (opts.WithScores)
{
var entries = db.SortedSetRangeByScoreWithScores(opts.Key, opts.Min, opts.Max, Exclude.None, Order.Ascending);
if (opts.Table)
{
var rows = entries.Select(e => new string[] { e.Element.ToString(), e.Score.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Member", "Score" }, rows);
}
else
{
foreach (var e in entries)
Console.WriteLine(Output.Green($"{e.Element} {e.Score}"));
}
}
else
{
var members = db.SortedSetRangeByScore(opts.Key, opts.Min, opts.Max, Exclude.None, Order.Ascending);
if (opts.Table)
{
var rows = members.Select(m => new[] { m.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Member" }, rows);
}
else
{
foreach (var member in members)
Console.WriteLine(Output.Green(member.ToString()));
}
}
return 0;
}
/// <summary>
/// Executes the ZCARD command to get the number of members in a sorted set.
/// </summary>
/// <param name="opts">The ZCardOptions containing instance and key.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZCard(ZCardOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var count = db.SortedSetLength(opts.Key);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Cardinality" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
else
Console.WriteLine(Output.Green(count.ToString()));
return 0;
}
/// <summary>
/// Executes the ZSCORE command to get the score of a member in a sorted set.
/// </summary>
/// <param name="opts">The ZScoreOptions containing instance, key, and member.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZScore(ZScoreOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var score = db.SortedSetScore(opts.Key, opts.Member);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Member", "Score" }, new List<string[]> { new[] { opts.Key, opts.Member, score?.ToString() ?? "[null]" } });
else if (score.HasValue)
Console.WriteLine(Output.Green(score.Value.ToString()));
else
Console.WriteLine(Output.Yellow("[null]"));
return 0;
}
/// <summary>
/// Executes the ZRANK command to get the rank of a member in a sorted set.
/// </summary>
/// <param name="opts">The ZRankOptions containing instance, key, and member.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZRank(ZRankOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var rank = db.SortedSetRank(opts.Key, opts.Member);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Member", "Rank" }, new List<string[]> { new[] { opts.Key, opts.Member, rank?.ToString() ?? "[null]" } });
else if (rank.HasValue)
Console.WriteLine(Output.Green(rank.Value.ToString()));
else
Console.WriteLine(Output.Yellow("[null]"));
return 0;
}
/// <summary>
/// Executes the ZREVRANK command to get the reverse rank of a member in a sorted set.
/// </summary>
/// <param name="opts">The ZRevRankOptions containing instance, key, and member.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZRevRank(ZRevRankOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var rank = db.SortedSetRank(opts.Key, opts.Member, Order.Descending);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Member", "Reverse Rank" }, new List<string[]> { new[] { opts.Key, opts.Member, rank?.ToString() ?? "[null]" } });
else if (rank.HasValue)
Console.WriteLine(Output.Green(rank.Value.ToString()));
else
Console.WriteLine(Output.Yellow("[null]"));
return 0;
}
/// <summary>
/// Executes the ZINCRBY command to increment the score of a member in a sorted set.
/// </summary>
/// <param name="opts">The ZIncrByOptions containing instance, key, increment, and member.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZIncrBy(ZIncrByOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var newScore = db.SortedSetIncrement(opts.Key, opts.Member, opts.Increment);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Member", "New Score" }, new List<string[]> { new[] { opts.Key, opts.Member, newScore.ToString() } });
else
Console.WriteLine(Output.Green(newScore.ToString()));
return 0;
}
/// <summary>
/// Executes the ZREVRANGEBYSCORE command to get a range of members by score in reverse order.
/// </summary>
/// <param name="opts">The ZRevRangeByScoreOptions containing instance, key, max, and min.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZRevRangeByScore(ZRevRangeByScoreOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
if (opts.WithScores)
{
var entries = db.SortedSetRangeByScoreWithScores(opts.Key, opts.Min, opts.Max, Exclude.None, Order.Descending);
if (opts.Table)
{
var rows = entries.Select(e => new string[] { e.Element.ToString(), e.Score.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Member", "Score" }, rows);
}
else
foreach (var entry in entries)
Console.WriteLine(Output.Green($"{entry.Element}: {entry.Score}"));
}
else
{
var members = db.SortedSetRangeByScore(opts.Key, opts.Min, opts.Max, Exclude.None, Order.Descending);
if (opts.Table)
{
var rows = members.Select(m => new string[] { m.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Member" }, rows);
}
else
foreach (var member in members)
Console.WriteLine(Output.Green(member.ToString()));
}
return 0;
}
/// <summary>
/// Executes the ZCOUNT command to count members within a score range in a sorted set.
/// </summary>
/// <param name="opts">The ZCountOptions containing instance, key, min, and max.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZCount(ZCountOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var count = db.SortedSetLengthByValue(opts.Key, opts.Min, opts.Max);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
else
Console.WriteLine(Output.Green(count.ToString()));
return 0;
}
/// <summary>
/// Executes the ZUNIONSTORE command to store the union of multiple sorted sets.
/// </summary>
/// <param name="opts">The ZUnionStoreOptions containing instance, destination, numkeys, and keys.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZUnionStore(ZUnionStoreOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
var count = db.SortedSetCombineAndStore(SetOperation.Union, opts.Destination, keys);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Destination", "Stored Count" }, new List<string[]> { new[] { opts.Destination, count.ToString() } });
else
Console.WriteLine(Output.Green($"Stored {count} member(s) in {opts.Destination}"));
return 0;
}
/// <summary>
/// Executes the ZINTERSTORE command to store the intersection of multiple sorted sets.
/// </summary>
/// <param name="opts">The ZInterStoreOptions containing instance, destination, numkeys, and keys.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZInterStore(ZInterStoreOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
var count = db.SortedSetCombineAndStore(SetOperation.Intersect, opts.Destination, keys);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Destination", "Stored Count" }, new List<string[]> { new[] { opts.Destination, count.ToString() } });
else
Console.WriteLine(Output.Green($"Stored {count} member(s) in {opts.Destination}"));
return 0;
}
/// <summary>
/// Executes the ZSCAN command to scan sorted set members.
/// </summary>
/// <param name="opts">The ZScanOptions containing instance, key, cursor, and scan options.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZScan(ZScanOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var result = db.SortedSetScan(opts.Key, opts.Match ?? "*", pageSize: opts.Count ?? 10, cursor: opts.Cursor);
if (opts.Table)
{
var rows = result.Select(e => new[] { e.Element.ToString(), e.Score.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Member", "Score" }, rows);
}
else
{
foreach (var entry in result)
Console.WriteLine(Output.Green($"{entry.Element}: {entry.Score}"));
}
return 0;
}
/// <summary>
/// Executes the ZPOPMAX command to remove and return members with the highest scores.
/// </summary>
/// <param name="opts">The ZPopMaxOptions containing instance, key, and count.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZPopMax(ZPopMaxOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
if (opts.Count.HasValue)
{
var entries = db.SortedSetPop(opts.Key, opts.Count.Value);
if (opts.Table)
{
var rows = entries.Select(e => new string[] { e.Element.ToString(), e.Score.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Popped Member", "Score" }, rows);
}
else
foreach (var entry in entries)
Console.WriteLine(Output.Green($"{entry.Element}: {entry.Score}"));
}
else
{
var entry = db.SortedSetPop(opts.Key);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Popped Member", "Score" }, new List<string[]> { new[] { entry.Value.Element.ToString(), entry.Value.Score.ToString() } });
else
Console.WriteLine(Output.Green($"{entry.Value.Element}: {entry.Value.Score}"));
}
return 0;
}
/// <summary>
/// Executes the ZPOPMIN command to remove and return members with the lowest scores.
/// </summary>
/// <param name="opts">The ZPopMinOptions containing instance, key, and count.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZPopMin(ZPopMinOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
if (opts.Count.HasValue)
{
var entries = db.SortedSetPop(opts.Key, opts.Count.Value);
if (opts.Table)
{
var rows = entries.Select(e => new string[] { e.Element.ToString(), e.Score.ToString() }).ToList();
RedisUtils.PrintTable(new[] { "Popped Member", "Score" }, rows);
}
else
foreach (var entry in entries)
Console.WriteLine(Output.Green($"{entry.Element}: {entry.Score}"));
}
else
{
var entry = db.SortedSetPop(opts.Key);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Popped Member", "Score" }, new List<string[]> { new[] { entry.Value.Element.ToString(), entry.Value.Score.ToString() } });
else
Console.WriteLine(Output.Green($"{entry.Value.Element}: {entry.Value.Score}"));
}
return 0;
}
/// <summary>
/// Executes the ZREMRANGEBYRANK command to remove members by rank range.
/// </summary>
/// <param name="opts">The ZRemRangeByRankOptions containing instance, key, start, and stop.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZRemRangeByRank(ZRemRangeByRankOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var count = db.SortedSetRemoveRangeByRank(opts.Key, opts.Start, opts.Stop);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Removed Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
else
Console.WriteLine(Output.Green($"Removed {count} member(s)"));
return 0;
}
/// <summary>
/// Executes the ZREMRANGEBYSCORE command to remove members by score range.
/// </summary>
/// <param name="opts">The ZRemRangeByScoreOptions containing instance, key, min, and max.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunZRemRangeByScore(ZRemRangeByScoreOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var count = db.SortedSetRemoveRangeByScore(opts.Key, opts.Min, opts.Max);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Key", "Removed Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
else
Console.WriteLine(Output.Green($"Removed {count} member(s)"));
return 0;
}
}
}

46
Commands/StatusCommand.cs Normal file
View File

@ -0,0 +1,46 @@
using CommandLine;
using StackExchange.Redis;
using System;
using RedisManager.Utils;
namespace RedisManager.Commands
{
/// <summary>
/// Command line options for the status command.
/// Used to check the connectivity and response time of a Redis instance.
/// </summary>
[Verb("status", HelpText = "Check status of a Redis instance.")]
public class StatusOptions
{
/// <summary>
/// Gets or sets the name of the Redis instance to check.
/// This must match a configured instance name in the configuration file.
/// </summary>
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
}
/// <summary>
/// Static class containing the implementation for the status command.
/// Provides functionality to check Redis instance connectivity and response time.
/// </summary>
public static class StatusCommand
{
/// <summary>
/// Executes the status command to check Redis instance connectivity.
/// Connects to the specified Redis instance and measures the ping response time.
/// </summary>
/// <param name="opts">The status command options containing the instance name</param>
/// <param name="config">The RedisManager configuration containing instance details</param>
/// <returns>Exit code (0 for success, non-zero for failure)</returns>
public static int RunStatus(StatusOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var pong = db.Ping();
Console.WriteLine(Output.Green($"PONG: {pong.TotalMilliseconds}ms"));
return 0;
}
}
}

154
Commands/StreamCommands.cs Normal file
View File

@ -0,0 +1,154 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
namespace RedisManager.Commands
{
[Verb("xadd", HelpText = "Add an entry to a stream.")]
public class XAddOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "stream", Required = true, HelpText = "Stream key.")]
public string Stream { get; set; }
[Option("id", Required = false, HelpText = "Entry ID (default: *)")]
public string Id { get; set; } = "*";
[Option("fields", Required = true, HelpText = "Field-value pairs as field1:value1,field2:value2...")]
public IEnumerable<string> Fields { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("xrange", HelpText = "Get entries from a stream within a range.")]
public class XRangeOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "stream", Required = true, HelpText = "Stream key.")]
public string Stream { get; set; }
[Option("start", Required = false, HelpText = "Start ID (default: -)")]
public string Start { get; set; } = "-";
[Option("end", Required = false, HelpText = "End ID (default: +)")]
public string End { get; set; } = "+";
[Option("count", Required = false, HelpText = "Maximum number of entries")]
public int? Count { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("xlen", HelpText = "Get the length of a stream.")]
public class XLenOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "stream", Required = true, HelpText = "Stream key.")]
public string Stream { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
[Verb("xdel", HelpText = "Remove entries from a stream.")]
public class XDelOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "stream", Required = true, HelpText = "Stream key.")]
public string Stream { get; set; }
[Value(1, MetaName = "ids", Min = 1, Required = true, HelpText = "Entry IDs to delete.")]
public IEnumerable<string> Ids { get; set; }
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
public static class StreamCommands
{
public static int RunXAdd(XAddOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
// Use XADD command directly
var args = new List<RedisValue> { opts.Stream, opts.Id };
foreach (var field in opts.Fields)
{
var parts = field.Split(':', 2);
if (parts.Length == 2)
{
args.Add(parts[0]);
args.Add(parts[1]);
}
}
var id = db.Execute("XADD", args.ToArray());
if (opts.Table)
RedisUtils.PrintTable(new[] { "Stream", "ID", "Fields" }, new List<string[]> { new[] { opts.Stream, id.ToString(), string.Join(", ", opts.Fields) } });
else
Console.WriteLine(Output.Green(id.ToString()));
return 0;
}
public static int RunXRange(XRangeOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
// Use XRANGE command directly
var args = new List<RedisValue> { opts.Stream, opts.Start, opts.End };
if (opts.Count.HasValue)
{
args.Add("COUNT");
args.Add(opts.Count.Value);
}
var result = db.Execute("XRANGE", args.ToArray());
if (opts.Table)
{
Console.WriteLine(Output.Yellow("Table output not supported for XRANGE yet"));
}
else
Console.WriteLine(Output.Green(result.ToString()));
return 0;
}
public static int RunXLen(XLenOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
// Use XLEN command directly
var length = db.Execute("XLEN", opts.Stream);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Stream", "Length" }, new List<string[]> { new[] { opts.Stream, length.ToString() } });
else
Console.WriteLine(Output.Green(length.ToString()));
return 0;
}
public static int RunXDel(XDelOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
// Use XDEL command directly
var args = new List<RedisValue> { opts.Stream };
args.AddRange(opts.Ids.Select(id => (RedisValue)id));
var count = db.Execute("XDEL", args.ToArray());
if (opts.Table)
RedisUtils.PrintTable(new[] { "Stream", "Deleted Count" }, new List<string[]> { new[] { opts.Stream, count.ToString() } });
else
Console.WriteLine(Output.Green($"Deleted {count} entry(ies)"));
return 0;
}
}
}

201
Commands/StringCommands.cs Normal file
View File

@ -0,0 +1,201 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using RedisManager.Utils;
using RedisManager.Commands;
namespace RedisManager.Commands
{
/// <summary>
/// Command line options for the GET command.
/// Used to retrieve the value of a Redis key.
/// </summary>
[Verb("get", HelpText = "Get value of a key.")]
public class GetOptions : InstanceOptions
{
/// <summary>
/// Gets or sets the key name to retrieve the value for.
/// </summary>
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
public string Key { get; set; }
/// <summary>
/// Gets or sets whether to output the result in table format.
/// </summary>
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
/// <summary>
/// Command line options for the SET command.
/// Used to set the value of a Redis key with optional TTL.
/// </summary>
[Verb("set", HelpText = "Set value of a key.")]
public class SetOptions : InstanceOptions
{
/// <summary>
/// Gets or sets the key name to set the value for.
/// </summary>
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
public string Key { get; set; }
/// <summary>
/// Gets or sets the value to store in the key.
/// </summary>
[Value(1, MetaName = "value", Required = true, HelpText = "Value.")]
public string Value { get; set; }
/// <summary>
/// Gets or sets the time to live in seconds for the key.
/// </summary>
[Option("ttl", Required = false, HelpText = "Time to live in seconds.")]
public int? TTL { get; set; }
}
/// <summary>
/// Command line options for the DEL command.
/// Used to delete one or more Redis keys with optional confirmation.
/// </summary>
[Verb("del", HelpText = "Delete one or more keys.")]
public class DelOptions : InstanceOptions
{
/// <summary>
/// Gets or sets whether to skip the confirmation prompt.
/// </summary>
[Option("yes", Required = false, HelpText = "Skip confirmation.")]
public bool Yes { get; set; }
/// <summary>
/// Gets or sets the collection of keys to delete.
/// </summary>
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "Key(s) to delete.")]
public IEnumerable<string> Keys { get; set; }
/// <summary>
/// Gets or sets whether to output the result in table format.
/// </summary>
[Option("table", Required = false, HelpText = "Output as table.")]
public bool Table { get; set; }
}
/// <summary>
/// Static class containing implementations for basic Redis string operations.
/// Provides functionality for GET, SET, and DEL commands with various output formats.
/// </summary>
public static class StringCommands
{
/// <summary>
/// Executes the GET command to retrieve a value from Redis.
/// Supports table output format and JSON pretty-printing.
/// </summary>
/// <param name="opts">The GET command options</param>
/// <param name="config">The RedisManager configuration</param>
/// <returns>Exit code (0 for success, non-zero for failure)</returns>
public static int RunGet(GetOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var value = db.StringGet(opts.Key);
if (opts.Table)
{
RedisUtils.PrintTable(new[] { "Key", "Value" }, new List<string[]> { new[] { opts.Key, value.IsNull ? "[null]" : value.ToString() } });
}
else if (value.IsNull)
Console.WriteLine(Output.Yellow($"[null]"));
else
{
// Try pretty-print JSON
string str = value.ToString();
if (IsJson(str))
{
try
{
var doc = JsonDocument.Parse(str);
var pretty = JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(Output.Green(pretty));
}
catch { Console.WriteLine(Output.Green(str)); }
}
else
{
Console.WriteLine(Output.Green(str));
}
}
return 0;
}
/// <summary>
/// Executes the SET command to store a value in Redis.
/// Supports optional TTL (time to live) for the key.
/// </summary>
/// <param name="opts">The SET command options</param>
/// <param name="config">The RedisManager configuration</param>
/// <returns>Exit code (0 for success, non-zero for failure)</returns>
public static int RunSet(SetOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
bool ok;
if (opts.TTL.HasValue)
ok = db.StringSet(opts.Key, opts.Value, TimeSpan.FromSeconds(opts.TTL.Value));
else
ok = db.StringSet(opts.Key, opts.Value);
if (ok)
Console.WriteLine(Output.Green("OK"));
else
Console.WriteLine(Output.Red("Failed to set key."));
return 0;
}
/// <summary>
/// Executes the DEL command to delete one or more keys from Redis.
/// Supports confirmation prompts and table output format.
/// </summary>
/// <param name="opts">The DEL command options</param>
/// <param name="config">The RedisManager configuration</param>
/// <returns>Exit code (0 for success, non-zero for failure)</returns>
public static int RunDel(DelOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
if (!opts.Yes)
{
Console.WriteLine(Output.Yellow($"Are you sure you want to delete {keys.Length} key(s)? (y/N)"));
var response = Console.ReadLine()?.ToLower();
if (response != "y" && response != "yes")
{
Console.WriteLine(Output.Red("Operation cancelled."));
return 1;
}
}
var count = db.KeyDelete(keys);
if (opts.Table)
RedisUtils.PrintTable(new[] { "Keys", "Deleted" }, new List<string[]> { new[] { string.Join(", ", opts.Keys), count.ToString() } });
else
Console.WriteLine(Output.Green($"Deleted {count} key(s)"));
return 0;
}
/// <summary>
/// Determines if a string appears to be JSON formatted.
/// Checks if the string starts and ends with curly braces or square brackets.
/// </summary>
/// <param name="str">The string to check for JSON formatting</param>
/// <returns>True if the string appears to be JSON, false otherwise</returns>
private static bool IsJson(string str)
{
str = str.Trim();
return (str.StartsWith("{") && str.EndsWith("}")) || (str.StartsWith("[") && str.EndsWith("]"));
}
}
}

View File

@ -0,0 +1,134 @@
using CommandLine;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using RedisManager.Utils;
namespace RedisManager.Commands
{
/// <summary>
/// Contains command line options and implementations for Redis transaction operations.
/// Provides functionality for MULTI, EXEC, DISCARD, WATCH, and UNWATCH commands.
/// </summary>
[Verb("multi", HelpText = "Mark the start of a transaction block.")]
public class MultiOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
}
[Verb("exec", HelpText = "Execute all commands issued after MULTI.")]
public class ExecOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
}
[Verb("discard", HelpText = "Flush all commands issued after MULTI.")]
public class DiscardOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
}
[Verb("watch", HelpText = "Watch the given keys to determine execution of the MULTI/EXEC block.")]
public class WatchOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "Keys to watch.")]
public IEnumerable<string> Keys { get; set; }
}
[Verb("unwatch", HelpText = "Forget about all watched keys.")]
public class UnwatchOptions
{
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
public string Instance { get; set; }
}
public static class TransactionCommands
{
/// <summary>
/// Executes the MULTI command to mark the start of a transaction block.
/// </summary>
/// <param name="opts">The MultiOptions containing instance.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunMulti(MultiOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.Execute("MULTI");
Console.WriteLine(Output.Green("OK"));
return 0;
}
/// <summary>
/// Executes the EXEC command to execute all commands issued after MULTI.
/// </summary>
/// <param name="opts">The ExecOptions containing instance.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunExec(ExecOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var result = db.Execute("EXEC");
Console.WriteLine(Output.Green(result.ToString()));
return 0;
}
/// <summary>
/// Executes the DISCARD command to flush all commands issued after MULTI.
/// </summary>
/// <param name="opts">The DiscardOptions containing instance.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunDiscard(DiscardOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.Execute("DISCARD");
Console.WriteLine(Output.Green("OK"));
return 0;
}
/// <summary>
/// Executes the WATCH command to watch the given keys for a transaction block.
/// </summary>
/// <param name="opts">The WatchOptions containing instance and keys.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunWatch(WatchOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
db.Execute("WATCH", keys);
Console.WriteLine(Output.Green("OK"));
return 0;
}
/// <summary>
/// Executes the UNWATCH command to forget about all watched keys.
/// </summary>
/// <param name="opts">The UnwatchOptions containing instance.</param>
/// <param name="config">The RedisManager configuration.</param>
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
public static int RunUnwatch(UnwatchOptions opts, Config config)
{
var instance = RedisUtils.GetInstance(config, opts.Instance);
using var redis = RedisUtils.ConnectRedis(instance);
var db = redis.GetDatabase();
db.Execute("UNWATCH");
Console.WriteLine(Output.Green("OK"));
return 0;
}
}
}