Last active
December 20, 2015 07:17
-
-
Save garrydzeng/7155a5dc298c9f0416a5 to your computer and use it in GitHub Desktop.
CommandOption is a callback-based program option parser.
This file contains 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
using System; | |
using System.Collections.Generic; | |
using System.Collections.ObjectModel; | |
using System.ComponentModel; | |
using System.Reflection; | |
using System.Runtime.InteropServices; | |
using System.Runtime.Serialization; | |
using System.Security; | |
using System.Text.RegularExpressions; | |
using System.Linq; | |
[assembly: ComVisible(false)] | |
[assembly: AssemblyVersion("1.0.0")] | |
[assembly: AssemblyTitle("CommandOption is a callback-based program option parser.")] | |
[assembly: AssemblyProduct("CommandOption")] | |
namespace CommandOption { | |
// Option prototype, defined as interface... | |
public interface IOption { | |
bool IsMark { get; } | |
string Description { get; } | |
string Name { get; } | |
bool IsRequired { get; } | |
bool IsHelp { get; } | |
void Process(string argument); | |
} | |
[Serializable] | |
public class OptionException : Exception { | |
public string OptionName { | |
protected set; | |
get; | |
} | |
public OptionException() { } | |
public OptionException(string message, string optionName) | |
: base(message) { | |
OptionName = optionName; | |
} | |
public OptionException(string message, string optionName, Exception innerException) | |
: base(message, innerException) { | |
OptionName = optionName; | |
} | |
protected OptionException(SerializationInfo info, StreamingContext context) | |
: base(info, context) { | |
OptionName = info.GetString("OptionName"); | |
} | |
[SecurityCritical] | |
public override void GetObjectData(SerializationInfo info, StreamingContext context) { | |
base.GetObjectData(info, context); | |
info.AddValue("OptionName", OptionName); | |
} | |
} | |
public class OptionSet : List<IOption> { | |
public OptionSet() { } | |
public OptionSet(IEnumerable<IOption> collection) : base(collection) { } | |
public OptionSet(int capacity) : base(capacity) { } | |
// get help... | |
public virtual string Help { | |
get { | |
throw new NotImplementedException(); | |
} | |
} | |
public void Add<T>(string name, Action<T> callback, bool isRequired = true, bool isMark = false, bool isHelp = false) { | |
Add(new CommonOption<T>( | |
Name: name, | |
Callback: callback, | |
IsMark: isMark, | |
IsRequired: isRequired, | |
IsHelp: isHelp | |
)); | |
} | |
public void Add<T>(string name, string description, Action<T> callback, bool isRequired = true, bool isMark = false, bool isHelp = false) { | |
Add(new CommonOption<T>( | |
Name: name, | |
Description: description, | |
Callback: callback, | |
IsMark: isMark, | |
IsRequired: isRequired, | |
IsHelp: isHelp | |
)); | |
} | |
public void Add(string name, Action<string> callback, bool isRequired = true, bool isMark = false, bool isHelp = false) { | |
Add(new CommonOption( | |
Name: name, | |
Callback: callback, | |
IsMark: isMark, | |
IsRequired: isRequired, | |
IsHelp: isHelp | |
)); | |
} | |
public void Add(string name, string description, Action<string> callback, bool isRequired = true, bool isMark = false, bool isHelp = false) { | |
Add(new CommonOption( | |
Name: name, | |
Description: description, | |
Callback: callback, | |
IsMark: isMark, | |
IsRequired: isRequired, | |
IsHelp: isHelp | |
)); | |
} | |
static readonly Regex pattern = new Regex("^(--|-|/)([^:=]+)([:=](.*))?$"); | |
string filter(string value) { | |
return string.IsNullOrEmpty(value) ? null : value; | |
} | |
IDictionary<string, string> ParseArguments(string[] arguments) { | |
Match match; | |
Dictionary<string, string> parsed = new Dictionary<string, string>(); | |
GroupCollection group; | |
// Ignore invalid... | |
foreach (string argument in arguments) { | |
match = pattern.Match(argument); | |
if (match.Success) { | |
group = match.Groups; | |
parsed[group[2].Value] = filter(group[4].Value); | |
} | |
} | |
return parsed; | |
} | |
public virtual void Parse(string[] arguments) { | |
IDictionary<string, string> dictionary = ParseArguments(arguments); | |
IEnumerable<IOption> options = this | |
.Where(n => n.IsHelp); | |
// Try to find help argument. | |
// if we found, | |
// process they only. | |
if (!options.Any(n => dictionary.ContainsKey(n.Name))) { | |
options = this; | |
} | |
bool exists; | |
string value = null; | |
foreach (IOption option in options) { | |
exists = dictionary.ContainsKey(option.Name); | |
// check key... | |
if (option.IsRequired && !exists) { | |
throw new OptionException( | |
string.Format("Missing required '{0}' option.", option.Name), | |
option.Name | |
); | |
} | |
// try get value... | |
value = exists ? | |
dictionary[option.Name] : | |
null; | |
if (option.IsRequired && value == null) { | |
throw new OptionException( | |
string.Format("Missing required value for option '{0}'.", option.Name), | |
option.Name | |
); | |
} | |
if (option.IsMark && value != null) { | |
throw new OptionException( | |
string.Format("Flag '{0}' can't has value.", option.Name), | |
option.Name | |
); | |
} | |
// call the Action when: | |
// 1. current option is non-help option. | |
// 2. current option passed. | |
if (!option.IsHelp || exists) { | |
option.Process(value); | |
} | |
} | |
} | |
} | |
public class CommonOption<T> : IOption { | |
public bool IsMark { get; protected set; } | |
public string Description { get; protected set; } | |
public string Name { get; protected set; } | |
public bool IsRequired { get; protected set; } | |
public bool IsHelp { get; protected set; } | |
protected Action<T> Callback; | |
public CommonOption(string Name, Action<T> Callback, string Description = null, bool IsRequired = true, bool IsMark = false, bool IsHelp = false) { | |
if (Name == null || Name.Length == 0) { | |
throw new ArgumentException( | |
"Option name can't be null or empty.", | |
"Name" | |
); | |
} | |
if (Callback == null) { | |
throw new ArgumentNullException("Callback"); | |
} | |
this.Callback = Callback; | |
this.Name = Name; | |
this.Description = Description; | |
this.IsMark = IsMark; | |
this.IsRequired = IsRequired; | |
this.IsHelp = IsHelp; | |
} | |
protected T Parse(string argument) { | |
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T)); | |
try { | |
return argument != null ? | |
(T)converter.ConvertFromString(argument) : | |
default(T); | |
} | |
catch (Exception exception) { | |
string format = "Could not convert string '{0}' to type {1} for option '{2}'."; | |
throw new OptionException( | |
string.Format(format, argument, typeof(T).Name, Name), | |
Name, | |
exception | |
); | |
} | |
} | |
public void Process(string argument) { | |
Callback(Parse(argument)); | |
} | |
} | |
public class CommonOption : IOption { | |
public bool IsMark { get; protected set; } | |
public string Description { get; protected set; } | |
public string Name { get; protected set; } | |
public bool IsRequired { get; protected set; } | |
public bool IsHelp { get; protected set; } | |
protected Action<string> Callback; | |
public CommonOption(string Name, Action<string> Callback, string Description = null, bool IsRequired = true, bool IsMark = false, bool IsHelp = false) { | |
if (Name == null || Name.Length == 0) { | |
throw new ArgumentException( | |
"Option name can't be null or empty.", | |
"Name" | |
); | |
} | |
if (Callback == null) { | |
throw new ArgumentNullException("Callback"); | |
} | |
this.Callback = Callback; | |
this.Name = Name; | |
this.Description = Description; | |
this.IsMark = IsMark; | |
this.IsRequired = IsRequired; | |
this.IsHelp = IsHelp; | |
} | |
public void Process(string argument) { | |
Callback(argument); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment