using CommandLine; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Linq; using RedisManager.Utils; namespace RedisManager.Commands { /// /// 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. /// [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 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 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 { /// /// Executes the HGET command to get the value of a field in a hash. /// /// The HGetOptions containing instance, hash, and field. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { 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; } /// /// Executes the HSET command to set the value of a field in a hash. /// /// The HSetOptions containing instance, hash, field, and value. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { 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; } /// /// Executes the HDEL command to delete a field from a hash. /// /// The HDelOptions containing instance, hash, and field. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { 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; } /// /// Executes the HGETALL command to get all fields and values in a hash. /// /// The HGetAllOptions containing instance and hash. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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; } /// /// Executes the HKEYS command to list all fields in a hash. /// /// The HKeysOptions containing instance and hash. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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; } /// /// Executes the HVALS command to list all values in a hash. /// /// The HValsOptions containing instance and hash. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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; } /// /// Executes the HLEN command to get the number of fields in a hash. /// /// The HLenOptions containing instance and hash. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Hash, len.ToString() } }); else Console.WriteLine(Output.Green(len.ToString())); return 0; } /// /// Executes the HEXISTS command to check if a field exists in a hash. /// /// The HExistsOptions containing instance, hash, and field. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Hash, opts.Field, exists ? "Yes" : "No" } }); else Console.WriteLine(Output.Green(exists ? "1" : "0")); return 0; } /// /// Executes the HINCRBY command to increment a field value in a hash by an integer. /// /// The HIncrByOptions containing instance, hash, field, and increment. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Hash, opts.Field, opts.Increment.ToString(), result.ToString() } }); else Console.WriteLine(Output.Green(result.ToString())); return 0; } /// /// Executes the HINCRBYFLOAT command to increment a field value in a hash by a float. /// /// The HIncrByFloatOptions containing instance, hash, field, and increment. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Hash, opts.Field, opts.Increment.ToString(), result.ToString() } }); else Console.WriteLine(Output.Green(result.ToString())); return 0; } /// /// Executes the HMSET command to set multiple fields in a hash. /// /// The HMSetOptions containing instance, hash, and field-value pairs. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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(); 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; } /// /// Executes the HMGET command to get multiple field values from a hash. /// /// The HMGetOptions containing instance, hash, and fields. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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(); 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; } /// /// Executes the HSETNX command to set a field value in a hash only if the field does not exist. /// /// The HSetNxOptions containing instance, hash, field, and value. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Hash, opts.Field, opts.Value, result ? "Set" : "Not set (field exists)" } }); else Console.WriteLine(Output.Green(result ? "1" : "0")); return 0; } /// /// Executes the HSTRLEN command to get the string length of a hash field value. /// /// The HStrLenOptions containing instance, hash, and field. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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 { new[] { opts.Hash, opts.Field, length.ToString() } }); else Console.WriteLine(Output.Green(length.ToString())); return 0; } /// /// Executes the HSCAN command to incrementally iterate hash fields. /// /// The HScanOptions containing instance, hash, and scan options. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). 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; } } }