Last active
June 1, 2016 18:52
-
-
Save 71/3a36b0dbd8e2c7eeb6c5 to your computer and use it in GitHub Desktop.
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
/* | |
______ _ _ _ _____ _ _ ___________ ___ ______ _____ ___________ | |
| ___| | | | | | ___| \ | |_ _| ___ \/ _ \ | ___ \/ ___| ___| ___ \ | |
| |_ | | | | | | |__ | \| | | | | |_/ / /_\ \| |_/ /\ `--.| |__ | |_/ / | |
| _| | | | | | | __|| . ` | | | | __/| _ || / `--. \ __|| / | |
| | | |___| |_| | |___| |\ | | | | | | | | || |\ \ /\__/ / |___| |\ \ | |
\_| \_____/\___/\____/\_| \_/ \_/ \_| \_| |_/\_| \_|\____/\____/\_| \_| | |
A single file to parse arguments easily. | |
Parses IEnumerable<T> and T, with T : string / int / bool | |
UNLICENSED: <http://unlicense.org/UNLICENSE> | |
*/ | |
/* | |
Usage: | |
Options argv = new FluentParser<Options>(Environment.GetCommandLineArgs()) | |
.Define(x => x.Password).Long("password", "pass").Short('p').Required().Done() | |
.Define(x => x.Username).Long("username", "user").Short('u').Required().Done() | |
.Define(x => x.Email).Long("email", "mail").Short('@', 'm').Default("[email protected]").Done() | |
.Item; | |
Console.Write(argv.Password); | |
*/ | |
namespace Jee | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
public class FluentParser<T> where T : new() | |
{ | |
internal List<FPB> parsers; | |
internal Dictionary<string, string> values; | |
public string[] Args { get; private set; } | |
public string Help { get; set; } | |
private T item; | |
public T Item | |
{ | |
get | |
{ | |
parse(); | |
return item; | |
} | |
} | |
public FluentParser(string[] args) | |
{ | |
item = new T(); | |
Args = args; | |
parsers = new List<FPB>(); | |
values = new Dictionary<string, string>(); | |
} | |
#region Parse | |
private bool isPropertyKey(string arg) | |
{ | |
return (arg.StartsWith("--") && arg.Length > 2) || (arg.StartsWith("-") && arg.Length == 2); | |
} | |
private void parse() | |
{ | |
string main = ""; | |
for (int i = 0; i < Args.Length; i++) | |
{ | |
string arg = Args[i]; | |
if (isPropertyKey(arg)) // long & short name | |
{ | |
foreach (FPB fpb in parsers.Where(x => (arg.Length == 2 && x.canParse(arg[1])) || x.canParse(arg.Substring(2)))) | |
{ | |
// parse values depending on the type accepted by fpb | |
if (fpb.parses == typeof(string)) | |
{ | |
string s = ""; | |
while (Args.Length > i + 1 && !isPropertyKey(Args[++i])) | |
s += Args[i]; | |
fpb.set(s); | |
break; | |
} | |
else if (fpb.parses == typeof(int)) | |
{ | |
int val; | |
if (Args.Length > i + 1 && int.TryParse(Args[++i], out val)) | |
{ | |
fpb.set(val); | |
break; | |
} | |
else | |
{ | |
continue; | |
} | |
} | |
else if (fpb.parses == typeof(bool)) | |
{ | |
fpb.set(true); | |
break; | |
} | |
else if (fpb.parses == typeof(string[])) | |
{ | |
List<string> s = new List<string>(); | |
while (Args.Length > i + 1 && !isPropertyKey(Args[++i])) | |
s.Add(Args[i]); | |
fpb.set(s.ToArray()); | |
break; | |
} | |
else if (fpb.parses == typeof(int[])) | |
{ | |
List<int> s = new List<int>(); | |
bool success = true; | |
while (Args.Length > i + 1 && !isPropertyKey(Args[++i])) | |
{ | |
int val; | |
if (int.TryParse(Args[i], out val)) | |
{ | |
s.Add(val); | |
} | |
else | |
{ | |
success = false; | |
break; | |
} | |
} | |
if (success) | |
{ | |
fpb.set(s.ToArray()); | |
break; | |
} | |
else | |
{ | |
continue; | |
} | |
} | |
else | |
{ | |
throw new NotSupportedException("Cast to " + fpb.parses.Name + " not supported."); | |
} | |
} | |
} | |
else | |
{ | |
main += Args[i] + ' '; | |
} | |
} | |
if (main != "") | |
{ | |
foreach (FPB fpb in parsers.Where(x => x.canParseMain())) | |
{ | |
// parse values depending on the type accepted by fpb | |
if (fpb.parses == typeof(string)) | |
{ | |
fpb.set(main); | |
break; | |
} | |
else | |
{ | |
throw new NotSupportedException("Cast to " + fpb.parses.Name + " not supported."); | |
} | |
} | |
} | |
} | |
#endregion | |
internal void Set(MemberExpression ex, object value) | |
{ | |
if (ex.Member is FieldInfo) | |
(ex.Member as FieldInfo).SetValue(item, value); | |
else if (ex.Member is PropertyInfo) | |
(ex.Member as PropertyInfo).SetValue(item, value, null); | |
else | |
throw new ArgumentException(); | |
} | |
public FluentParserBuilder<TReturn> Define<TReturn>(Expression<Func<T, TReturn>> expr) | |
{ | |
return new FluentParserBuilder<TReturn>(this, parsers.Count, expr.Body as MemberExpression); | |
} | |
#region Builder abstract class | |
public abstract class FPB | |
{ | |
internal abstract Type parses { get; set; } | |
internal abstract bool canParseMain(); | |
internal abstract bool canParse(string name); | |
internal abstract bool canParse(char name); | |
internal abstract void set(object value); | |
internal abstract void setDefault(); | |
} | |
#endregion | |
#region Builder | |
public class FluentParserBuilder<TReturn> : FPB | |
{ | |
private FluentParser<T> _fp; | |
private int _id; | |
private MemberExpression _ex; | |
private string[] longs; | |
private char[] shorts; | |
private string val = null; | |
private TReturn def = default(TReturn); | |
private bool defSet = false; | |
private bool requir = false; | |
private bool main = false; | |
internal override Type parses { get; set; } | |
internal FluentParserBuilder(FluentParser<T> fp, int id, MemberExpression ex) | |
{ | |
parses = typeof(TReturn); | |
_fp = fp; | |
_id = id; | |
_ex = ex; | |
} | |
internal override bool canParse(string name) | |
{ | |
return longs.Contains(name); | |
} | |
internal override bool canParse(char name) | |
{ | |
return shorts.Contains(name); | |
} | |
internal override void set(object value) | |
{ | |
_fp.Set(_ex, value); | |
} | |
internal override void setDefault() | |
{ | |
if (defSet) | |
_fp.Set(_ex, def); | |
else if (!requir) | |
_fp.Set(_ex, default(TReturn)); | |
else | |
throw new MissingFieldException(); | |
} | |
internal override bool canParseMain() | |
{ | |
return main; | |
} | |
private FluentParserBuilder<TReturn> update() | |
{ | |
if (_fp.parsers.Count > _id) | |
_fp.parsers[_id] = this; | |
else | |
_fp.parsers.Add(this); | |
return this; | |
} | |
public FluentParserBuilder<TReturn> Long(params string[] names) | |
{ | |
longs = names; | |
return update(); | |
} | |
public FluentParserBuilder<TReturn> Short(params char[] names) | |
{ | |
shorts = names; | |
return update(); | |
} | |
public FluentParserBuilder<TReturn> Default(TReturn value) | |
{ | |
def = value; | |
defSet = true; | |
return update(); | |
} | |
public FluentParserBuilder<TReturn> Required() | |
{ | |
requir = true; | |
return update(); | |
} | |
public FluentParserBuilder<TReturn> Main() | |
{ | |
main = true; | |
return update(); | |
} | |
public FluentParser<T> Done() | |
{ | |
return _fp; | |
} | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment