-
Notifications
You must be signed in to change notification settings - Fork 0
/
CliOptionParser.cs
133 lines (113 loc) · 3.7 KB
/
CliOptionParser.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
namespace TeleScope.UI.Cli.Options
{
/// <summary>
/// This class provides a routine to put application arguments into properties of an object of type T,
/// where matching <seealso cref="CliAttribute"/> are applied to the properties.
/// </summary>
/// <typeparam name="T">The type of the arguments class.</typeparam>
public class CliOptionParser<T> where T : new()
{
// --fields
private readonly T options;
// -- properties
/// <summary>
/// Gets or sets the Prefix that is used to identify options.
/// </summary>
public string Prefix { get; set; }
/// <summary>
/// Gets or sets the format how the strings are parsed into types of the target properties.
/// </summary>
public IFormatProvider Format { get; set; }
// -- constructor
/// <summary>
/// The default constructor creates a new instance of the options and sets the prefix to `-`.
/// The format is set to `InvariantCulture`.
/// </summary>
public CliOptionParser()
{
options = new T();
Prefix = "-";
Format = CultureInfo.InvariantCulture;
}
/// <summary>
/// The constructor calls the default constructor and overrides the <seealso cref="Prefix"/> property.
/// </summary>
/// <param name="prefix">The prefix that is used for identifying application arguments in the string array.</param>
public CliOptionParser(string prefix) : this()
{
Prefix = prefix;
}
/// <summary>
/// The constructor calls the default constructor and overrides the <seealso cref="Format"/> property.
/// </summary>
/// <param name="format">The format specifies how the strings are parsed into types of the target properties.</param>
public CliOptionParser(IFormatProvider format) : this()
{
Format = format;
}
/// <summary>
/// he constructor calls the default constructor and overrides the <seealso cref="Prefix"/> and <seealso cref="Format"/> properties.
/// </summary>
/// <param name="prefix">The prefix that is used for identifying application arguments in the string array.</param>
/// <param name="format">The format specifies how the strings are parsed into types of the target properties.</param>
public CliOptionParser(string prefix, IFormatProvider format) : this()
{
Prefix = prefix;
Format = format;
}
// -- methods
/// <summary>
/// Reads the command line arguments and places them into the properties of the according instance of type T.
/// </summary>
/// <param name="args">The cli options of the main method.</param>
/// <returns>The resulting instance of type T.</returns>
public T ReadArguments(string[] args)
{
for (int i = 0; i < args.Length; i++)
{
var arg = args[i];
if (!arg.StartsWith(Prefix))
{
continue;
}
SetOption(arg, i, args);
}
return options;
}
// -- helper
private void SetOption(string arg, int index, string[] args)
{
arg = Regex.Replace(arg, @"[^0-9a-zA-Z]+", "");
foreach (var prop in options.GetType().GetProperties())
{
var attr = prop.GetCustomAttributes<CliAttribute>().FirstOrDefault();
if (attr is null || IsMismatch(attr, arg))
{
continue;
}
SetProperty(index, args, prop);
}
}
private static bool IsMismatch(CliAttribute attr, string arg)
{
return !attr.Short.Equals(arg) && !attr.Long.Equals(arg);
}
private void SetProperty(int index, string[] args, PropertyInfo prop)
{
if (prop.PropertyType == typeof(bool))
{
prop.SetValue(options, true);
}
else
{
var val = args.Length > index + 1 ? args[index + 1] : string.Empty;
prop.SetValue(options, Convert.ChangeType(val, prop.PropertyType, Format));
}
}
}
}