Skip to content

Instantly share code, notes, and snippets.

@Larry57
Created April 17, 2013 07:00
Show Gist options
  • Select an option

  • Save Larry57/5402309 to your computer and use it in GitHub Desktop.

Select an option

Save Larry57/5402309 to your computer and use it in GitHub Desktop.
/* This package contains a powerful, declarative command-line parsing system in a single .cs file.
* You can include this in any project with almost zero footprint and very easy/readable usage,
* as shown below. More switch types, including generic ones are coming soon. Visit the project
* page for a sample of how to use this handy package. Part of Code Blocks (http://codeblocks.codeplex.com)
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.IO;
using System.Reflection;
static class CommandLine
{
private interface IValType
{
Type GetValType { get; }
}
public abstract class SwitchBase
{
protected SwitchBase()
{
}
public string Name
{
get;
protected set;
}
public string ShortForm
{
get;
protected set;
}
public string Description { get; set; }
public object DefaultValue { get; set; }
public bool Optional { get; protected set; }
public abstract int InvokeHandler(IEnumerable<object> values);
}
public abstract class SwitchBase<T> : SwitchBase, IValType // Class that encapsulates switch data.
{
protected SwitchBase(string name, Action<IEnumerable<T>> handler, string shortForm)
{
Name = name;
Handler = handler;
ShortForm = shortForm;
}
protected SwitchBase(string name, Action<IEnumerable<T>> handler)
{
Name = name;
Handler = handler;
ShortForm = null;
}
public Action<IEnumerable<T>> Handler
{
get;
private set;
}
public override int InvokeHandler(IEnumerable<object> values)
{
Handler(values.Cast<T>());
return Optional ? 0 : 1;
}
new public T DefaultValue
{
get { return (T)base.DefaultValue; }
set { base.DefaultValue = value; }
}
Type IValType.GetValType
{
get { return typeof(T); }
}
}
public sealed class Switch : SwitchBase<string>
{
public Switch(string name, Action<IEnumerable<string>> handler, string shortForm)
: base(name, handler, shortForm)
{
}
public Switch(string name, Action<IEnumerable<string>> handler)
: base(name, handler)
{
}
new public bool Optional
{
get { return base.Optional; }
set { base.Optional = value; }
}
}
public sealed class OptionalSwitch : SwitchBase<string>
{
public OptionalSwitch(string name, Action<IEnumerable<string>> handler, string shortForm)
: base(name, handler, shortForm)
{
base.Optional = true;
}
public OptionalSwitch(string name, Action<IEnumerable<string>> handler)
: base(name, handler)
{
base.Optional = true;
}
new bool Optional { get { return true; } }
}
public sealed class MandatorySwitch : SwitchBase<string>
{
public MandatorySwitch(string name, Action<IEnumerable<string>> handler, string shortForm)
: base(name, handler, shortForm)
{
base.Optional = false;
}
public MandatorySwitch(string name, Action<IEnumerable<string>> handler)
: base(name, handler)
{
base.Optional = false;
}
new bool Optional { get { return false; } }
}
public sealed class Switch<T> : SwitchBase<T>
{
public Switch(string name, Action<IEnumerable<T>> handler, string shortForm)
: base(name, handler, shortForm)
{
}
public Switch(string name, Action<IEnumerable<T>> handler)
: base(name, handler)
{
}
}
public sealed class OptionalSwitch<T> : SwitchBase<T>
{
public OptionalSwitch(string name, Action<IEnumerable<T>> handler, string shortForm)
: base(name, handler, shortForm)
{
base.Optional = true;
}
public OptionalSwitch(string name, Action<IEnumerable<T>> handler)
: base(name, handler)
{
base.Optional = true;
}
new bool Optional { get { return true; } }
}
public sealed class MandatorySwitch<T> : SwitchBase<T>
{
public MandatorySwitch(string name, Action<IEnumerable<T>> handler, string shortForm)
: base(name, handler, shortForm)
{
base.Optional = false;
}
public MandatorySwitch(string name, Action<IEnumerable<T>> handler)
: base(name, handler)
{
base.Optional = false;
}
new bool Optional { get { return false; } }
}
/* The regex that extracts names and comma-separated values for switches
in the form (<switch>[="value 1",value2,...])+ */
private static readonly Regex ArgRegex =
new Regex(@"(?<name>[^=]+)=?((?<quoted>\""?)(?<value>(?(quoted)[^\""]+|[^,]+))\""?,?)*",
RegexOptions.Compiled | RegexOptions.CultureInvariant |
RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase);
private const string NameGroup = "name"; // Names of capture groups
private const string ValueGroup = "value";
private static string BuildUsageString(this string[] args, params SwitchBase[] switches)
{
var builder = new StringBuilder();
builder.AppendFormat("Usage is {0} ", Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().CodeBase));
var argUsages = from s in switches
select string.Format("{0}=<{1} {2}>", s.ShortForm == null ? s.Name : s.Name + "|" + s.ShortForm,
s.Description ?? string.Empty,
s.DefaultValue == null ? string.Empty : "(" + s.DefaultValue + ")");
builder.Append(string.Join(" ", argUsages));
return builder.ToString();
}
public static bool Process(this string[] args, Action<string> printUsage, params SwitchBase[] switches)
{
return Process(args, () => printUsage(BuildUsageString(args, switches)), switches);
}
public static bool Process(this string[] args, Action printUsage, params SwitchBase[] switches)
{
/* Run through all matches in the argument list and if any of the switches
match, get the values and invoke the handler we were given. We do a Sum()
here for 2 reasons; a) To actually run the handlers
and b) see if any were invoked at all (each returns 1 if invoked).
If none were invoked, we simply invoke the printUsage handler. */
if ((from arg in args
from Match match in ArgRegex.Matches(arg)
from s in switches
let destType = (s as IValType).GetValType
where match.Success &&
((string.Compare(match.Groups[NameGroup].Value, s.Name, true) == 0) ||
(string.Compare(match.Groups[NameGroup].Value, s.ShortForm, true) == 0))
select s.InvokeHandler(
from val in match.Groups[ValueGroup].Value.Split(',')
let typeConverter = TypeDescriptor.GetConverter(destType)
where ((typeConverter != null) && (typeConverter.CanConvertFrom(val.GetType())))
select typeConverter.ConvertFrom(val)
)).Sum() < (from s in switches select s.Optional ? 0 : 1).Sum())
{
printUsage(); // We didn't find some mandatory switches
return false;
}
else
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment