namespace HanaToolbox.Cli; /// /// Parsed result from the CLI argument array. /// Lightweight, AOT-safe, zero external dependencies. /// public sealed class CliArgs { public string Command { get; init; } = string.Empty; // e.g. "backup", "cron" public string SubCommand { get; init; } = string.Empty; // e.g. "setup" for "cron setup" public List Positional { get; init; } = []; // non-flag arguments public bool Verbose { get; init; } public string Sid { get; init; } = string.Empty; public bool Compress { get; init; } public int Threads { get; init; } public bool Replace { get; init; } /// /// Parses raw args into a CliArgs instance. /// /// Global options: -v | --verbose /// --sid <SID> /// /// Command-local options are also parsed here for simplicity (they are /// silently ignored when not applicable to the active command). /// /// Usage examples: /// hanatoolbox backup --verbose --sid NDB /// hanatoolbox export MYSCHEMA /hana/backup -c -t 4 /// hanatoolbox import-rename SRC DST /path --replace /// hanatoolbox cron setup /// public static CliArgs Parse(string[] args) { var positional = new List(); var verbose = false; var sid = string.Empty; var compress = false; var threads = 0; var replace = false; var command = string.Empty; var subCommand = string.Empty; int i = 0; // First token that is not a flag is the command // second non-flag token after that may be the subcommand (for "cron setup") while (i < args.Length) { var a = args[i]; switch (a) { case "-v" or "--verbose": verbose = true; i++; break; case "--sid" when i + 1 < args.Length: sid = args[++i]; i++; break; case "-c" or "--compress": compress = true; i++; break; case "--replace": replace = true; i++; break; case "-t" or "--threads" when i + 1 < args.Length: int.TryParse(args[++i], out threads); i++; break; default: if (!a.StartsWith('-')) { if (string.IsNullOrEmpty(command)) command = a; else if (string.IsNullOrEmpty(subCommand) && positional.Count == 0) // Peek: is this a known subcommand for the current command? subCommand = MaybeSubCommand(command, a) ? a : AddPositional(positional, a); else positional.Add(a); } // Unknown flag — silently skip i++; break; } } return new CliArgs { Command = command, SubCommand = subCommand, Positional = positional, Verbose = verbose, Sid = sid, Compress = compress, Threads = threads, Replace = replace, }; } // ── Helpers ─────────────────────────────────────────────────────────────── private static bool MaybeSubCommand(string command, string token) => command == "cron" && token == "setup"; private static string AddPositional(List list, string value) { list.Add(value); return string.Empty; } /// Gets positional arg at index, or empty string if not present. public string Pos(int index) => index < Positional.Count ? Positional[index] : string.Empty; }