/*
Two types of CLI arguments:
---------------------------
1. Full arguments
- listed explicitly (eg. "--arg1")
- usually a full word
- can contain spaces represented as "--project-name"
2. Shortened arguments
- listed implicitly version (eg. "-a")
- usually a single letter, interpreted as such
Two types of CLI values:
------------------------
1. String inputs
a) Single character (char)
b) Single word (Word type)
c) Multiple words (Sentence type)
2. Number inputs
a) Single digit (Digit type)
b) Digit string (Number type)
*/
using Builtins;
namespace CommandLine
{
///
/// A struct representing a command-line argument.
///
/// This contains two properties, a name and a list of aliases
/// that can be used instead of the name, and serves as a way
/// to represent a command-line argument in a centralised manner.
///
public struct Argument
{
private readonly string _name;
private readonly string[] _aliases;
public string Name { get { return _name; } }
public string[] Aliases { get { return _aliases; } }
///
/// Create an Argument instance with only a name and no aliases.
///
///
public Argument(string name)
{
_name = name;
_aliases = [];
}
///
/// Create an Argument instance with a name and an array of aliases.
///
/// These aliases will be used when comparing between two arguments
/// and serves as a way to tell if arguments are repeated or not.
///
///
///
public Argument(string name, string[] aliases)
{
_name = name;
_aliases = aliases;
}
///
/// Create an Argument instance with a name and a single alias.
///
/// Thhis alias will be used when comparing between two arguments
/// and serves as a way to tell if arguments are repeated or not.
///
///
///
public Argument(string name, string alias)
{
_name = name;
_aliases = [alias];
}
public override bool Equals(object? obj)
{
if (obj is null) return false;
if (obj is string) return Name == obj.ToString();
Argument other = (Argument)obj;
return Name == other.Name
|| other.Aliases.Contains(Name)
|| Aliases.Contains(other.Name);
}
public static implicit operator string(Argument arg) => arg.Name;
public static implicit operator Argument(string str) => new(str);
public override int GetHashCode() => Name.GetHashCode() ^ Aliases.GetHashCode();
public override string ToString() => $"Argument({_name}{(_aliases.Length > 0 ? $", aliases = [{string.Join(", ", _aliases)}]" : "")})";
}
public abstract class Command
{
private readonly string _name;
private readonly Argument[] _arguments;
private readonly string? _doc;
public string Name { get { return _name; } }
public Argument[] Arguments { get { return _arguments; } }
public string? Docstring { get { return _doc; } }
public Argument[] ReturnedArguments { get; set; }
public static List CreatedCommands { get {
List names = [];
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in asm.GetTypes())
{
if (type.BaseType == typeof(Command))
{
var inst = Activator.CreateInstance(type);
if (inst == null) throw new ArgumentNullException("Null error when searching for commands.");
names.Add((Command)inst);
}
}
}
return names;
} }
public Command(string name)
{
_name = name;
_arguments = [name];
_doc = null;
ReturnedArguments = [];
}
public Command(string name, string docstring)
{
_name = name;
_arguments = [name];
_doc = docstring.Trim().Replace("\n", "").Replace("\n\n", "\n");
ReturnedArguments = [];
}
public Command(string name, Argument[] arguments)
{
if (name == "help") throw new Exception("command names cannot be called 'help'.");
_name = name;
_arguments = [name, .. arguments];
_doc = null;
ReturnedArguments = [];
}
public Command(string name, Argument[] arguments, string docstring)
{
if (name == "help") throw new Exception("command names cannot be called 'help'.");
_name = name;
_arguments = [name, .. arguments];
_doc = docstring.Trim().Replace("\n", "").Replace("\n\n", "\n");
ReturnedArguments = [];
}
public virtual void Callback()
{
throw new NotImplementedException("this method must be overrided in a subclass.");
}
}
public struct CLI
{
public Command[] ValidCommands { get; set; }
public CLI(Command[] validCommands)
{
ValidCommands = validCommands;
}
public Dictionary ArgParser(string[] argsFromCLI, Argument[] validArguments)
{
if (validArguments
.Where(x => x.Name == "help")
.ToList()
.Count == 1
&& validArguments.Length > 1)
{
validArguments = [new Argument("help_args")];
}
List validArgIndexes = Enumerable
.Range(0, argsFromCLI.Length)
.Where(n => validArguments.Contains(argsFromCLI[n]))
.ToList();
validArgIndexes.Add(argsFromCLI.Length);
Dictionary convertedArgs = [];
for (int x = 0; x < validArgIndexes.Count - 1; x++)
{
int firstIndex = validArgIndexes[x];
int secondIndex = validArgIndexes[x + 1];
string first = argsFromCLI[firstIndex];
string other = string.Join(" ", argsFromCLI[(firstIndex + 1) .. secondIndex]);
convertedArgs.Add(first, other);
}
return convertedArgs;
}
public Command? SingleArgParser(string[] argsFromCLI)
{
string[] validArguments = ValidCommands.Select(x => x.Name).ToArray();
List validArgIndexes = Enumerable
.Range(0, argsFromCLI.Length)
.Where(n => validArguments.Contains(argsFromCLI[n]))
.ToList();
if (validArgIndexes.Count != 2) return null;
int argIndex = validArgIndexes[0];
string other = string.Join(' ', argsFromCLI[(argIndex + 1) .. (argsFromCLI.Length - argIndex)]);
Command command = ValidCommands.Where(x => x.Name == argsFromCLI[argIndex]).First();
command.ReturnedArguments = [other];
return command;
}
}
}