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 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 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 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 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 { /// /// Executes the LPUSH command to push elements to the left of a list. /// /// The LPushOptions containing instance, key, and values. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Key, length.ToString() } }); } else Console.WriteLine(Output.Green($"Pushed {values.Length} element(s), new length: {length}")); return 0; } /// /// Executes the RPUSH command to push elements to the right of a list. /// /// The RPushOptions containing instance, key, and values. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Key, length.ToString() } }); } else Console.WriteLine(Output.Green($"Pushed {values.Length} element(s), new length: {length}")); return 0; } /// /// Executes the LPOP command to pop an element from the left of a list. /// /// The LPopOptions containing instance and key. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Key, value.ToString() } }); else Console.WriteLine(Output.Green(value.ToString())); return 0; } /// /// Executes the RPOP command to pop an element from the right of a list. /// /// The RPopOptions containing instance and key. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Key, value.ToString() } }); else Console.WriteLine(Output.Green(value.ToString())); return 0; } /// /// Executes the LRANGE command to get a range of elements from a list. /// /// The LRangeOptions containing instance, key, start, and stop. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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; } /// /// Executes the LLEN command to get the length of a list. /// /// The LLenOptions containing instance and key. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Key, length.ToString() } }); else Console.WriteLine(Output.Green(length.ToString())); return 0; } /// /// Executes the LINDEX command to get the element at a specific index in a list. /// /// The LIndexOptions containing instance, key, and index. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Key, opts.Index.ToString(), value.ToString() } }); else Console.WriteLine(Output.Green(value.ToString())); return 0; } /// /// Executes the LSET command to set the element at a specific index in a list. /// /// The LSetOptions containing instance, key, index, and value. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Key, opts.Index.ToString(), opts.Value, "OK" } }); else Console.WriteLine(Output.Green("OK")); return 0; } /// /// Executes the LREM command to remove elements from a list. /// /// The LRemOptions containing instance, key, count, and value. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Key, opts.Value, count.ToString() } }); else Console.WriteLine(Output.Green($"Removed {count} element(s)")); return 0; } /// /// Executes the LINSERT command to insert an element before or after a pivot in a list. /// /// The LInsertOptions containing instance, key, position, pivot, and value. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Key, opts.Position, opts.Pivot, opts.Value, result.ToString() } }); else Console.WriteLine(Output.Green($"New length: {result}")); return 0; } /// /// Executes the LTRIM command to trim a list to a specified range. /// /// The LTrimOptions containing instance, key, start, and stop. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Key, opts.Start.ToString(), opts.Stop.ToString(), "OK" } }); else Console.WriteLine(Output.Green("OK")); return 0; } /// /// Executes the BLPOP command to perform a blocking pop from the left of a list. /// /// The BLPopOptions containing instance, keys, and timeout. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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().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 { new[] { key ?? "", value ?? "" } }); } else { Console.WriteLine(Output.Green($"{key}: {value}")); } } else { Console.WriteLine(Output.Yellow("Unexpected result format.")); } return 0; } /// /// Executes the BRPOP command to perform a blocking pop from the right of a list. /// /// The BRPopOptions containing instance, keys, and timeout. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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().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 { new[] { key ?? "", value ?? "" } }); } else { Console.WriteLine(Output.Green($"{key}: {value}")); } } else { Console.WriteLine(Output.Yellow("Unexpected result format.")); } return 0; } /// /// Executes the RPOPLPUSH command to pop from the right of the source and push to the left of the destination list. /// /// The RPopLPushOptions containing instance, source, and destination. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Source, opts.Destination, result.ToString() } }); else Console.WriteLine(Output.Green(result.ToString())); return 0; } } }