diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 5e9f299..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": ".NET Core Launch (console)", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "build", - "program": "${workspaceFolder}/examples/Args/bin/Debug/net8.0/Args.dll", - "args": ["a", "--bar", "a", "a"], - "cwd": "${workspaceFolder}", - "stopAtEntry": false, - "console": "externalTerminal", - } - - - - ] -} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 8fe9072..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "type": "dotnet", - "task": "build", - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": [], - "label": "build" - }, - ] -} \ No newline at end of file diff --git a/Terminal/Arguments/Argument.cs b/Terminal/Arguments/Argument.cs index ff3dc42..90027d0 100644 --- a/Terminal/Arguments/Argument.cs +++ b/Terminal/Arguments/Argument.cs @@ -1,137 +1,67 @@ namespace OxDED.Terminal.Arguments; /// -/// Represents an optional argument (-f, --foo). +/// Represents a required argument in a specific order. /// -public class Argument : ICloneable, IEquatable { +public class Argument : ICloneable { /// - /// The keys of this argument (f, foo). + /// The name (key) of this argument. /// - public string[] keys; + public string name; /// - /// The parameters of this argument. + /// The description of this argument. /// - public ArgumentParameter[] parameters; + public string? description; + internal string? value; /// - /// The description of this argument. + /// If this argument has a value (should be yes). /// - public string? description = null; + public bool HasValue { get => value != null; } /// - /// Creates an argument. + /// The value of this argument (error if it isn't parsed). /// - /// The key of this argument. - /// The description of this argument (optional). - /// The parameters of this argument (default: empty). - public Argument(string key, string? description = null, IEnumerable? parameters = null) { - keys = [key]; - this.description = description; - this.parameters = parameters == null ? [] : [.. parameters]; - } + /// + public string Value { get { + if (HasValue) { + return value!; + } else { + throw new InvalidOperationException("This argument has not yet been parsed"); + } + } } + /// - /// Creates an argument. + /// Creates a argument. /// - /// The keys of this argument. + /// The name of this argument. /// The description of this argument (optional). - /// The parameters of this argument (default: empty). - public Argument(IEnumerable keys, string? description = null, IEnumerable? parameters = null) { - this.keys = [.. keys]; + public Argument(string name, string? description = null) { + this.name = name; this.description = description; - this.parameters = parameters == null ? [] : [.. parameters]; } /// - /// Sets the key of this argument. + /// Sets the name of this argument. /// - /// The new key. + /// The new name of this argument. /// This argument. - public Argument Key(string key) { - keys = [key]; - return this; - } - /// - /// Sets the keys of this argument. - /// - /// The new keys. - /// This argument. - public Argument Keys(IEnumerable keys) { - this.keys = [.. keys]; + public Argument Name(string name) { + this.name = name; return this; } /// /// Sets the description of this argument. /// - /// The new description. + /// The new description of this argument. /// This argument. public Argument Description(string? description) { this.description = description; return this; } - /// - /// Sets the parameters of this argument. - /// - /// The new parameters. - /// This argument. - public Argument Parameters(IEnumerable parameters) { - this.parameters = [.. parameters]; - return this; - } - /// - /// Adds a parameter to this argument. - /// - /// The parameter to add. - /// This argument. - public Argument AddParameter(ArgumentParameter parameter) { - parameters = [.. parameters, parameter]; - return this; - } - /// - /// If this argument's parameters have values (should be yes). - /// - public bool HasValue { get => parameters.All((ArgumentParameter parameter) => parameter.HasValue); } - /// - public static bool operator ==(Argument? left, Argument? right) { - if (left is null && right is null) { - return true; - } else if (left is null) { - return false; - } - return left.Equals(right); - } - /// - public static bool operator !=(Argument? left, Argument? right) { - return !(left == right); - } - /// - /// - /// Checks if the that color is identical to this one. - /// - public bool Equals(Argument? other) { - if (other is null) { - return false; - } - if (ReferenceEquals(this, other)) { - return true; - } - if (GetType() != other.GetType()) { - return false; - } - return keys == other.keys; - } - /// - /// - /// Checks if the that color is identical to this one. - /// - public override bool Equals(object? obj) { - return Equals(obj as Color); - } - /// - public override int GetHashCode() { - return keys.GetHashCode(); - } /// /// /// Calls . /// + public object Clone() { return CloneArgument(); } @@ -142,6 +72,6 @@ public object Clone() { /// The new copy of this color. /// public Argument CloneArgument() { - return new Argument(keys, description, parameters); + return new Argument(name, description); } } \ No newline at end of file diff --git a/Terminal/Arguments/ArgumentFormatter.cs b/Terminal/Arguments/ArgumentFormatter.cs new file mode 100644 index 0000000..b364ed0 --- /dev/null +++ b/Terminal/Arguments/ArgumentFormatter.cs @@ -0,0 +1,174 @@ +namespace OxDED.Terminal.Arguments; + +/// +/// Represents a format for arguments and options. +/// +public partial class ArgumentFormatter { + public string? name; + public string? description; + public string? version; + + /// + /// The options of this format. + /// + public List Options { get; private set; } + /// + /// The arguments of this format. + /// + public List Arguments { get; private set; } + + /// + /// Creates a new argument format. + /// + public ArgumentFormatter(List? arguments = null, List? options = null) { + Options = options ?? []; + Arguments = arguments ?? []; + } + + public OptionFormat Option() { + return new(this); + } + public ArgumentFormat Argument() { + return new(this); + } + + public ArgumentFormatter Name(string name) { + this.name = name; + return this; + } + public ArgumentFormatter Description(string? description) { + this.description = description; + return this; + } + public ArgumentFormatter Version(string? version) { + this.version = version; + return this; + } + + public ArgumentFormatter AddHelpOption(bool quit = true, IEnumerable? keys = null) { + Option() + .Keys(keys ?? ["-h", "--help"]) + .Description("Shows this help message.") + .Finish(); + return this; + } + public ArgumentFormatter AddVersionOption(bool quit = true, IEnumerable? keys = null) { + Option() + .Keys(keys ?? ["-v", "--version"]) + .Description("Shows the version of this application.") + .Finish(); + return this; + } + + // public ArgumentFormatter +} + +public partial class ArgumentFormatter { + public class ArgumentFormat { + public string description; + public string name; + + public ArgumentFormatter ArgumentFormatter { get; private set; } + + public ArgumentFormat(ArgumentFormatter argumentFormatter) { + ArgumentFormatter = argumentFormatter; + } + + public ArgumentFormat Name(string name) { + this.name = name; + return this; + } + public ArgumentFormat Description(string description) { + this.description = description; + return this; + } + + public ArgumentFormatter Finish() { + ArgumentFormatter.Arguments = [.. ArgumentFormatter.Arguments, this]; + return ArgumentFormatter; + } + } +} + +public partial class ArgumentFormatter { + public class OptionFormat { + public string[] keys; + public string description; + public ParameterFormat[] parameters; + + public ArgumentFormatter ArgumentFormatter { get; private set; } + + public OptionFormat(ArgumentFormatter argumentFormatter) { + ArgumentFormatter = argumentFormatter; + } + + public OptionFormat Key(string key) { + keys = [.. keys, key]; + return this; + } + public OptionFormat Keys(IEnumerable keys) { + this.keys = [.. this.keys, .. keys]; + return this; + } + public OptionFormat Description(string description) { + this.description = description; + return this; + } + public ParameterFormat Parameter() { + return new(this); + } + + public ArgumentFormatter Finish() { + ArgumentFormatter.Options = [.. ArgumentFormatter.Options, this]; + return ArgumentFormatter; + } + + public class ParameterFormat { + public string name { get; set; } + public string description { get; set; } + public bool required { get; set; } + + public OptionFormat OptionFormat { get; private set; } + + public ParameterFormat(OptionFormat optionBuilder) { + OptionFormat = optionBuilder; + } + + public ParameterFormat Name(string name) { + this.name = name; + return this; + } + public ParameterFormat Description(string description) { + this.description = description; + return this; + } + public ParameterFormat Required(bool required) { + this.required = required; + return this; + } + + public OptionFormat Finish() { + OptionFormat.parameters = [.. OptionFormat.parameters, this]; + return OptionFormat; + } + } + } +} + +public class HelpOptionFormat : ArgumentFormatter.OptionFormat { + public HelpOptionFormat(ArgumentFormatter argumentFormatter) : base(argumentFormatter) { } + public bool quit; + public HelpOptionFormat Quit(bool quit) { + this.quit = quit; + return this; + } +} + +public class VersionOptionFormat : ArgumentFormatter.OptionFormat { + public VersionOptionFormat(ArgumentFormatter argumentFormatter) : base(argumentFormatter) { } + public bool quit; + public VersionOptionFormat Quit(bool quit) { + this.quit = quit; + return this; + } +} \ No newline at end of file diff --git a/Terminal/Arguments/ArgumentParameter.cs b/Terminal/Arguments/ArgumentParameter.cs deleted file mode 100644 index c23555e..0000000 --- a/Terminal/Arguments/ArgumentParameter.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace OxDED.Terminal.Arguments; - -/// -/// A parameter for an . -/// -public class ArgumentParameter { - /// - /// The name of this argument parameter. - /// - public string name; - /// - /// The description of this argument parameter. - /// - public string? description; - internal string? value; - /// - /// If this argument parameter has a value (should be yes). - /// - public bool HasValue { get => value != null; } - /// - /// The value of this argument parameter (error if it isn't parsed). - /// - /// - public string Value { get { - if (HasValue) { - return value!; - } else { - throw new InvalidOperationException("This argument parameter has not been parsed."); - } - } } - /// - /// Creates an argument parameter. - /// - /// The name of this parameter. - /// The description of this parameter (optional). - public ArgumentParameter(string name, string? description = null) { - this.name = name; - this.description = description; - } - /// - /// Sets the name of this parameter. - /// - /// The new name of this parameter. - /// This parameter. - public ArgumentParameter Name(string name) { - this.name = name; - return this; - } - /// - /// Sets the description of this parameter. - /// - /// The new description of this parameter. - /// This parameter. - public ArgumentParameter Description(string? description) { - this.description = description; - return this; - } -} \ No newline at end of file diff --git a/Terminal/Arguments/ArgumentParser.cs b/Terminal/Arguments/ArgumentParser.cs index a7d1224..b281545 100644 --- a/Terminal/Arguments/ArgumentParser.cs +++ b/Terminal/Arguments/ArgumentParser.cs @@ -1,420 +1,12 @@ namespace OxDED.Terminal.Arguments; -// TODO: add docs - -/// -/// Helps you with parsing arguments. -/// -public class ArgumentParser -{ - /// - /// This is the name of the application. - /// - public string? name = null; - /// - /// The description of the application. - /// - public string? description = null; - private Argument? versionArgument = null; - private Argument? helpArgument = null; - /// - /// The version of the application. - /// - public string? version = null; - /// - /// The arguments of the parser. - /// - public Dictionary arguments = []; - /// - /// The positional arguments of the parser. - /// - public List positionalArguments = []; - /// - /// Sets the help argument of the application parser. - /// - /// - /// - /// - /// - public ArgumentParser Help(IEnumerable? keys = null, bool showDescription = true, bool showVersion = false, bool shouldExit = true) - { - if (helpArgument != null) - { - RemoveArgument(helpArgument); - } - helpArgument = new Argument(keys ?? ["h", "help"], "Shows all the available arguments."); - AddArgument(helpArgument, (Argument arg) => - { - WriteHelp(showDescription, showVersion); - if (shouldExit) - { - Environment.Exit(0); - } - }); - return this; - } - /// - /// Sets the version of the application parser. And adds an argument. - /// - /// The version of the application. - /// The keys for the parameter (default: v, version). - /// - /// - - public ArgumentParser Version(string version, IEnumerable? keys = null, bool shouldExit = true) - { - if (versionArgument != null) - { - RemoveArgument(versionArgument); - } - versionArgument = new Argument(keys ?? ["v", "version"], name == null ? "Shows the version of this application." : $"Shows the version of {name}."); // TODO: add documentation to first assign a name - AddArgument(versionArgument, (Argument arg) => - { - WriteVersion(); - if (shouldExit) - { - Environment.Exit(0); - } - }); - this.version = version; - return this; - } - /// - /// Sets the description of the application parser. - /// - public ArgumentParser Description(string? description) - { - this.description = description; - return this; - } - /// - /// Sets the name of the application parser. - /// - public ArgumentParser Name(string? name) - { - this.name = name; - return this; - } - /// - /// Removes a positional argument. - /// - public ArgumentParser RemovePositionalArgument(int position) { - positionalArguments.RemoveAt(position); - return this; - } - /// - /// Removes a positional argument. - /// - public ArgumentParser RemovePositionalArgument(PositionalArgument argument) { - positionalArguments.Remove(argument); - return this; - } - /// - /// Removes an argument. - /// - public ArgumentParser RemoveArgument(Argument argument) - { - foreach (string key in argument.keys) - { - arguments.Remove(key); - } - return this; - } - /// - /// Adds an argument. - /// - public ArgumentParser AddArgument(Argument argument, ArgumentCallback? callback = null) - { - foreach (string key in argument.keys) - { - arguments.Add(key, argument); - } - if (callback != null) - { - OnArgument += (Argument arg) => - { - if (arg.keys == argument.keys) - { - callback?.Invoke(arg); - } - }; - } - return this; - } - /// - /// Adds a positional argument. - /// - public ArgumentParser AddPositionalArgument(PositionalArgument argument, PositionalArgumentCallback? callback = null) - { - positionalArguments.Add(argument); - if (callback != null) - { - OnPositionalArgument += (PositionalArgument arg) => - { - if (arg.name == argument.name) - { - callback?.Invoke(arg); - } - }; - } - return this; - } - /// - /// Writes the help menu to the terminal. - /// - public void WriteHelp(bool showDescription = true, bool showVersion = false) - { - Terminal.WriteLine(GetHelp(showDescription, showVersion)); - } - /// - /// Writes the version to the terminal. - /// - public void WriteVersion() - { - Terminal.WriteLine(GetVersion()); - } - private static string GetArgumentHelp(PositionalArgument arg, bool isRed = false) - { - return $"{(isRed ? Color.LightRed.ToForegroundANSI() : Color.Orange.ToForegroundANSI())}\u2520{ANSI.Styles.ResetAll} {arg.name}{(arg.description == null ? "" : ": "+arg.description)}\n"; - } - private static string GetArgumentHelpName(string[] keys) - { - string result = ""; - for (int i = 0; i < keys.Length; i++) - { - string key = keys[i]; - result += key.Length == 1 ? "-" : "--"; - result += key; - if (i != keys.Length - 1) - { - result += ", "; - } - } - return result; - } - private static string GetArgumentHelp(Argument arg) - { - string result = $"{Color.DarkGreen.ToForegroundANSI()}\u2520{ANSI.Styles.ResetAll} {GetArgumentHelpName(arg.keys)}{(arg.description == null ? "" : ": "+arg.description)}\n"; - foreach (ArgumentParameter para in arg.parameters) - { - result += $"{Color.DarkGreen.ToForegroundANSI()}\u2503 \u2560{ANSI.Styles.ResetAll} {para.name}{(para.description == null ? "" : ": "+para.description)}\n"; - } - return result; +public class ArgumentParser { + public ArgumentFormatter Register { get; private set; } + public ArgumentParser(ArgumentFormatter register) { + Register = register; } - private string GetHelp(bool showDescription = true, bool showVersion = false, int positionalIndex = -1) - { - string result = $"{ANSI.Styles.Bold}{name}{ANSI.Styles.ResetBold} {((showVersion && version != null) ? version : "")}{((showDescription && description != null) ? '\n' + description : "")}\n\n"; - if (positionalArguments.Count > 0) - { - result += $"{Color.Orange.ToForegroundANSI()}\u250E\u2500\u2500{ANSI.Styles.ResetAll} Required Arguments\n"; - for (int i = 0; i < positionalArguments.Count; i++) - { - PositionalArgument arg = positionalArguments[i]; - if (positionalIndex <= i && positionalIndex != -1) - { - result += GetArgumentHelp(arg, true); - } - else - { - result += GetArgumentHelp(arg, false); - } + public void Parse(string[] args) { - } - result += "\n"; - } - if (arguments.Count > 0 || helpArgument != null || versionArgument != null) - { - result += $"{Color.DarkGreen.ToForegroundANSI()}\u250E\u2500\u2500{ANSI.Styles.ResetAll} Arguments\n"; - if (helpArgument != null) - { - result += $"\u2520 {GetArgumentHelpName(helpArgument.keys)}: {helpArgument.description}\n"; - } - if (versionArgument != null) - { - result += $"\u2520 {GetArgumentHelpName(versionArgument.keys)}: {versionArgument.description}\n"; - } - if (arguments.Count > 0) - { - foreach (Argument argument in arguments.Values.Distinct()) - { - if (argument == versionArgument || argument == helpArgument) - { - continue; - } - result += GetArgumentHelp(argument); - } - } - } - return result; } - private string GetVersion() - { - return $"{ANSI.Styles.Bold}{name}{ANSI.Styles.ResetBold} {version ?? ""}{(description != null ? '\n' + description : "")}"; - } - /// - /// An event that is called when an argument is parsed. - /// - public event ArgumentCallback? OnArgument; - /// - /// An event that is called when a positional argument is parsed. - /// - public event PositionalArgumentCallback? OnPositionalArgument; - /// - /// An event that is called when the format is invalid. - /// - public event InvalidFormatCallback? OnInvalidFormatCallback; - private void WriteInvalid(string message) - { - WriteHelp(false); - OnInvalidFormatCallback?.Invoke(message); - Terminal.WriteLine("\n" + message, new Style { ForegroundColor = Color.Red }); - Environment.Exit(1); - } - private void WriteNoArgument(string message, int positionalIndex) - { - Terminal.WriteLine(GetHelp(false, false, positionalIndex)); - OnInvalidFormatCallback?.Invoke(message); - Terminal.WriteLine("\n" + message, new Style { ForegroundColor = Color.Red }); - Environment.Exit(1); - } - /// - /// Parses the arguments. - /// - public bool Parse(string arguments) - { - return Parse(arguments.Split(' ')); - } - /// - /// Parses the arguments. - /// - public bool Parse(string[] arguments) - { - int positionalArgumentIndex = 0; - Argument? parsingArgument = null; - List parameters = []; - bool isParsingArgument = false; - foreach (string argument in arguments) - { - if (!isParsingArgument) - { - if (argument.StartsWith("--") && argument.Length > 2) - { - parsingArgument = GetArgument(argument[2..]); - if (parsingArgument == null) - { - WriteInvalid("No such argument as: --" + argument[2..] + "."); - return false; - } - else - { - isParsingArgument = true; - } - } - else if (argument.StartsWith('-') && argument.Length > 1) - { - if (argument.Length >= 3) - { - WriteInvalid("Invalid symbol usage (-): " + argument + ".\n Should it be (--)?"); - } - parsingArgument = GetArgument(argument[1].ToString()); - if (parsingArgument == null) - { - WriteInvalid("No such argument as: -" + argument[1] + "."); - return false; - } - else - { - isParsingArgument = true; - } - - } - else - { - PositionalArgument? positionalArgument = GetPositionalArgument(positionalArgumentIndex); - if (positionalArgument != null) - { - positionalArgument.value = argument; - OnPositionalArgument?.Invoke(positionalArgument); - positionalArgumentIndex++; - } - else - { - WriteInvalid("Too much positional arguments."); - return false; - } - } - } - if (isParsingArgument) - { - if (!(parameters.Count - 1 >= parsingArgument!.parameters.Length)) - { - parameters.Add(argument); - } - if (parameters.Count - 1 >= parsingArgument!.parameters.Length) - { - for (int j = 0; j < parsingArgument!.parameters.Length; j++) - { - parsingArgument!.parameters[j].value = parameters[j + 1]; - } - OnArgument?.Invoke(parsingArgument); - isParsingArgument = false; - parameters = []; - parsingArgument = null; - } - } - } - if (isParsingArgument) - { - WriteInvalid("Invalid argument parameters for " + parameters[0] + "."); - return false; - } - if (positionalArguments.Count != positionalArgumentIndex) - { - WriteNoArgument("Not enough positional arguments.", positionalArgumentIndex); - return false; - } - - return true; - } - /// - /// Gets an argument that is registered (null if it isn't registered). - /// - public Argument? GetArgument(string key) - { - if (!arguments.TryGetValue(key, out Argument? value)) - { - return null; - } - return value; - } - /// - /// Gets a positional argument at a location (in order as registered) (if there is one). - /// - public PositionalArgument? GetPositionalArgument(int pos) - { - return positionalArguments.Count > pos ? positionalArguments[pos] : null; - } - /// - /// True if an argument is registered (not used). - /// - public bool HasArgument(string key) - { - return GetArgument(key) != null; - } - /// - /// Gets all the registered arguments (not used). - /// - public Argument[] GetArguments() - { - return [.. arguments.Values.Distinct()]; - } - /// - /// Gets all the registered positional arguments. - /// - public PositionalArgument[] GetPositionalArguments() - { - return [.. positionalArguments]; - } - } \ No newline at end of file diff --git a/Terminal/Arguments/ArgumentParser_old.cs b/Terminal/Arguments/ArgumentParser_old.cs new file mode 100644 index 0000000..e439b39 --- /dev/null +++ b/Terminal/Arguments/ArgumentParser_old.cs @@ -0,0 +1,348 @@ +namespace OxDED.Terminal.Arguments; + +/// +/// Helps you with parsing arguments. +/// +public class ArgumentParserOld { + /// + /// This is the name of the application. + /// + public string? name = null; + /// + /// The description of the application. + /// + public string? description = null; + private Option? versionArgument = null; + private Option? helpArgument = null; + /// + /// The version of the application. + /// + public string? version = null; + /// + /// The arguments of the parser. + /// + public Dictionary options = []; + /// + /// The arguments of the parser. + /// + public List arguments = []; + /// + /// Sets the help argument of the application parser. + /// + /// + /// + /// + /// + public ArgumentParserOld Help(IEnumerable? keys = null, bool showDescription = true, bool showVersion = false, bool shouldExit = true) { + if (helpArgument != null) { + RemoveOption(helpArgument); + } + helpArgument = new Option(keys ?? ["h", "help"], "Shows all the available arguments."); + AddOption(helpArgument, (Option arg) => { + WriteHelp(showDescription, showVersion); + if (shouldExit) { + Environment.Exit(0); + } + }); + return this; + } + /// + /// Sets the version of the application parser. And adds an argument. + /// + /// The version of the application. + /// The keys for the parameter (default: v, version). + /// + /// + + public ArgumentParserOld Version(string version, IEnumerable? keys = null, bool shouldExit = true) { + if (versionArgument != null) { + RemoveOption(versionArgument); + } + versionArgument = new Option(keys ?? ["v", "version"], name == null ? "Shows the version of this application." : $"Shows the version of {name}."); // TODO: add documentation to first assign a name + AddOption(versionArgument, (Option arg) => { + WriteVersion(); + if (shouldExit) { + Environment.Exit(0); + } + }); + this.version = version; + return this; + } + /// + /// Sets the description of the application parser. + /// + public ArgumentParserOld Description(string? description) { + this.description = description; + return this; + } + /// + /// Sets the name of the application parser. + /// + public ArgumentParserOld Name(string? name) { + this.name = name; + return this; + } + /// + /// Removes a argument. + /// + public ArgumentParserOld RemoveArgument(int position) { + arguments.RemoveAt(position); + return this; + } + /// + /// Removes a argument. + /// + public ArgumentParserOld RemoveArgument(Argument argument) { + arguments.Remove(argument); + return this; + } + /// + /// Removes an argument. + /// + public ArgumentParserOld RemoveOption(Option argument) { + foreach (string key in argument.keys) { + options.Remove(key); + } + return this; + } + /// + /// Adds an argument. + /// + public ArgumentParserOld AddOption(Option argument, OptionCallback? callback = null) { + foreach (string key in argument.keys) { + options.Add(key, argument); + } + if (callback != null) { + OnOption += (Option arg) => { + if (arg.keys == argument.keys) { + callback?.Invoke(arg); + } + }; + } + return this; + } + /// + /// Adds a argument. + /// + public ArgumentParserOld AddArgument(Argument argument, ArgumentCallback? callback = null) { + arguments.Add(argument); + if (callback != null) { + OnArgument += (Argument arg) => { + if (arg.name == argument.name) { + callback?.Invoke(arg); + } + }; + } + return this; + } + /// + /// Writes the help menu to the terminal. + /// + public void WriteHelp(bool showDescription = true, bool showVersion = false) { + Terminal.WriteLine(GetHelp(showDescription, showVersion)); + } + /// + /// Writes the version to the terminal. + /// + public void WriteVersion() { + Terminal.WriteLine(GetVersion()); + } + private static string GetArgumentHelp(Argument arg, bool isRed = false) { + return $"{(isRed ? Color.LightRed.ToForegroundANSI() : Color.Orange.ToForegroundANSI())}\u2520{ANSI.Styles.ResetAll} {arg.name}{(arg.description == null ? "" : ": "+arg.description)}\n"; + } + private static string GetArgumentHelpName(string[] keys) { + string result = ""; + for (int i = 0; i < keys.Length; i++) { + string key = keys[i]; + result += key.Length == 1 ? "-" : "--"; + result += key; + if (i != keys.Length - 1) { + result += ", "; + } + } + return result; + } + private static string GetArgumentHelp(Option arg) { + string result = $"{Color.DarkGreen.ToForegroundANSI()}\u2520{ANSI.Styles.ResetAll} {GetArgumentHelpName(arg.keys)}{(arg.description == null ? "" : ": "+arg.description)}\n"; + foreach (OptionParameter para in arg.parameters) { + result += $"{Color.DarkGreen.ToForegroundANSI()}\u2503 \u2560{ANSI.Styles.ResetAll} {para.name}{(para.description == null ? "" : ": "+para.description)}\n"; + } + return result; + } + private string GetHelp(bool showDescription = true, bool showVersion = false, int positionalIndex = -1) { + string result = $"{ANSI.Styles.Bold}{name}{ANSI.Styles.ResetBold} {((showVersion && version != null) ? version : "")}{((showDescription && description != null) ? '\n' + description : "")}\n\n"; + + if (arguments.Count > 0) { + result += $"{Color.Orange.ToForegroundANSI()}\u250E\u2500\u2500{ANSI.Styles.ResetAll} Required Arguments\n"; + for (int i = 0; i < arguments.Count; i++) { + Argument arg = arguments[i]; + if (positionalIndex <= i && positionalIndex != -1) { + result += GetArgumentHelp(arg, true); + } + else { + result += GetArgumentHelp(arg, false); + } + + } + result += "\n"; + } + if (options.Count > 0 || helpArgument != null || versionArgument != null) { + result += $"{Color.DarkGreen.ToForegroundANSI()}\u250E\u2500\u2500{ANSI.Styles.ResetAll} Arguments\n"; + if (helpArgument != null) { + result += $"\u2520 {GetArgumentHelpName(helpArgument.keys)}: {helpArgument.description}\n"; + } + if (versionArgument != null) { + result += $"\u2520 {GetArgumentHelpName(versionArgument.keys)}: {versionArgument.description}\n"; + } + if (options.Count > 0) { + foreach (Option argument in options.Values.Distinct()) { + if (argument == versionArgument || argument == helpArgument) { + continue; + } + result += GetArgumentHelp(argument); + } + } + } + return result; + } + private string GetVersion() { + return $"{ANSI.Styles.Bold}{name}{ANSI.Styles.ResetBold} {version ?? ""}{(description != null ? '\n' + description : "")}"; + } + /// + /// An event that is called when an option is parsed. + /// + public event OptionCallback? OnOption; + /// + /// An event that is called when a argument is parsed. + /// + public event ArgumentCallback? OnArgument; + /// + /// An event that is called when the format is invalid. + /// + public event InvalidFormatCallback? OnInvalidFormatCallback; + private void WriteInvalid(string message) { + WriteHelp(false); + OnInvalidFormatCallback?.Invoke(message); + Terminal.WriteLine("\n" + message, new Style { ForegroundColor = Color.Red }); + Environment.Exit(1); + } + private void WriteNoArgument(string message, int positionalIndex) { + Terminal.WriteLine(GetHelp(false, false, positionalIndex)); + OnInvalidFormatCallback?.Invoke(message); + Terminal.WriteLine("\n" + message, new Style { ForegroundColor = Color.Red }); + Environment.Exit(1); + } + /// + /// Parses the arguments. + /// + public bool Parse(string arguments) { + return Parse(arguments.Split(' ')); + } + /// + /// Parses the arguments. + /// + public bool Parse(string[] arguments) { + int argumentIndex = 0; + Option? parsingOption = null; + List parameters = []; + bool isParsingArgument = false; + foreach (string raw in arguments) { + if (!isParsingArgument) { + if (raw.StartsWith("--") && raw.Length > 2) { + parsingOption = GetArgument(raw[2..]); + if (parsingOption == null) { + WriteInvalid("No such argument as: --" + raw[2..] + "."); + return false; + } + else { + isParsingArgument = true; + } + } + else if (raw.StartsWith('-') && raw.Length > 1) { + if (raw.Length >= 3) { + WriteInvalid("Invalid symbol usage (-): " + raw + ".\n Should it be (--)?"); + } + parsingOption = GetArgument(raw[1].ToString()); + if (parsingOption == null) { + WriteInvalid("No such argument as: -" + raw[1] + "."); + return false; + } + else { + isParsingArgument = true; + } + + } + else { + Argument? argument = GetArgument(argumentIndex); + if (argument != null) { + argument.value = raw; + OnArgument?.Invoke(argument); + argumentIndex++; + } + else { + WriteInvalid("Too much arguments."); + return false; + } + } + } + if (isParsingArgument) { + if (!(parameters.Count - 1 >= parsingOption!.parameters.Length)) { + parameters.Add(raw); + } + if (parameters.Count - 1 >= parsingOption!.parameters.Length) { + for (int j = 0; j < parsingOption!.parameters.Length; j++) { + parsingOption!.parameters[j].value = parameters[j + 1]; + } + OnOption?.Invoke(parsingOption); + isParsingArgument = false; + parameters = []; + parsingOption = null; + } + } + } + if (isParsingArgument) { + WriteInvalid("Invalid option parameters for " + parameters[0] + "."); + return false; + } + if (this.arguments.Count != argumentIndex) { + WriteNoArgument("Not enough arguments.", argumentIndex); + return false; + } + + return true; + } + /// + /// Gets an argument that is registered (null if it isn't registered). + /// + public Option? GetArgument(string key) { + if (!options.TryGetValue(key, out Option? value)) { + return null; + } + return value; + } + /// + /// Gets a argument at a location (in order as registered) (if there is one). + /// + public Argument? GetArgument(int pos) { + return arguments.Count > pos ? arguments[pos] : null; + } + /// + /// True if an argument is registered (not used). + /// + public bool HasArgument(string key) { + return GetArgument(key) != null; + } + /// + /// Gets all the registered arguments (not used). + /// + public Option[] GetOptions() { + return [.. options.Values.Distinct()]; + } + /// + /// Gets all the registered arguments. + /// + public Argument[] GetArguments() { + return [.. arguments]; + } + +} \ No newline at end of file diff --git a/Terminal/Arguments/Delegates.cs b/Terminal/Arguments/Delegates.cs index 58d6c9d..afafe01 100644 --- a/Terminal/Arguments/Delegates.cs +++ b/Terminal/Arguments/Delegates.cs @@ -4,12 +4,12 @@ namespace OxDED.Terminal.Arguments; /// An callback for when an argument has been parsed. /// /// The argument that has been parsed. -public delegate void ArgumentCallback(Argument argument); +public delegate void OptionCallback(Option argument); /// /// An callback for when an postitional argument has been parsed. /// /// The positional argument that has been parsed. -public delegate void PositionalArgumentCallback(PositionalArgument argument); +public delegate void ArgumentCallback(Argument argument); /// /// An callback for when the parsing has failed. /// diff --git a/Terminal/Arguments/Option.cs b/Terminal/Arguments/Option.cs new file mode 100644 index 0000000..07bfcd9 --- /dev/null +++ b/Terminal/Arguments/Option.cs @@ -0,0 +1,157 @@ +namespace OxDED.Terminal.Arguments; + +/// +/// Represents an optional argument (e.g. -f, --foo). +/// +public partial class Option : ICloneable, IEquatable