Skip to content

Instantly share code, notes, and snippets.

@ZacharyPatten
Last active December 6, 2019 18:45
Show Gist options
  • Select an option

  • Save ZacharyPatten/ee71dff90054c8729dc7b93e326dc4da to your computer and use it in GitHub Desktop.

Select an option

Save ZacharyPatten/ee71dff90054c8729dc7b93e326dc4da to your computer and use it in GitHub Desktop.
A tool that helps you handle command line arguments.
using System;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using Towel;
using static Towel.Syntax;
namespace ConsoleCoreSandbox
{
class Program
{
// This code is a snippet from the https://github.com/ZacharyPatten/Towel project.
// Please check out the project if you want to see more code like it. :)
// This is some code that aims to help you handle command line arguments to Console
// applications. Just add "CommandLine.Argument" fields to the class of your entry
// point and they will be automatically resolved.
//
// The "HasValue" property returns true if the argument was
// provided or a default value exists.
//
// The "Status" property gives you information about the argument.
// - Default: The arguemnt was not provided but a default value exists.
// - SyntaxError: The definition of the CommandLineArgument is invalid (not supported). The code needs to be updated.
// - NotProvided: The argument was not provided by the command line arguments and no default exists.
// - DuplicateProvided: The argument was provided multiple times. The command line arguments are invalid.
// - ParseFailed: The argument was provided, but the relative value was not valid.
// - ValueProvided: The argument was provided and successfully parsed.
//
// Examples Of Command Lines:
// dotnet THISAPP A: helloworld
// dotnet THISAPP B: 3
// dotnet THISAPP A: hello B: 4 C: 11.5 D: world
// dotnet THISAPP Help
// dotnet THISAPP Version
static CommandLine.Argument Version;
static CommandLine.Argument Help;
static CommandLine.Argument<string> A = "default";
static CommandLine.Argument<int> B = 7;
static CommandLine.Argument<float> C = 8.5f;
static CommandLine.Argument<string> D;
static void Main()
{
if (Version.Exists || Help.Exists)
{
Console.WriteLine(CommandLine.GetDefaultInfoString);
Console.WriteLine(" Summary: TODO");
Console.WriteLine(" Documentation: TODO");
Console.WriteLine(" Contact(s): TODO");
return;
}
Console.WriteLine(A);
Console.WriteLine(B + 1);
Console.WriteLine(C + 1.5);
Console.WriteLine(D.HasValue ? D.Value : nameof(D) + ": " + D.Status);
}
}
}
#region Towel Definitions
namespace Towel
{
public static class CommandLine
{
internal static string[] Args = Environment.GetCommandLineArgs();
public static string GetDefaultInfoString
{
get
{
StringBuilder stringBuilder = new StringBuilder();
Assembly entryAssembly = Assembly.GetEntryAssembly();
AssemblyName assemblyName = entryAssembly.GetName();
stringBuilder.Append(" Name: ");
stringBuilder.AppendLine(assemblyName.Name);
stringBuilder.Append(" Version: ");
stringBuilder.AppendLine(assemblyName.Version.ToString());
stringBuilder.Append(" Command Line Arguments:");
MethodInfo entryMethod = entryAssembly.EntryPoint;
Type entryType = entryMethod.DeclaringType;
FieldInfo[] fieldInfos = entryType.GetFields(
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic);
bool hasCommandLineArguments = false;
for (int i = 0; i < fieldInfos.Length; i++)
{
FieldInfo field = fieldInfos[i];
object fieldValue = field.GetValue(null);
if (fieldValue is IGenericArgument genericArgument)
{
hasCommandLineArguments = true;
stringBuilder.AppendLine();
stringBuilder.Append(" ");
stringBuilder.Append(field.Name);
stringBuilder.AppendLine(":");
stringBuilder.Append(" Type: ");
stringBuilder.Append(genericArgument.Type.Name);
if (genericArgument.HasDefaultValue)
{
stringBuilder.AppendLine();
stringBuilder.Append(" Default: ");
stringBuilder.Append(genericArgument.DefaultValueString);
}
}
else if (fieldValue is Argument)
{
hasCommandLineArguments = true;
stringBuilder.AppendLine();
stringBuilder.Append(" ");
stringBuilder.Append(field.Name);
}
}
if (!hasCommandLineArguments)
{
stringBuilder.Append(" None");
}
string result = stringBuilder.ToString();
return result;
}
}
public enum ArgumentStatus
{
Null = 0,
Default,
SyntaxError,
NotProvided,
DuplicateProvided,
ParseFailed,
ValueProvided,
}
public struct Argument
{
internal class Data
{
internal ArgumentStatus _status;
}
internal Data _data;
public bool Exists
{
get
{
Process();
return _data._status is ArgumentStatus.ValueProvided;
}
}
public ArgumentStatus Status
{
get
{
Process();
return _data._status;
}
}
internal void Process()
{
_data ??= new Data();
if (!(_data._status is ArgumentStatus.Null))
{
return;
}
Assembly entryAssembly = Assembly.GetEntryAssembly();
MethodInfo entryMethod = entryAssembly.EntryPoint;
Type entryType = entryMethod.DeclaringType;
FieldInfo[] fieldInfos = entryType.GetFields(
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic);
foreach (FieldInfo field in fieldInfos)
{
if (field.GetValue(null) is Argument argumentT)
{
if (argumentT._data is null || !(argumentT._data._status is ArgumentStatus.Null))
{
continue;
}
if (argumentT._data == _data)
{
string name = field.Name;
int index = -1;
for (int i = 0; i < Args.Length; i++)
{
if (Args[i] == name)
{
if (index > -1)
{
_data._status = ArgumentStatus.DuplicateProvided;
return;
}
else
{
index = i;
}
}
}
if (index == -1)
{
_data._status = ArgumentStatus.NotProvided;
return;
}
else
{
_data._status = ArgumentStatus.ValueProvided;
return;
}
}
}
}
_data._status = ArgumentStatus.SyntaxError;
return;
}
}
internal interface IGenericArgument
{
bool HasDefaultValue { get; }
string DefaultValueString { get; }
Type Type { get; }
}
public struct Argument<T> : IGenericArgument
{
internal class Data
{
internal ArgumentStatus _status;
internal T _value;
internal T _defaultValue;
internal bool _hasDefault;
}
internal Data _data;
public T Value
{
get
{
Process();
return _data._status is ArgumentStatus.ValueProvided || _data._status is ArgumentStatus.Default
? _data._value
: throw new InvalidOperationException("Attempted to get a command line argument with a status of " + _data._status + ".");
}
}
public bool HasDefaultValue
{
get
{
Process();
return _data._hasDefault;
}
}
public T DefaultValue
{
get
{
Process();
return _data._hasDefault
? _data._defaultValue
: throw new InvalidOperationException("Attempted to get the default value of a command line argument with no default value.");
}
}
public string DefaultValueString
{
get
{
Process();
return _data._hasDefault
? _data._defaultValue.ToString()
: throw new InvalidOperationException("Attempted to get the default value string of a command line argument with no default value.");
}
}
public bool HasValue
{
get
{
Process();
return _data._status is ArgumentStatus.ValueProvided || _data._status is ArgumentStatus.Default;
}
}
public ArgumentStatus Status
{
get
{
Process();
return _data._status;
}
}
public Type Type => typeof(T);
internal void Process()
{
_data ??= new Data();
if (!(_data._status is ArgumentStatus.Null))
{
return;
}
Assembly entryAssembly = Assembly.GetEntryAssembly();
MethodInfo entryMethod = entryAssembly.EntryPoint;
Type entryType = entryMethod.DeclaringType;
FieldInfo[] fieldInfos = entryType.GetFields(
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic);
foreach (FieldInfo field in fieldInfos)
{
if (field.GetValue(null) is Argument<T> argumentT)
{
if (argumentT._data is null || !(argumentT._data._status is ArgumentStatus.Null))
{
continue;
}
if (argumentT._data == _data)
{
string name = field.Name + ":";
int index = -1;
for (int i = 0; i < Args.Length; i++)
{
if (Args[i] == name)
{
if (index > -1)
{
_data._status = ArgumentStatus.DuplicateProvided;
return;
}
else
{
index = i;
}
}
}
if (index == -1)
{
if (_data._hasDefault)
{
_data._status = ArgumentStatus.Default;
return;
}
else
{
_data._status = ArgumentStatus.NotProvided;
return;
}
}
else if (index == Args.Length - 1)
{
_data._status = ArgumentStatus.NotProvided;
return;
}
else if (typeof(T) == typeof(string))
{
Argument<string>.Data data_string = _data as Argument<string>.Data;
data_string._value = Args[index + 1];
_data._status = ArgumentStatus.ValueProvided;
return;
}
else if (TryParse(Args[index + 1], out _data._value))
{
_data._status = ArgumentStatus.ValueProvided;
return;
}
else
{
_data._status = ArgumentStatus.ParseFailed;
return;
}
}
}
}
_data._status = ArgumentStatus.SyntaxError;
return;
}
public static implicit operator T(Argument<T> argument) => argument.Value;
public static implicit operator Argument<T>(T value) =>
new Argument<T>()
{
_data = new Data()
{
_defaultValue = value,
_hasDefault = true,
_value = value,
}
};
}
}
public static class Meta
{
public static MethodInfo GetTryParseMethod<A>() => GetTryParseMethodCache<A>.Value;
public static MethodInfo GetTryParseMethod(Type a)
{
_ = a ?? throw new ArgumentNullException(nameof(a));
MethodInfo methodInfo = a.GetMethod("TryParse",
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic,
null,
new Type[] { typeof(string), a.MakeByRefType() },
null);
return !(methodInfo is null)
&& methodInfo.ReturnType == typeof(bool)
? methodInfo
: null;
}
internal static class GetTryParseMethodCache<A>
{
internal static readonly MethodInfo Value = GetTryParseMethod(typeof(A));
}
}
public static class Syntax
{
public static A TryParse<A>(string @string, A Default = default) =>
TryParse(@string, out A value)
? value
: Default;
public static bool TryParse<A>(string @string, out A value) =>
TryParseImplementation<A>.Function(@string, out value);
internal static class TryParseImplementation<A>
{
internal static TryParse<A> Function = (string @string, out A value) =>
{
static bool Default(string @string, out A value)
{
value = default;
return false;
}
MethodInfo methodInfo = Meta.GetTryParseMethod<A>();
Function = methodInfo is null
? Default
: (TryParse<A>)methodInfo.CreateDelegate(typeof(TryParse<A>));
return Function(@string, out value);
};
}
}
public delegate bool TryParse<T>(string @string, out T value);
}
#endregion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment