Created
November 17, 2014 20:11
-
-
Save zaphar/6c62eb593be2579bfe63 to your computer and use it in GitHub Desktop.
C# command line parsing library that doesn't suck.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Heavily inspired by golang's flags library https://golang.org/pkg/flag/ | |
using System; | |
using System.Linq; | |
using System.Collections.Generic; | |
namespace mzhs.flags | |
{ | |
/// <summary> | |
/// The interface a CommandLine Flag must support to be used by the command line parser. | |
/// </summary> | |
public interface FlagValue | |
{ | |
/// <summary> | |
/// Returns a string version of the command line flag's current value and description. | |
/// </summary> | |
string ToString(); | |
/// <summary> | |
/// Set the specified flag from the provided string value. | |
/// </summary> | |
/// <returns> true if the flag was successfully set from the string, false otherwise.</returns> | |
bool Set(string flag); | |
/// <summary> | |
/// This method tells the parser if the method is boolean in it's value. | |
/// It's a convenience to make parsing bool flags that have no value to set easier. | |
/// </summary> | |
bool IsBool { get; } | |
} | |
/// <summary> | |
/// A Typed FlagValue. | |
/// </summary> | |
public interface FlagValue<T> : FlagValue | |
{ | |
/// <summary> | |
/// Gets the current value for this FlagValue. | |
/// </summary> | |
T Value { get; } | |
} | |
/// <summary> | |
/// Flags Command Line Parsing library for C#. | |
/// </summary> | |
public class Flags | |
{ | |
private List<string> _args = new List<string>(); | |
private Dictionary<string, FlagValue> _flags = new Dictionary<string, FlagValue>(); | |
private bool _HasErrors; | |
private string _ErrorText = ""; | |
/// <summary> | |
/// Add your own Custom FlagValue handler for flag parsing. | |
/// </summary> | |
public FlagValue<T> Add<T>(string name, FlagValue<T> val) | |
{ | |
_flags[name] = val; | |
return val; | |
} | |
/// <summary> | |
/// Add an Integer command line flag to be parsed. | |
/// </summary> | |
public FlagValue<int> AddIntFlag(string name, int defaultValue, string description="") | |
{ | |
return Add(name, new Val<int>(defaultValue, | |
(s) => { | |
int i; | |
bool result = int.TryParse(s, out i); | |
return Tuple.Create(result, i); | |
}, description: description)); | |
} | |
/// <summary> | |
/// Adds a boolean command line flag to be parsed. | |
/// </summary> | |
/// <param name="description">Description.</param> | |
public FlagValue<bool> AddBoolFlag(string name, bool defaultValue, string description="") | |
{ | |
return Add(name, new Val<bool>(defaultValue, | |
(s) => { | |
bool b; | |
bool result = bool.TryParse(s, out b); | |
return Tuple.Create(result, b); | |
}, description: description, isBool: true)); | |
} | |
/// <summary> | |
/// Add a string command line flag to be parsed. | |
/// </summary> | |
public FlagValue<string> AddStringFlag(string name, string defaultValue, string description="") | |
{ | |
return Add(name, new Val<string>(defaultValue, | |
(s) => { | |
return Tuple.Create(true, s); | |
}, description: description)); | |
} | |
/// <summary> | |
/// Parse a set of command line args. | |
/// </summary> | |
/// <remarks> | |
/// This method will stop at the first error encountered while parsing flags. | |
/// Check the HasErrors property to see if there was an error while parsing. | |
/// If HasErrors is true then the ErrorText property will contain the error message. | |
/// </remarks> | |
public void Parse(string[] args) | |
{ | |
for (var i = 0; i < args.Length; i++) | |
{ | |
var flag = args[i]; | |
if (flag[0] == '-') | |
{ | |
var start = 1; | |
if (flag[1] == '-') { start = 2; } | |
flag = args[i].Substring(start); | |
} | |
else { | |
_args.Add(flag); | |
continue; | |
} | |
string val = ""; | |
bool hadEquals = false; | |
if (flag.Contains("=")) | |
{ | |
hadEquals = true; | |
var results = flag.Split(new char[] { '=' }, 2); | |
flag = results[0]; | |
val = results[1]; | |
} | |
else if (args.Length > (i+1)) | |
{ | |
val = args[i + 1]; | |
} | |
if (_flags.ContainsKey(flag)) | |
{ | |
var fv = _flags[flag]; | |
if (val != "" && hadEquals) | |
{ | |
fv.Set(val); | |
} | |
else if (fv.IsBool) | |
{ | |
fv.Set(bool.TrueString); | |
} | |
else if (val != "") | |
{ | |
if (!fv.Set(val)) | |
{ | |
_ErrorText = "\"" + val + "\" is not a valid " + flag + "."; | |
_HasErrors = true; | |
} | |
i++; | |
} | |
else | |
{ | |
// This would be a parse error. | |
_ErrorText = flag + " expects an argument."; | |
_HasErrors = true; | |
} | |
} | |
else | |
{ | |
// This is an unparsed arg. | |
_args.Add(args[i]); | |
} | |
if (_HasErrors) | |
{ | |
break; | |
} | |
} | |
} | |
/// <summary> | |
/// Returns the text of the first error encountered while parsing. | |
/// </summary> | |
public string ErrorText | |
{ | |
get { return _ErrorText; } | |
} | |
/// <summary> | |
/// Returns true if the parser encountered an Error while parsing. | |
/// </summary> | |
public bool HasErrors | |
{ | |
get { return _HasErrors; } | |
} | |
/// <summary> | |
/// Returns any left over arguments not parsed by the command line parser. | |
/// </summary> | |
public List<string> Args | |
{ | |
get { return _args; } | |
} | |
public List<string> HelpText | |
{ | |
get | |
{ | |
var help = new List<string>(); | |
foreach (var kv in _flags) | |
{ | |
help.Add("-" + kv.Key + " " + kv.Value.ToString()); | |
} | |
return help; | |
} | |
} | |
} | |
public class Val<T> : FlagValue<T> | |
{ | |
private T _value; | |
private string _description = ""; | |
private Func<string, Tuple<bool,T>> _parseFn; | |
public Val(T defaultValue, Func<string, Tuple<bool, T>> parseFn, string description="", bool isBool=false) | |
{ | |
_value = defaultValue; | |
_description = description; | |
IsBool = isBool; | |
_parseFn = parseFn; | |
} | |
public bool Set(string value) | |
{ | |
var tpl = _parseFn(value); | |
_value = tpl.Item1 ? tpl.Item2 : _value; | |
return tpl.Item1; | |
} | |
public T Value | |
{ | |
get { return _value; } | |
} | |
public bool IsBool | |
{ | |
get; | |
private set; | |
} | |
public override string ToString() | |
{ | |
return "[" + _value + "]" + (_description.IsEmpty() ? "" : " - " + _description); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment