├── .gitignore ├── .vs └── ConsoleCommon │ └── v15 │ └── .suo ├── ConsoleCommon.Tests ├── CheckForHelpTests.cs ├── ConsoleCommon.Tests.csproj ├── CustomParsingTests.cs ├── DefaultTypeParsingTests.cs ├── DynamicTypeCreatorTests.cs ├── DynamicTypeParsing │ ├── DynamicTypeCreator.cs │ ├── DynamicTypeCreatorBaseAddMethods.cs │ ├── DynamicTypeCreatorBaseAddProperty.cs │ ├── IEmptyObject.cs │ ├── OtherInterfaces.cs │ └── PropertyBuilding.cs ├── FullExampleTest.cs ├── Helpers │ ├── DynamicParamsCreator.cs │ └── ParamsObjectTestHelpers.cs ├── InlineHelpTextAndDefaultSwitchNameTest.cs ├── ParamsObjectCreatorTests.cs ├── Properties │ └── AssemblyInfo.cs ├── TestCommandInputText.cs └── packages.config ├── ConsoleCommon.sln ├── ConsoleCommon ├── ConsoleCommon.csproj ├── Entities │ ├── InputParameterType.cs │ ├── SwitchOptions.cs │ └── SwitchParameterEntity.cs ├── HelpText │ ├── BasicHelpTextParser.cs │ ├── HelpTextAttribute.cs │ ├── IHelpTextOptions.cs │ ├── IHelpTextParser.cs │ └── SwitchHelpTextAttribute.cs ├── Helpers │ ├── CommandLineHelpers.cs │ ├── ReflectionExtensionMethods.cs │ └── TypesByInheritanceLevelComparer.cs ├── ParamsObject.cs ├── Parsing │ ├── ISwitchParser.cs │ ├── ITypeParseContainer.cs │ ├── PrimitiveParser.cs │ ├── StringSwitchParser.cs │ ├── SwitchParser.cs │ ├── TypeParserContainerBase.cs │ └── TypeParsers │ │ ├── Concrete │ │ ├── ArrayParser.cs │ │ ├── BoolParser.cs │ │ ├── DefaultTypeContainer.cs │ │ ├── EnumParser.cs │ │ ├── KeyValueParser.cs │ │ ├── NullableParser.cs │ │ ├── ObjectParser.cs │ │ ├── SecureStringParser.cs │ │ ├── TypeParserContainer.cs │ │ └── TypeTypeParser.cs │ │ └── Interfaces │ │ ├── ITypeParser.cs │ │ └── TypeParserBase.cs ├── Properties │ └── AssemblyInfo.cs ├── SwitchAttribute.cs ├── TypeParamAttribute.cs ├── bin │ └── Release │ │ └── ConsoleCommon.dll ├── obj │ ├── Debug │ │ ├── ConsoleCommon.csproj.FileListAbsolute.txt │ │ ├── ConsoleCommon.exe │ │ ├── ConsoleCommon.pdb │ │ └── DesignTimeResolveAssemblyReferencesInput.cache │ └── Release │ │ ├── ConsoleCommon.csproj.FileListAbsolute.txt │ │ └── ConsoleCommon.dll └── packages.config ├── NugetInstructions.txt └── readme.txt /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This .gitignore file was automatically created by Microsoft(R) Visual Studio. 3 | ################################################################################ 4 | 5 | /ConsoleCommon/bin/Debug 6 | /.vs/ConsoleCommon/v14 7 | /ConsoleCommon/bin/Release/root 8 | /ConsoleCommon/bin/Release/root/lib/net45 9 | /ConsoleCommon/bin/Release/root 10 | /ConsoleCommon/bin/Release/ConsoleCommon.dll.2.0.0.nupkg 11 | /ConsoleCommon/bin/Release/ConsoleCommon.dll.zip 12 | /ConsoleCommon/bin/Release/ConsoleCommon.exe 13 | /ConsoleCommon/bin/Release/ConsoleCommon.pdb 14 | *.cache 15 | *.exe 16 | *.pdb 17 | /ConsoleCommon/ConsoleCommon.nuspec 18 | *.nupkg 19 | *.zip 20 | /packages/NuGet.CommandLine.4.5.1 21 | /ConsoleCommon.source.zip 22 | /manifest.nuspec 23 | /packages/NuGet.CommandLine.4.5.1 24 | /packages/NuGet.CommandLine.4.5.1 25 | /manifest.nuspec 26 | *.dll 27 | /.vs/ConsoleCommon/v15/Server/sqlite3/storage.ide-wal 28 | /.vs/ConsoleCommon/v15/Server/sqlite3/storage.ide-shm 29 | /.vs/ConsoleCommon/v15/Server/sqlite3/storage.ide 30 | /.vs/ConsoleCommon/v15/Server/sqlite3/db.lock 31 | *.dtbcache 32 | /ConsoleCommon.Tests/bin/Debug 33 | /ConsoleCommon.Tests/obj/Debug 34 | /packages/NUnit.3.11.0 35 | /ConsoleCommon.Tests/obj/Release 36 | -------------------------------------------------------------------------------- /.vs/ConsoleCommon/v15/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylax/ConsoleCommon/c4cc3c52bef3d7910c9924f6b9e7579b31b6bf66/.vs/ConsoleCommon/v15/.suo -------------------------------------------------------------------------------- /ConsoleCommon.Tests/CheckForHelpTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | 8 | namespace ConsoleCommon.Tests 9 | { 10 | [TestFixture] 11 | public class CheckForHelpTests 12 | { 13 | [Test] 14 | public void TestCStorArgs() 15 | { 16 | ParamsObject _input = null; 17 | Exception _ex = null; 18 | bool _created = true; 19 | try 20 | { 21 | _input = DynamicParamsCreator 22 | .Create() 23 | .AddSwitch("FirstName") 24 | .FinishBuilding("/?"); 25 | } 26 | catch(Exception ex) 27 | { 28 | _created = false; 29 | _ex = ex; 30 | } 31 | Assert.IsTrue(_created, $"Failed to parse switches. Ex: {handleEx(_ex)}"); 32 | string _helpText = _input.GetHelpIfNeeded(); 33 | Assert.IsTrue(!string.IsNullOrWhiteSpace(_helpText), "Help not retrieved"); 34 | } 35 | [Test] 36 | public void TestCStorCommandText() 37 | { 38 | ParamsObject _input = null; 39 | bool _created = true; 40 | Exception _ex = null; 41 | try 42 | { 43 | _input = DynamicParamsCreator 44 | .Create() 45 | .AddSwitch("FirstName") 46 | .FinishBuilding(new StringBuilder("/?")); 47 | } 48 | catch(Exception ex) 49 | { 50 | _ex = ex; 51 | _created = false; 52 | } 53 | Assert.IsTrue(_created, $"Failed to parse switches. Ex: {handleEx(_ex)}"); 54 | string _helpText = _input.GetHelpIfNeeded(); 55 | Assert.IsTrue(!string.IsNullOrWhiteSpace(_helpText), "Help not retrieved"); 56 | } 57 | private string handleEx(Exception ex) 58 | { 59 | int _i = 1; 60 | Exception _innerEx = ex; 61 | string _msg = ""; 62 | while(_innerEx!=null) 63 | { 64 | _msg += $"{Environment.NewLine}Ex {_i}: {_innerEx.Message}"; 65 | _innerEx = _innerEx.InnerException; 66 | _i++; 67 | } 68 | return _msg; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/ConsoleCommon.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {401A0951-A83D-419D-855A-AD7E13A7D65C} 9 | Library 10 | Properties 11 | ConsoleCommon.Tests 12 | ConsoleCommon.Tests 13 | v4.6.1 14 | 512 15 | 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | ..\packages\NUnit.3.11.0\lib\net45\nunit.framework.dll 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 | {74f5c3d6-192b-4e59-92b6-b351e286dd80} 73 | ConsoleCommon 74 | 75 | 76 | 77 | 78 | 79 | 80 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/CustomParsingTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | using ConsoleCommon; 8 | using ConsoleCommon.Parsing; 9 | using ConsoleCommon.Parsing.TypeParsers; 10 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 11 | using System.Globalization; 12 | using ConsoleCommon.Tests.Helpers; 13 | 14 | namespace ConsoleCommon.Tests 15 | { 16 | [TestFixture] 17 | public class CustomParsingTests 18 | { 19 | public class DayFirstDashOnlyDateTimeParser : TypeParserBase 20 | { 21 | public override object Parse(string toParse, Type typeToParse, ITypeParserContainer parserContainer) 22 | { 23 | CultureInfo _culture = CultureInfo.CreateSpecificCulture(""); 24 | return DateTime.ParseExact(toParse, "dd-MM-yyyy", _culture); 25 | } 26 | } 27 | [Test] 28 | public void Test_Override_DateTimeParsing_Bad() 29 | { 30 | 31 | ParamsObject _paramObj = 32 | DynamicParamsCreator 33 | .Create() 34 | .AddSwitch("Bday") 35 | .AddSwitch("Age") 36 | .AddSwitch("Name") 37 | .FinishBuilding("/Bday:28/11/1987", "/Age:30", "/Name:Yisrael Lax"); 38 | 39 | ParamsObjectTestHelpers.AssertCheckParams(_paramObj, 40 | "Parsing should have failed b/c incorrect DateTime format", 41 | true, 42 | "string was not recognized as a valid datetime"); 43 | Assert.IsTrue(_paramObj.GetPropertyValue("Bday") == default(DateTime)); 44 | Assert.IsTrue(_paramObj.GetPropertyValue("Age") == 30); 45 | Assert.IsTrue(_paramObj.GetPropertyValue("Name") == "Yisrael Lax"); 46 | } 47 | [Test] 48 | public void Test_Override_DateTimeParsing_Good() 49 | { 50 | ParamsObject _paramObj = 51 | DynamicParamsCreator 52 | .Create() 53 | .AddSwitch("Bday") 54 | .AddSwitch("Age") 55 | .AddSwitch("Name") 56 | .OverrideTypeParsers(() => new TypeParserContainer(true, new DefaultTypeContainer(), new DayFirstDashOnlyDateTimeParser())) 57 | .FinishBuilding("/Bday:28-11-1987", "/Age:30", "/Name:Yisrael Lax"); 58 | 59 | ParamsObjectTestHelpers.AssertCheckParams(_paramObj); 60 | Assert.IsTrue(_paramObj.GetPropertyValue("Bday") == new DateTime(1987, 11, 28)); 61 | Assert.IsTrue(_paramObj.GetPropertyValue("Age") == 30); 62 | Assert.IsTrue(_paramObj.GetPropertyValue("Name") == "Yisrael Lax"); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/DefaultTypeParsingTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | using ConsoleCommon; 8 | using ConsoleCommon.Parsing; 9 | using ConsoleCommon.Parsing.TypeParsers; 10 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 11 | using System.Reflection; 12 | using System.Security; 13 | using ConsoleCommon.Tests.Helpers; 14 | 15 | namespace ConsoleCommon.Tests 16 | { 17 | [TestFixture] 18 | public class DefaultTypeParsingTests 19 | { 20 | [TypeParam("Employee")] 21 | public class EmployeePersonType { } 22 | [TypeParam("Customer")] 23 | public class CustomerPersonType { } 24 | [TypeParam("Boss")] 25 | public class BossPersonType { } 26 | public enum MyColors 27 | { 28 | Orange, 29 | Blue, 30 | Yellow 31 | } 32 | [Flags] 33 | public enum MyPets 34 | { 35 | None = 0, 36 | Dog = 2, 37 | Cat = 4, 38 | Turtle = 8 39 | } 40 | #region FullParsing 41 | [Test] 42 | public void TestAllDefaultParsing_Is_AllValuesSet() 43 | { 44 | ParamsObject _paramObj = 45 | DynamicParamsCreator 46 | .Create() 47 | .AddSwitch("Nums") 48 | .AddSwitch("IsItTrue") 49 | .AddSwitch>("NameAge") 50 | .AddSwitch("Color") 51 | .AddSwitch("Height") 52 | .AddSwitch("pw") 53 | .FinishBuilding("/Nums:one,two,three", "/IsItTrue:true", 54 | "/NameAge:yiz:30", "/Color:Blue", "/Height:65", "/pw:passw0rd"); 55 | 56 | bool _parseErr = false; 57 | try 58 | { 59 | _paramObj.CheckParams(); 60 | } 61 | catch { _parseErr = true; } 62 | Assert.IsFalse(_parseErr, "Check params failed"); 63 | Assert.IsTrue(_paramObj.GetPropertyValue("Nums")[1] == "two"); 64 | Assert.IsTrue(_paramObj.GetPropertyValue("IsItTrue") == true); 65 | Assert.IsTrue(_paramObj.GetPropertyValue>("NameAge").Value == 30); 66 | Assert.IsTrue(_paramObj.GetPropertyValue("Color") == MyColors.Blue); 67 | Assert.IsTrue(_paramObj.GetPropertyValue("Height") == 65); 68 | Assert.IsTrue(_paramObj.GetPropertyValue("pw").Length == 8); 69 | } 70 | [Test] 71 | public void TestAllDefaultParsing_Not_AllValuesSet() 72 | { 73 | ParamsObject _paramObj = 74 | DynamicParamsCreator 75 | .Create() 76 | .AddSwitch("Nums") 77 | .AddSwitch("IsItTrue") 78 | .AddSwitch>("NameAge") 79 | .AddSwitch("Color") 80 | .AddSwitch("Height") 81 | .AddSwitch("pw") 82 | .FinishBuilding("/IsItTrue:true", 83 | "/NameAge:yiz:30", "/Color:Blue", "/pw:passw0rd"); 84 | 85 | bool _parseErr = false; 86 | try 87 | { 88 | _paramObj.CheckParams(); 89 | } 90 | catch { _parseErr = true; } 91 | Assert.IsFalse(_parseErr, "Check params failed"); 92 | Assert.IsTrue(_paramObj.GetPropertyValue("Nums") == null); 93 | Assert.IsTrue(_paramObj.GetPropertyValue("IsItTrue") == true); 94 | Assert.IsTrue(_paramObj.GetPropertyValue>("NameAge").Value == 30); 95 | Assert.IsTrue(_paramObj.GetPropertyValue("Color") == MyColors.Blue); 96 | Assert.IsTrue(_paramObj.GetPropertyValue("Height") == 0); 97 | Assert.IsTrue(_paramObj.GetPropertyValue("pw").Length == 8); 98 | } 99 | #endregion 100 | 101 | #region KeyValue, string, int Parsing 102 | [Test] 103 | public void TestKeyValueOnlyParsing_Bad() 104 | { 105 | ParamsObject _paramObj = 106 | DynamicParamsCreator 107 | .Create() 108 | .OverrideTypeParsers(() => new TypeParserContainer(false, new KeyValueParser())) 109 | .AddSwitch>("NameAge") 110 | .AddSwitch("FirstName") 111 | .FinishBuilding("/NameAge:Yizzy:30"); 112 | 113 | TypeParserContainer _container = _paramObj.GetPropertyValue("TypeParser"); 114 | Assert.IsTrue(_container.TypeParsers.Count() == 1, "More than 1 type parser should not have existed"); 115 | 116 | bool _parseErr = false; 117 | try 118 | { 119 | _paramObj.CheckParams(); 120 | } 121 | catch { _parseErr = true; } 122 | Assert.IsTrue(_parseErr, "String and int parsers were not explicated, so this should have failed"); 123 | KeyValuePair _namgeAge = 124 | _paramObj.GetPropertyValue>("NameAge"); 125 | Assert.IsNull(_namgeAge.Key, "Name key should have been null"); 126 | Assert.IsTrue(_namgeAge.Value == 0, "Age value should have been null"); 127 | } 128 | [Test] 129 | public void TestKeyValueOnlyParsing_Good() 130 | { 131 | ParamsObject _paramObj = 132 | DynamicParamsCreator 133 | .Create() 134 | .OverrideTypeParsers(() => new TypeParserContainer(false, new KeyValueParser(), new ObjectParser())) 135 | .AddSwitch>("NameAge") 136 | .AddSwitch("FirstName") 137 | .FinishBuilding("/NameAge:Yizzy:30"); 138 | 139 | TypeParserContainer _container = _paramObj.GetPropertyValue("TypeParser"); 140 | Assert.IsTrue(_container.TypeParsers.Count() == 2, "Only 2 type parsers should have existed"); 141 | 142 | bool _parseErr = false; 143 | try 144 | { 145 | _paramObj.CheckParams(); 146 | } 147 | catch { _parseErr = true; } 148 | Assert.IsFalse(_parseErr, "String and int parsers were explicated, so this should NOT have failed"); 149 | 150 | KeyValuePair? _namgeAge = _paramObj.GetPropertyValue>("NameAge"); 151 | Assert.IsNotNull(_namgeAge); 152 | Assert.IsTrue(_namgeAge.Value.Key == "Yizzy"); 153 | Assert.IsTrue(_namgeAge.Value.Value == 30); 154 | } 155 | #endregion 156 | 157 | #region DateTime parsing (ObjectParser) 158 | [Test] 159 | public void Test_DateTimeParsing_Bad() 160 | { 161 | ParamsObject _paramObj = 162 | DynamicParamsCreator 163 | .Create() 164 | .AddSwitch("Bday") 165 | .AddSwitch("Age") 166 | .AddSwitch("Name") 167 | .FinishBuilding("/Bday:28/11/1987", "/Age:30", "/Name:Yisrael Lax"); 168 | 169 | bool _parseErr = false; 170 | try 171 | { 172 | _paramObj.CheckParams(); 173 | } 174 | catch { _parseErr = true; } 175 | Assert.IsTrue(_parseErr, "Parsing should have failed b/c incorrect DateTime format"); 176 | Assert.IsTrue(_paramObj.GetPropertyValue("Bday") == default(DateTime)); 177 | } 178 | [Test] 179 | public void Test_DateTimeParsing_Good() 180 | { 181 | ParamsObject _paramObj = 182 | DynamicParamsCreator 183 | .Create() 184 | .AddSwitch("Bday") 185 | .AddSwitch("Age") 186 | .AddSwitch("Name") 187 | .FinishBuilding("/Bday:11/28/1987", "/Age:30", "/Name:Yisrael Lax"); 188 | 189 | bool _parseErr = false; 190 | try 191 | { 192 | _paramObj.CheckParams(); 193 | } 194 | catch { _parseErr = true; } 195 | Assert.IsFalse(_parseErr, "Parsing failed"); 196 | DateTime _bday = _paramObj.GetPropertyValue("Bday"); 197 | Assert.IsTrue(_bday == new DateTime(1987, 11, 28), "Bday is incorrect"); 198 | } 199 | #endregion 200 | 201 | #region Type Parsing 202 | [Test] 203 | public void Test_TypeTypeParser_FriendName_Good() 204 | { 205 | ParamsObject _paramObj = DynamicParamsCreator 206 | .Create() 207 | .AddSwitch("PersonType") 208 | .FinishBuilding("/PersonType:Employee"); 209 | 210 | ParamsObjectTestHelpers.AssertCheckParams(_paramObj, "PersonType parsing failed"); 211 | Assert.IsTrue(_paramObj.GetPropertyValue("PersonType") == typeof(EmployeePersonType)); 212 | } 213 | [Test] 214 | public void Test_TypeTypeParser_FriendName_Bad() 215 | { 216 | ParamsObject _paramObj = DynamicParamsCreator 217 | .Create() 218 | .AddSwitch("PersonType") 219 | .FinishBuilding("/PersonType:NotReal"); 220 | 221 | Assert.IsTrue(_paramObj.GetPropertyValue("PersonType") == null); 222 | } 223 | [Test] 224 | public void Test_TypeTypeParser_ClassName_Good() 225 | { 226 | ParamsObject _paramObj = DynamicParamsCreator 227 | .Create() 228 | .AddSwitch("PersonType") 229 | .FinishBuilding("/PersonType:EmployeePersonType"); 230 | 231 | ParamsObjectTestHelpers.AssertCheckParams(_paramObj, "PersonType parsing failed"); 232 | Assert.IsTrue(_paramObj.GetPropertyValue("PersonType") == typeof(EmployeePersonType)); 233 | } 234 | [Test] 235 | public void Test_TypeTypeParser_SwitchValueList_FriendName_Good() 236 | { 237 | ParamsObject _paramObj = DynamicParamsCreator 238 | .Create() 239 | .AddSwitch("PersonType", "PersonType", true, -1, "Employee", "Customer") 240 | .FinishBuilding("/PersonType:Employee"); 241 | 242 | ParamsObjectTestHelpers.AssertCheckParams(_paramObj, "PersonType parsing failed"); 243 | Assert.IsTrue(_paramObj.GetPropertyValue("PersonType") == typeof(EmployeePersonType)); 244 | } 245 | [Test] 246 | public void Test_TypeTypeParser_SwitchValueList_FriendName_Bad() 247 | { 248 | ParamsObject _paramObj = DynamicParamsCreator 249 | .Create() 250 | .AddSwitch("PersonType", "PersonType", true, -1, "Employee", "Customer") 251 | .FinishBuilding("/PersonType:Boss"); 252 | 253 | ParamsObjectTestHelpers.AssertCheckParams(_paramObj, "PersonType parsing failed", true); 254 | } 255 | [Test] 256 | public void Test_TypeTypeParser_SwitchValueList_ClassName_Good() 257 | { 258 | ParamsObject _paramObj = DynamicParamsCreator 259 | .Create() 260 | .AddSwitch("PersonType", "PersonType", true, -1, "Employee", "Customer") 261 | .FinishBuilding("/PersonType:EmployeePersonType"); 262 | 263 | ParamsObjectTestHelpers.AssertCheckParams(_paramObj, "PersonType parsing failed"); 264 | Assert.IsTrue(_paramObj.GetPropertyValue("PersonType") == typeof(EmployeePersonType)); 265 | } 266 | #endregion 267 | 268 | #region Enum Parsing 269 | [Test] 270 | public void Test_BasicEnumParsing_Good() 271 | { 272 | ParamsObject _paramObj = DynamicParamsCreator 273 | .Create() 274 | .AddSwitch("Color") 275 | .FinishBuilding("/Color:Yellow"); 276 | 277 | ParamsObjectTestHelpers.AssertCheckParams(_paramObj); 278 | Assert.IsTrue(_paramObj.GetPropertyValue("Color") == MyColors.Yellow); 279 | } 280 | 281 | [Test] 282 | public void Test_FlagsEnumParsing_Good() 283 | { 284 | ParamsObject _paramObj = DynamicParamsCreator 285 | .Create() 286 | .AddSwitch("Pets") 287 | .FinishBuilding("/Pets:Dog,Turtle"); 288 | 289 | ParamsObjectTestHelpers.AssertCheckParams(_paramObj); 290 | Assert.IsTrue(_paramObj.GetPropertyValue("Pets") == (MyPets.Dog | MyPets.Turtle)); 291 | } 292 | #endregion 293 | 294 | #region Arrays 295 | [Test] 296 | public void TestKeyValueArray_Good() 297 | { 298 | ParamsObject _paramObj = DynamicParamsCreator 299 | .Create() 300 | .AddSwitch[]>("NameAges") 301 | .FinishBuilding("/NameAges:Yisrael:30,Srully:10,Yitschak:40"); 302 | 303 | ParamsObjectTestHelpers.AssertCheckParams(_paramObj); 304 | KeyValuePair[] _nameAges = _paramObj.GetPropertyValue[]>("NameAges"); 305 | Assert.IsNotNull(_nameAges); 306 | Assert.IsTrue(_nameAges.Length == 3); 307 | Assert.IsTrue(_nameAges[1].Key == "Srully"); 308 | Assert.IsTrue(_nameAges[1].Value == 10); 309 | } 310 | [Test] 311 | public void TestEnumArray_Good() 312 | { 313 | ParamsObject _paramObj = DynamicParamsCreator 314 | .Create() 315 | .AddSwitch("Colors") 316 | .FinishBuilding("/Colors:Orange,Yellow"); 317 | 318 | ParamsObjectTestHelpers.AssertCheckParams(_paramObj); 319 | MyColors[] _colors = _paramObj.GetPropertyValue("Colors"); 320 | Assert.IsNotNull(_colors); 321 | Assert.IsTrue(_colors.Length == 2); 322 | Assert.IsTrue(_colors[0] == MyColors.Orange); 323 | Assert.IsTrue(_colors[1] == MyColors.Yellow); 324 | } 325 | #endregion 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/DynamicTypeCreatorTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | using System.Reflection; 8 | using System.Reflection.Emit; 9 | using ConsoleCommon; 10 | using System.Diagnostics; 11 | using System.Linq.Expressions; 12 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 13 | using ConsoleCommon.Parsing.TypeParsers; 14 | using ConsoleCommon.Parsing; 15 | 16 | namespace ConsoleCommon.Tests 17 | { 18 | public class Product 19 | { 20 | public virtual int MyAge { get { return 30; } } 21 | public string Name { get; set; } 22 | public Product ExtraProduct => new Product { Name = "Base Product" }; 23 | public virtual Product VirtualProduct => new Product { Name = "Virtual Product" }; 24 | public virtual Product GetVirtualProductMethod() 25 | { 26 | return new Product { Name = "Virtual Product From Method" }; 27 | } 28 | public virtual Product GetVirtualProductMethodInput(Product input) 29 | { 30 | return new Product { Name = "Virtual Product From Method" }; 31 | } 32 | } 33 | [TestFixture] 34 | public class DynamicTypeCreatorTests 35 | { 36 | [Test] 37 | public void TestTypeCreator() 38 | { 39 | //create and save new type 40 | object _newProduct = DynamicTypeCreator 41 | .Create("NewProduct", typeof(Product), @"C:\PROD") 42 | .AddPassThroughCtors() 43 | .AddAutoProperty("ProductName", typeof(string)) 44 | .AddVoidMethod("WriteStuff", (s) => Debug.WriteLine(s)) 45 | .FinishBuildingAndSaveType("NewProduct.dll") 46 | .GetConstructor(new Type[0] { }) 47 | .Invoke(new object[0] { }); 48 | 49 | _newProduct.GetType().GetMethod("WriteStuff").Invoke(_newProduct, new object[] { "2nd ol" }); 50 | //set ProductName value 51 | _newProduct.GetType().GetProperty("ProductName").SetValue(_newProduct, "Cool Item"); 52 | 53 | //get ProductName value 54 | string _prodName = _newProduct.GetType().GetProperty("ProductName").GetValue(_newProduct).ToString(); 55 | Assert.IsTrue(_prodName == "Cool Item"); 56 | } 57 | [Test] 58 | public void TestAddVoidMethod_2() 59 | { 60 | //create and save new type 61 | object _newProduct = DynamicTypeCreator 62 | .Create("NewProduct", typeof(Product)) 63 | .AddPassThroughCtors() 64 | .AddAutoProperty("ProductName", typeof(string)) 65 | .AddVoidMethod("WriteStuff", (s, i) => Debug.WriteLine($"{s} {i * -1}")) 66 | .FinishBuildingType() 67 | .GetConstructor(new Type[0] { }) 68 | .Invoke(new object[0] { }); 69 | 70 | _newProduct.GetType().GetMethod("WriteStuff").Invoke(_newProduct, new object[] { "2nd ol", 5 }); 71 | } 72 | [Test] 73 | public void TestAddMethod_1() 74 | { 75 | //create and save new type 76 | object _newProduct = DynamicTypeCreator 77 | .Create("NewProduct", typeof(Product)) 78 | .AddPassThroughCtors() 79 | .AddAutoProperty("ProductName", typeof(string)) 80 | .AddMethod("WriteStuff", () => $"{20 * -1}") 81 | .FinishBuildingType() 82 | .GetConstructor(new Type[0] { }) 83 | .Invoke(new object[0] { }); 84 | 85 | string _out = 86 | _newProduct.GetType().GetMethod("WriteStuff") 87 | .Invoke(_newProduct, new object[0] { }) 88 | .ToString(); 89 | 90 | Assert.IsTrue(_out == (-20).ToString()); 91 | } 92 | [Test] 93 | public void TestAddMethod_2() 94 | { 95 | //create and save new type 96 | object _newProduct = DynamicTypeCreator 97 | .Create("NewProduct", typeof(Product)) 98 | .AddPassThroughCtors() 99 | .AddAutoProperty("ProductName", typeof(string)) 100 | .AddMethod("WriteStuff", (i) => $"{i * -1}") 101 | .FinishBuildingType() 102 | .GetConstructor(new Type[0] { }) 103 | .Invoke(new object[0] { }); 104 | 105 | string _out = 106 | _newProduct.GetType().GetMethod("WriteStuff") 107 | .Invoke(_newProduct, new object[] { 5 }) 108 | .ToString(); 109 | 110 | Assert.IsTrue(_out == (-5).ToString()); 111 | } 112 | [Test] 113 | public void TestUseBaseProperty() 114 | { 115 | //create and save new type 116 | object _newProduct = DynamicTypeCreator 117 | .Create("NewProduct", typeof(Product)) 118 | .AddPassThroughCtors() 119 | .AddAutoProperty("dude", typeof(string)) 120 | //.AddMethod("get_MyAge", () => 18) 121 | .FinishBuildingType() 122 | .GetConstructor(new Type[0] { }) 123 | .Invoke(new object[0] { }); 124 | 125 | PropertyInfo _ageProp = _newProduct.GetType().GetProperty("MyAge"); 126 | Assert.IsNotNull(_ageProp, "MyAge prop not found"); 127 | int _out = (int)_ageProp.GetValue(_newProduct); 128 | Assert.IsTrue(_out == 30); 129 | } 130 | [Test] 131 | public void TestOverrideBaseProperty() 132 | { 133 | //create and save new type 134 | object _newProduct = DynamicTypeCreator 135 | .Create("NewProduct", typeof(Product)) 136 | .AddPassThroughCtors() 137 | .AddAutoProperty("popo", typeof(string)) 138 | .OverrideGet("MyAge", () => 18) 139 | .FinishBuildingType() 140 | .GetConstructor(new Type[0] { }) 141 | .Invoke(new object[0] { }); 142 | 143 | IEnumerable _ageProps = 144 | _newProduct.GetType().GetProperties().Where(m => m.Name == "MyAge"); 145 | Assert.IsFalse(_ageProps.Count() != 1, "more or less than one age prop appeared"); 146 | int _age = (int)_ageProps.First().GetValue(_newProduct); 147 | Assert.IsTrue(_age == 18); 148 | 149 | _age = (int)_newProduct.GetType().GetMethod("get_MyAge").Invoke(_newProduct, null); 150 | Assert.IsTrue(_age == 18); 151 | } 152 | [Test] 153 | public void TestCombinedAutoProperty() 154 | { 155 | //create and save new type 156 | object _newProduct = DynamicTypeCreator 157 | .Create("NewProduct", typeof(Product)) 158 | .AddPassThroughCtors() 159 | .AddAutoProperty("popo", typeof(string)) 160 | .AddAutoProperty("MyAge", typeof(int)) 161 | .FinishBuildingType() 162 | .GetConstructor(new Type[0] { }) 163 | .Invoke(new object[0] { }); 164 | 165 | setPropVal(_newProduct, "popo", "Big Cop"); 166 | Assert.IsTrue(getPropVal(_newProduct, "popo").ToString() == "Big Cop"); 167 | Assert.IsTrue((int)getPropVal(_newProduct, "MyAge") == 0); 168 | setPropVal(_newProduct, "MyAge", 18); 169 | Assert.IsTrue((int)getPropVal(_newProduct, "MyAge") == 18); 170 | } 171 | [Test] 172 | public void TestCombinedGetProperty() 173 | { 174 | //create and save new type 175 | object _newProduct = DynamicTypeCreator 176 | .Create("NewProduct", typeof(Product)) 177 | .AddPassThroughCtors() 178 | .AddProperty("popo", typeof(string)) 179 | .AddGet(() => "Big Cop") 180 | .AddProperty("MyAge", typeof(int)) 181 | .AddGet(() => 18) 182 | .FinishBuildingType() 183 | .GetConstructor(new Type[0] { }) 184 | .Invoke(new object[0] { }); 185 | 186 | Assert.IsTrue(getPropVal(_newProduct, "popo").ToString() == "Big Cop"); 187 | Assert.IsTrue((int)getPropVal(_newProduct, "MyAge") == 18); 188 | bool _smallCopError = false; 189 | try 190 | { 191 | setPropVal(_newProduct, "popo", "Small Cop"); 192 | } 193 | catch { _smallCopError = true; } 194 | Assert.IsTrue(_smallCopError); 195 | 196 | bool _ageError = false; 197 | try 198 | { 199 | setPropVal(_newProduct, "MyAge", 28); 200 | } 201 | catch { _ageError = true; } 202 | Assert.IsTrue(_ageError); 203 | } 204 | [Test] 205 | public void TestCombinedGetProperty_Generics() 206 | { 207 | //create and save new type 208 | object _newProduct = DynamicTypeCreator 209 | .Create("NewProduct") 210 | .AddPassThroughCtors() 211 | .AddProperty("popo") 212 | .AddGet(() => "Big Cop") 213 | .AddProperty("MyAge") 214 | .AddGet(() => 18) 215 | .FinishBuildingType() 216 | .GetConstructor(new Type[0] { }) 217 | .Invoke(new object[0] { }); 218 | 219 | Assert.IsTrue(getPropVal(_newProduct, "popo").ToString() == "Big Cop"); 220 | Assert.IsTrue((int)getPropVal(_newProduct, "MyAge") == 18); 221 | } 222 | [Test] 223 | public void TestAddSimpleMethod() 224 | { 225 | //create and save new type 226 | object _newProduct = DynamicTypeCreator 227 | .Create("NewProduct") 228 | .AddPassThroughCtors() 229 | .AddMethod("newMeth", (i, j) => i + j) 230 | .FinishBuildingType() 231 | .GetConstructor(new Type[0] { }) 232 | .Invoke(new object[0] { }); 233 | 234 | MethodInfo _meth = _newProduct.GetType().GetMethods().FirstOrDefault(m => m.Name == "newMeth"); 235 | Assert.IsNotNull(_meth, "new method not found"); 236 | int _add = (int)_meth.Invoke(_newProduct, new object[] { 5, 6 }); 237 | Assert.IsTrue(_add == 11); 238 | } 239 | [Test] 240 | public void TestAddInComplexMethod() 241 | { 242 | //create and save new type 243 | object _newProduct = DynamicTypeCreator 244 | .Create("NewProduct") 245 | .AddPassThroughCtors() 246 | .AddMethod("calcOldAge", (i) => 70 - i.MyAge) 247 | .FinishBuildingType() 248 | .GetConstructor(new Type[0] { }) 249 | .Invoke(new object[0] { }); 250 | 251 | MethodInfo _meth = _newProduct.GetType().GetMethods().FirstOrDefault(m => m.Name == "calcOldAge"); 252 | Assert.IsNotNull(_meth, "new method not found"); 253 | int _old = (int)_meth.Invoke(_newProduct, new object[] { new Product() }); 254 | Assert.IsTrue(_old == 40); 255 | } 256 | [Test] 257 | public void TestAddOutComplexMethod() 258 | { 259 | //create and save new type 260 | object _newProduct = DynamicTypeCreator 261 | .Create("NewProduct") 262 | .AddPassThroughCtors() 263 | .AddMethod("newProd", (i) => new Product { Name = i }) 264 | .FinishBuildingType() 265 | .GetConstructor(new Type[0] { }) 266 | .Invoke(new object[0] { }); 267 | 268 | MethodInfo _meth = _newProduct.GetType().GetMethods().FirstOrDefault(m => m.Name == "newProd"); 269 | Assert.IsNotNull(_meth, "new method not found"); 270 | Product _yis = (Product)_meth.Invoke(_newProduct, new object[] { "Yisrael" }); 271 | Assert.IsTrue(_yis.Name == "Yisrael"); 272 | } 273 | [Test] 274 | public void TestAddOutInComplexMethod() 275 | { 276 | //create and save new type 277 | object _newProduct = DynamicTypeCreator 278 | .Create("NewProduct") 279 | .AddPassThroughCtors() 280 | .AddMethod("newProd", (i) => new Product { Name = i.Name + " Johannan!" }) 281 | .FinishBuildingType() 282 | .GetConstructor(new Type[0] { }) 283 | .Invoke(new object[0] { }); 284 | 285 | MethodInfo _meth = _newProduct.GetType().GetMethods().FirstOrDefault(m => m.Name == "newProd"); 286 | Assert.IsNotNull(_meth, "new method not found"); 287 | Product _yis = new Product() { Name = "Yisrael" }; 288 | Product _newProd = (Product)_meth.Invoke(_newProduct, new object[] { _yis }); 289 | Assert.IsTrue(_newProd.Name == "Yisrael Johannan!"); 290 | } 291 | [Test] 292 | public void TestAddComplexProperty() 293 | { 294 | //create and save new type 295 | object _newProduct = DynamicTypeCreator 296 | .Create("NewProduct") 297 | .AddPassThroughCtors() 298 | .AddProperty("newProd") 299 | .AddGet(() => new Product { Name = "Yisrael Johannan!" }) 300 | .FinishBuildingType() 301 | .GetConstructor(new Type[0] { }) 302 | .Invoke(new object[0] { }); 303 | 304 | PropertyInfo _meth = _newProduct.GetType().GetProperties().FirstOrDefault(m => m.Name == "newProd"); 305 | Assert.IsNotNull(_meth, "new method not found"); 306 | Product _yis = (Product)_meth.GetValue(_newProduct); 307 | Assert.IsTrue(_yis.Name == "Yisrael Johannan!"); 308 | } 309 | [Test] 310 | public void TestHideComplexProperty() 311 | { 312 | //create and save new type 313 | object _newProduct = DynamicTypeCreator 314 | .Create("NewProduct") 315 | .AddPassThroughCtors() 316 | .AddProperty("ExtraProduct") 317 | .AddGet(() => new Product { Name = "Overriden property!" }) 318 | .FinishBuildingType() 319 | .GetConstructor(new Type[0] { }) 320 | .Invoke(new object[0] { }); 321 | 322 | PropertyInfo _meth = _newProduct.GetType().GetProperties().FirstOrDefault(m => m.Name == "ExtraProduct"); 323 | Assert.IsNotNull(_meth, "new method not found"); 324 | Product _yis = (Product)_meth.GetValue(_newProduct); 325 | Assert.IsTrue(_yis.Name == "Overriden property!"); 326 | } 327 | [Test] 328 | public void TestOverrideVirtualComplexProperty() 329 | { 330 | string _prodName = "Overridden virtual property!"; 331 | //create and save new type 332 | object _newProduct = DynamicTypeCreator 333 | .Create("NewProduct") 334 | .AddPassThroughCtors() 335 | .OverrideGet("VirtualProduct", () => new Product { Name = "Overridden virtual property!" }) 336 | .FinishBuildingType() 337 | .GetConstructor(Type.EmptyTypes) 338 | .Invoke(new object[0] { }); 339 | 340 | PropertyInfo _meth = _newProduct.GetType().GetProperties().FirstOrDefault(m => m.Name == "VirtualProduct"); 341 | Assert.IsNotNull(_meth, "new method not found"); 342 | Product _yis = (Product)_meth.GetValue(_newProduct); 343 | Assert.IsTrue(_yis.Name == _prodName); 344 | } 345 | [Test] 346 | public void TestOverrideVirtualComplexProperty_ParamsObject() 347 | { 348 | //create and save new type 349 | object _newProduct = DynamicTypeCreator 350 | .Create("NewParams") 351 | .AddPassThroughCtors() 352 | .OverrideGet("TypeParser", 353 | () => new TypeParserContainer(false, new KeyValueParser())) 354 | .FinishBuildingType() 355 | .GetConstructor(new Type[] { typeof(string[]) }) 356 | .Invoke(new object[] { new string[0] { } }); 357 | 358 | PropertyInfo _meth = _newProduct.GetType().GetProperties().FirstOrDefault(m => m.Name == "TypeParser"); 359 | Assert.IsNotNull(_meth, "new method not found"); 360 | ITypeParserContainer _yis = (ITypeParserContainer)_meth.GetValue(_newProduct); 361 | bool _stringNotAccepted = false; 362 | try 363 | { 364 | _yis.GetParser(typeof(string)); 365 | } 366 | catch 367 | { 368 | _stringNotAccepted = true; 369 | } 370 | Assert.IsTrue(_stringNotAccepted, "String parser found"); 371 | bool _keyValueParserFound = true; 372 | try 373 | { 374 | _yis.GetParser(typeof(KeyValuePair<,>)); 375 | } 376 | catch 377 | { 378 | _keyValueParserFound = false; 379 | } 380 | Assert.IsTrue(_keyValueParserFound, "Keyvalue parser not found"); 381 | } 382 | [Test] 383 | public void TestMethodOverride() 384 | { 385 | //create and save new type 386 | object _newProduct = DynamicTypeCreator 387 | .Create("NewProduct") 388 | .AddPassThroughCtors() 389 | .OverrideMethod("GetVirtualProductMethod", () => new Product { Name = "Overridden Method!" }) 390 | .FinishBuildingType() 391 | .GetConstructor(Type.EmptyTypes) 392 | .Invoke(new object[0] { }); 393 | 394 | MethodInfo _meth = _newProduct.GetType().GetMethods().FirstOrDefault(m => m.Name == "GetVirtualProductMethod"); 395 | Assert.IsNotNull(_meth, "new method not found"); 396 | Product _yis = (Product)_meth.Invoke(_newProduct, new object[0] { }); 397 | Assert.IsTrue(_yis.Name == "Overridden Method!"); 398 | } 399 | [Test] 400 | public void TestMethodOverrideInput() 401 | { 402 | //create and save new type 403 | object _newProduct = DynamicTypeCreator 404 | .Create("NewProduct") 405 | .AddPassThroughCtors() 406 | .OverrideMethod("GetVirtualProductMethodInput", (p) => new Product { Name = $"Overridden Method! In Text: {p.Name}" }) 407 | .FinishBuildingType() 408 | .GetConstructor(Type.EmptyTypes) 409 | .Invoke(new object[0] { }); 410 | 411 | MethodInfo _meth = _newProduct.GetType().GetMethods().FirstOrDefault(m => m.Name == "GetVirtualProductMethodInput"); 412 | Assert.IsNotNull(_meth, "new method not found"); 413 | Product _yis = (Product)_meth.Invoke(_newProduct, new object[] { new Product { Name = "In Product!" } }); 414 | Assert.IsTrue(_yis.Name == "Overridden Method! In Text: In Product!"); 415 | } 416 | private object getPropVal(object obj, string propName) 417 | { 418 | PropertyInfo _prop = obj.GetType().GetProperty(propName); 419 | return _prop.GetValue(obj); 420 | } 421 | private void setPropVal(object obj, string propName, object propVal) 422 | { 423 | PropertyInfo _prop = obj.GetType().GetProperty(propName); 424 | _prop.SetValue(obj, propVal); 425 | } 426 | } 427 | } 428 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/DynamicTypeParsing/DynamicTypeCreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | using System.IO; 7 | using System.Linq.Expressions; 8 | 9 | namespace ConsoleCommon.Tests 10 | { 11 | public static class DynamicTypeCreator 12 | { 13 | public static IBaseObject Create(string className) 14 | { 15 | return Create(className, typeof(TParent)); 16 | } 17 | public static IBaseObject Create(string className, Type parentType) 18 | { 19 | return new DynamicTypeCreatorBase().Create(className, parentType); 20 | } 21 | public static IBaseObject Create(string className, string dir) 22 | { 23 | return Create(className, typeof(TParent), dir); 24 | } 25 | public static IBaseObject Create(string className, Type parentType, string dir) 26 | { 27 | return new DynamicTypeCreatorBase().Create(className, parentType, dir); 28 | } 29 | } 30 | public partial class DynamicTypeCreatorBase : IBaseObject, IEmptyObject, IAfterProperty, IAfterAttribute 31 | { 32 | TypeBuilder _tBuilder; 33 | List _propBuilds = new List(); 34 | AssemblyBuilder _aBuilder; 35 | Type _parentType; 36 | 37 | /// 38 | /// Begins creating type using the specified name. 39 | /// 40 | /// Class name for new type 41 | /// Name of base class. Use object if none 42 | /// 43 | public IBaseObject Create(string className, Type parentType) 44 | { 45 | return Create(className, parentType, ""); 46 | } 47 | /// 48 | /// Begins creating type using the specified name and saved in the specified directory. 49 | /// Use this overload to save the resulting .dll in a specified directory. 50 | /// 51 | /// Class name for new type 52 | /// Name of base class. Use object if none 53 | /// Directory path to save .dll in 54 | /// 55 | public IBaseObject Create (string className, Type parentType, string dir) 56 | { 57 | _parentType = parentType; 58 | //Define type 59 | AssemblyName _name = new AssemblyName(className); 60 | if (string.IsNullOrWhiteSpace(dir)) 61 | { 62 | _aBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(_name, AssemblyBuilderAccess.RunAndSave); 63 | } 64 | else _aBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(_name, AssemblyBuilderAccess.RunAndSave, dir); 65 | ModuleBuilder _mBuilder = _aBuilder.DefineDynamicModule(_name.Name, _name.Name + ".dll"); 66 | _tBuilder = _mBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class, parentType); 67 | return this; 68 | } 69 | /// 70 | /// Adds constructors to new type that match all constructors on base type. 71 | /// Parameters are passed to base type. 72 | /// 73 | /// 74 | public IEmptyObject AddPassThroughCtors() 75 | { 76 | foreach(ConstructorInfo _ctor in _parentType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) 77 | { 78 | ParameterInfo[] _params = _ctor.GetParameters(); 79 | Type[] _paramTypes = _params.Select(p => p.ParameterType).ToArray(); 80 | Type[][] _reqModifiers = _params.Select(p => p.GetRequiredCustomModifiers()).ToArray(); 81 | Type[][] _optModifiers = _params.Select(p => p.GetOptionalCustomModifiers()).ToArray(); 82 | ConstructorBuilder _ctorBuild = _tBuilder.DefineConstructor(MethodAttributes.Public, _ctor.CallingConvention, _paramTypes, _reqModifiers, _optModifiers); 83 | for (int i = 0; i < _params.Length; i++) 84 | { 85 | ParameterInfo _param = _params[i]; 86 | ParameterBuilder _prmBuild = _ctorBuild.DefineParameter(i + 1, _param.Attributes, _param.Name); 87 | if (((int)_param.Attributes & (int)ParameterAttributes.HasDefault) != 0) _prmBuild.SetConstant(_param.RawDefaultValue); 88 | 89 | foreach(CustomAttributeBuilder _attr in GetCustomAttrBuilders(_param.CustomAttributes)) 90 | { 91 | _prmBuild.SetCustomAttribute(_attr); 92 | } 93 | } 94 | 95 | //ConstructorBuilder _cBuilder = _tBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Any, argTypes); 96 | ILGenerator _ctorGen = _ctorBuild.GetILGenerator(); 97 | _ctorGen.Emit(OpCodes.Nop); 98 | //arg0=new obj, arg1-arg3=passed params. Push onto stack for call to base class 99 | _ctorGen.Emit(OpCodes.Ldarg_0); 100 | for (int i = 1; i <= _params.Length; i++) _ctorGen.Emit(OpCodes.Ldarg, i); 101 | _ctorGen.Emit(OpCodes.Call, _ctor); 102 | _ctorGen.Emit(OpCodes.Ret); 103 | } 104 | return this; 105 | } 106 | 107 | public IAfterAttribute AddPropertyAttribute(Type[] ctorArgTypes, params object[] ctorArgs) 108 | { 109 | return AddPropertyAttribute(typeof(TAttr), ctorArgTypes, ctorArgs); 110 | } 111 | /// 112 | /// Adds an attribute to a property just added. 113 | /// 114 | /// Type of attribute 115 | /// Types of attribute's cstor parameters 116 | /// Values to pass in to attribute's cstor. Must match in type and order of cstorArgTypes parameter 117 | /// 118 | public IAfterAttribute AddPropertyAttribute(Type attrType, Type[] ctorArgTypes, params object[] ctorArgs) 119 | { 120 | if (ctorArgTypes.Length != ctorArgs.Length) throw new Exception("Type count must match arg count for attribute specification"); 121 | ConstructorInfo _attrCtor = attrType.GetConstructor(ctorArgTypes); 122 | if (_attrCtor == null) throw new Exception("Cstor for property attribute not found"); 123 | for (int i = 0; i < ctorArgTypes.Length; i++) 124 | { 125 | CustomAttributeBuilder _attrBuild = new CustomAttributeBuilder(_attrCtor, ctorArgs); 126 | _propBuilds.Last().propertyBuilder.SetCustomAttribute(_attrBuild); 127 | } 128 | return this; 129 | } 130 | /// 131 | /// Completes building type, compiles it, and returns the resulting type 132 | /// 133 | /// 134 | public Type FinishBuildingType() 135 | { 136 | foreach(var _pBuilder in _propBuilds) 137 | { 138 | if (_pBuilder.getBuilder != null) _pBuilder.propertyBuilder.SetGetMethod(_pBuilder.getBuilder); 139 | if (_pBuilder.setBuilder != null) _pBuilder.propertyBuilder.SetSetMethod(_pBuilder.setBuilder); 140 | } 141 | 142 | Type _paramsType = _tBuilder.CreateType(); 143 | return _paramsType; 144 | } 145 | /// 146 | /// Completes building type, compiles it, saves it, and returns the resultying type. 147 | /// Assembly is saved in the calling assembly's directory or in the dir specified in the Create method. 148 | /// 149 | /// Filename of the assembly 150 | /// 151 | public Type FinishBuildingAndSaveType(string assemblyFileName) 152 | { 153 | Type _newType = FinishBuildingType(); 154 | Save(assemblyFileName); 155 | return _newType; 156 | } 157 | #region Helpers 158 | private IAfterProperty addMethod(string methodName, LambdaExpression methBody, MethodAttributes methAttrs = MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.Static) 159 | { 160 | MethodBuilder _mb = _tBuilder.DefineMethod(methodName, methAttrs); 161 | methBody.CompileToMethod(_mb); 162 | 163 | return this; 164 | } 165 | private IAfterProperty overrideMethod(string methodName, LambdaExpression methBody, MethodAttributes methAttrs = MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.Static) 166 | { 167 | Type[] _paramTypes = methBody.Parameters.Select(p => p.Type).ToArray(); 168 | MethodInfo _methToOverride = getMethodToOverride(methodName, _paramTypes); 169 | bool _isOverride = _methToOverride!=null; 170 | if (!_isOverride) throw new Exception($"Method '{methodName}' not found"); 171 | 172 | //stat method 173 | MethodAttributes _statAttrs = MethodAttributes.Private | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.Static; 174 | MethodBuilder _statMBuild = _tBuilder.DefineMethod($"stat_{methodName}", _statAttrs); 175 | methBody.CompileToMethod(_statMBuild); 176 | 177 | //non stat method 178 | _statAttrs = MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.Virtual; 179 | MethodBuilder _mBuild = _tBuilder.DefineMethod( 180 | methodName, 181 | _statAttrs, 182 | CallingConventions.HasThis, 183 | _statMBuild.ReturnType, 184 | _paramTypes 185 | ); 186 | ILGenerator _mGen = _mBuild.GetILGenerator(); 187 | //_mGen.Emit(OpCodes.Ldarg_0); 188 | for (int i = 1; i <= _paramTypes.Length; i++) _mGen.Emit(OpCodes.Ldarg, i); 189 | _mGen.Emit(OpCodes.Call, _statMBuild); 190 | _mGen.Emit(OpCodes.Ret); 191 | 192 | _tBuilder.DefineMethodOverride(_mBuild, _methToOverride); 193 | 194 | return this; 195 | } 196 | private CustomAttributeBuilder[] GetCustomAttrBuilders(IEnumerable customAttributes) 197 | { 198 | return customAttributes.Select(attribute => { 199 | object[] attributeArgs = attribute.ConstructorArguments.Select(a => a.Value).ToArray(); 200 | PropertyInfo[] namedPropertyInfos = attribute.NamedArguments.Select(a => a.MemberInfo).OfType().ToArray(); 201 | object[] namedPropertyValues = attribute.NamedArguments.Where(a => a.MemberInfo is PropertyInfo).Select(a => a.TypedValue.Value).ToArray(); 202 | FieldInfo[] namedFieldInfos = attribute.NamedArguments.Select(a => a.MemberInfo).OfType().ToArray(); 203 | object[] namedFieldValues = attribute.NamedArguments.Where(a => a.MemberInfo is FieldInfo).Select(a => a.TypedValue.Value).ToArray(); 204 | return new CustomAttributeBuilder(attribute.Constructor, attributeArgs, namedPropertyInfos, namedPropertyValues, namedFieldInfos, namedFieldValues); 205 | }).ToArray(); 206 | } 207 | /// 208 | /// Requires admin privileges. 209 | /// To save in a specified dir, use the Create overload that requires a 'dir' parameter. 210 | /// 211 | /// 212 | private void Save(string assemblyFileName) 213 | { 214 | string _assemblyName = assemblyFileName; 215 | if (!Path.HasExtension(assemblyFileName) || Path.GetExtension(assemblyFileName).ToLower() != ".dll") 216 | _assemblyName += ".dll"; 217 | _aBuilder.Save(_assemblyName); 218 | } 219 | private MethodInfo getMethodToOverride(string methName, Type[] parameterTypes) 220 | { 221 | BindingFlags _bFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static; 222 | MethodInfo _matchMeth = 223 | _parentType.GetMethods(_bFlags) 224 | .Where(m => 225 | m.Name == methName && 226 | m.GetParameters() 227 | .Select(p => p.ParameterType) 228 | .SequenceEqual(parameterTypes)) 229 | .FirstOrDefault(); 230 | return _matchMeth; 231 | } 232 | 233 | public IAfterProperty OverrideGet(string propertyName, Expression> getBody) 234 | { 235 | return OverrideMethod($"get_{propertyName}", getBody); 236 | } 237 | 238 | public IAfterProperty OverrideSet(string propertyName, Expression> setBody) 239 | { 240 | return OverrideVoidMethod($"set_{propertyName}", setBody); 241 | } 242 | #endregion 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/DynamicTypeParsing/DynamicTypeCreatorBaseAddMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace ConsoleCommon.Tests 5 | { 6 | public partial class DynamicTypeCreatorBase 7 | { 8 | public IAfterProperty AddVoidMethod(string methodName, Expression methBody) 9 | { 10 | return addMethod(methodName, methBody); 11 | } 12 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 13 | { 14 | return addMethod(methodName, methBody); 15 | } 16 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 17 | { 18 | return addMethod(methodName, methBody); 19 | } 20 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 21 | { 22 | return addMethod(methodName, methBody); 23 | } 24 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 25 | { 26 | return addMethod(methodName, methBody); 27 | } 28 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 29 | { 30 | return addMethod(methodName, methBody); 31 | } 32 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 33 | { 34 | return addMethod(methodName, methBody); 35 | } 36 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 37 | { 38 | return addMethod(methodName, methBody); 39 | } 40 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 41 | { 42 | return addMethod(methodName, methBody); 43 | } 44 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 45 | { 46 | return addMethod(methodName, methBody); 47 | } 48 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 49 | { 50 | return addMethod(methodName, methBody); 51 | } 52 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 53 | { 54 | return addMethod(methodName, methBody); 55 | } 56 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 57 | { 58 | return addMethod(methodName, methBody); 59 | } 60 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 61 | { 62 | return addMethod(methodName, methBody); 63 | } 64 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 65 | { 66 | return addMethod(methodName, methBody); 67 | } 68 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 69 | { 70 | return addMethod(methodName, methBody); 71 | } 72 | public IAfterProperty AddVoidMethod(string methodName, Expression> methBody) 73 | { 74 | return addMethod(methodName, methBody); 75 | } 76 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 77 | { 78 | return addMethod(methodName, methBody); 79 | } 80 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 81 | { 82 | return addMethod(methodName, methBody); 83 | } 84 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 85 | { 86 | return addMethod(methodName, methBody); 87 | } 88 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 89 | { 90 | return addMethod(methodName, methBody); 91 | } 92 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 93 | { 94 | return addMethod(methodName, methBody); 95 | } 96 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 97 | { 98 | return addMethod(methodName, methBody); 99 | } 100 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 101 | { 102 | return addMethod(methodName, methBody); 103 | } 104 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 105 | { 106 | return addMethod(methodName, methBody); 107 | } 108 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 109 | { 110 | return addMethod(methodName, methBody); 111 | } 112 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 113 | { 114 | return addMethod(methodName, methBody); 115 | } 116 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 117 | { 118 | return addMethod(methodName, methBody); 119 | } 120 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 121 | { 122 | return addMethod(methodName, methBody); 123 | } 124 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 125 | { 126 | return addMethod(methodName, methBody); 127 | } 128 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 129 | { 130 | return addMethod(methodName, methBody); 131 | } 132 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 133 | { 134 | return addMethod(methodName, methBody); 135 | } 136 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 137 | { 138 | return addMethod(methodName, methBody); 139 | } 140 | public IAfterProperty AddMethod(string methodName, Expression> methBody) 141 | { 142 | return addMethod(methodName, methBody); 143 | } 144 | 145 | 146 | public IAfterProperty OverrideVoidMethod(string methodName, Expression methBody) 147 | { 148 | return overrideMethod(methodName, methBody); 149 | } 150 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 151 | { 152 | return overrideMethod(methodName, methBody); 153 | } 154 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 155 | { 156 | return overrideMethod(methodName, methBody); 157 | } 158 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 159 | { 160 | return overrideMethod(methodName, methBody); 161 | } 162 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 163 | { 164 | return overrideMethod(methodName, methBody); 165 | } 166 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 167 | { 168 | return overrideMethod(methodName, methBody); 169 | } 170 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 171 | { 172 | return overrideMethod(methodName, methBody); 173 | } 174 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 175 | { 176 | return overrideMethod(methodName, methBody); 177 | } 178 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 179 | { 180 | return overrideMethod(methodName, methBody); 181 | } 182 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 183 | { 184 | return overrideMethod(methodName, methBody); 185 | } 186 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 187 | { 188 | return overrideMethod(methodName, methBody); 189 | } 190 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 191 | { 192 | return overrideMethod(methodName, methBody); 193 | } 194 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 195 | { 196 | return overrideMethod(methodName, methBody); 197 | } 198 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 199 | { 200 | return overrideMethod(methodName, methBody); 201 | } 202 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 203 | { 204 | return overrideMethod(methodName, methBody); 205 | } 206 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 207 | { 208 | return overrideMethod(methodName, methBody); 209 | } 210 | public IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody) 211 | { 212 | return overrideMethod(methodName, methBody); 213 | } 214 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 215 | { 216 | return overrideMethod(methodName, methBody); 217 | } 218 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 219 | { 220 | return overrideMethod(methodName, methBody); 221 | } 222 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 223 | { 224 | return overrideMethod(methodName, methBody); 225 | } 226 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 227 | { 228 | return overrideMethod(methodName, methBody); 229 | } 230 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 231 | { 232 | return overrideMethod(methodName, methBody); 233 | } 234 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 235 | { 236 | return overrideMethod(methodName, methBody); 237 | } 238 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 239 | { 240 | return overrideMethod(methodName, methBody); 241 | } 242 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 243 | { 244 | return overrideMethod(methodName, methBody); 245 | } 246 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 247 | { 248 | return overrideMethod(methodName, methBody); 249 | } 250 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 251 | { 252 | return overrideMethod(methodName, methBody); 253 | } 254 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 255 | { 256 | return overrideMethod(methodName, methBody); 257 | } 258 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 259 | { 260 | return overrideMethod(methodName, methBody); 261 | } 262 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 263 | { 264 | return overrideMethod(methodName, methBody); 265 | } 266 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 267 | { 268 | return overrideMethod(methodName, methBody); 269 | } 270 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 271 | { 272 | return overrideMethod(methodName, methBody); 273 | } 274 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 275 | { 276 | return overrideMethod(methodName, methBody); 277 | } 278 | public IAfterProperty OverrideMethod(string methodName, Expression> methBody) 279 | { 280 | return overrideMethod(methodName, methBody); 281 | } 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/DynamicTypeParsing/DynamicTypeCreatorBaseAddProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Reflection; 7 | using System.Reflection.Emit; 8 | using System.Linq.Expressions; 9 | 10 | namespace ConsoleCommon.Tests 11 | { 12 | public partial class DynamicTypeCreatorBase : IGetOrSetAdded 13 | { 14 | public ICreatingProperty AddProperty(string name) 15 | { 16 | return AddProperty(name, typeof(T)); 17 | } 18 | public ICreatingProperty AddProperty(string name, Type type) 19 | { 20 | PropertyBuilder _pBuilder = _tBuilder.DefineProperty(name, PropertyAttributes.SpecialName, CallingConventions.HasThis, type, Type.EmptyTypes); 21 | _propBuilds.Add(new PropertyBuilding { propertyBuilder = _pBuilder }); 22 | return this; 23 | } 24 | public IAfterProperty AddAutoProperty(string name) 25 | { 26 | return AddAutoProperty(name, typeof(T)); 27 | } 28 | /// 29 | /// Adds a new property to type with specified name and type. 30 | /// 31 | /// Name of property 32 | /// Type of property 33 | /// 34 | public IAfterProperty AddAutoProperty(string name, Type type) 35 | { 36 | //base property 37 | AddProperty(name, type); 38 | AddAutoGet(); 39 | AddAutoSet(); 40 | return this; 41 | } 42 | public IGetOrSetAdded AddGet(Expression> methBody) 43 | { 44 | //get last prop 45 | PropertyBuilding _pBuild = _propBuilds.Last(); 46 | string _name = _pBuild.propertyBuilder.Name; 47 | Type _type = _pBuild.propertyBuilder.PropertyType; 48 | 49 | //static method, get body 50 | MethodAttributes _statAttrs = MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.Static; 51 | MethodBuilder _statMethBuilder = _tBuilder.DefineMethod($"stat_get_{_name}", _statAttrs); 52 | methBody.CompileToMethod(_statMethBuilder); 53 | 54 | //get method 55 | MethodAttributes _attrs = GetGetSetAttrs(_name); 56 | MethodBuilder _getBuilder = _tBuilder.DefineMethod($"get_{_name}", _attrs, CallingConventions.HasThis, methBody.ReturnType, Type.EmptyTypes); 57 | ILGenerator _mGen = _getBuilder.GetILGenerator(); 58 | _mGen.Emit(OpCodes.Call, _statMethBuilder); 59 | _mGen.Emit(OpCodes.Ret); 60 | 61 | _pBuild.getBuilder = _getBuilder; 62 | return this; 63 | } 64 | public IGetOrSetAdded AddAutoGet() 65 | { 66 | PropertyBuilding _pBuild = _propBuilds.Last(); 67 | string _name = _pBuild.propertyBuilder.Name; 68 | Type _type = _pBuild.propertyBuilder.PropertyType; 69 | 70 | //backing field 71 | FieldBuilder _fBuilder = getFieldBuilder(_pBuild); 72 | 73 | //get method 74 | MethodAttributes _propAttrs = GetGetSetAttrs(_name); 75 | MethodBuilder _getBuilder = _tBuilder.DefineMethod($"get_{_name}", _propAttrs, CallingConventions.HasThis, _type, Type.EmptyTypes); 76 | ILGenerator _getGen = _getBuilder.GetILGenerator(); 77 | _getGen.Emit(OpCodes.Ldarg_0); 78 | _getGen.Emit(OpCodes.Ldfld, _fBuilder); 79 | _getGen.Emit(OpCodes.Ret); 80 | 81 | _pBuild.getBuilder = _getBuilder; 82 | return this; 83 | } 84 | public IGetOrSetAdded AddAutoSet() 85 | { 86 | PropertyBuilding _pBuild = _propBuilds.Last(); 87 | string _name = _pBuild.propertyBuilder.Name; 88 | Type _type = _pBuild.propertyBuilder.PropertyType; 89 | 90 | //backing field 91 | FieldBuilder _fBuilder = getFieldBuilder(_pBuild); 92 | 93 | //set method 94 | MethodAttributes _propAttrs = GetGetSetAttrs(_name); 95 | MethodBuilder _setBuilder = _tBuilder.DefineMethod($"set_{_name}", _propAttrs, null, new Type[] { _type }); 96 | ILGenerator _setGen = _setBuilder.GetILGenerator(); 97 | _setGen.Emit(OpCodes.Ldarg_0); 98 | _setGen.Emit(OpCodes.Ldarg_1); 99 | _setGen.Emit(OpCodes.Stfld, _fBuilder); 100 | _setGen.Emit(OpCodes.Ret); 101 | 102 | _pBuild.setBuilder = _setBuilder; 103 | return this; 104 | } 105 | 106 | #region Helpers 107 | private MethodAttributes GetGetSetAttrs(string propName) 108 | { 109 | BindingFlags _bFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; 110 | MethodAttributes _attrs; 111 | //override 112 | if (_parentType.GetProperties(_bFlags).FirstOrDefault(p => p.Name == propName) != null) 113 | { 114 | _attrs = MethodAttributes.Public | MethodAttributes.HideBySig 115 | | MethodAttributes.SpecialName | MethodAttributes.Virtual 116 | | MethodAttributes.NewSlot | MethodAttributes.Final; 117 | } 118 | else 119 | { 120 | _attrs = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; 121 | } 122 | return _attrs; 123 | } 124 | private FieldBuilder getFieldBuilder(PropertyBuilding pBuilding) 125 | { 126 | //backing field 127 | string _name = pBuilding.propertyBuilder.Name; 128 | Type _type = pBuilding.propertyBuilder.PropertyType; 129 | 130 | FieldBuilder _fBuilder = pBuilding.backFieldBuilder; 131 | if (_fBuilder == null) 132 | { 133 | _fBuilder = _tBuilder.DefineField($"m_{_name}", _type, FieldAttributes.Private); 134 | pBuilding.backFieldBuilder = _fBuilder; 135 | } 136 | return _fBuilder; 137 | } 138 | #endregion 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/DynamicTypeParsing/IEmptyObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Linq.Expressions; 7 | 8 | namespace ConsoleCommon.Tests 9 | { 10 | public interface IEmptyObject 11 | { 12 | IAfterProperty OverrideGet(string propertyName, Expression> getBody); 13 | IAfterProperty OverrideSet(string propertyName, Expression> setBody); 14 | ICreatingProperty AddProperty(string name, Type type); 15 | IAfterProperty AddAutoProperty(string name, Type type); 16 | ICreatingProperty AddProperty(string name); 17 | IAfterProperty AddAutoProperty(string name); 18 | IAfterProperty AddVoidMethod(string methodName, Expression methBody); 19 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 20 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 21 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 22 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 23 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 24 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 25 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 26 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 27 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 28 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 29 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 30 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 31 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 32 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 33 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 34 | IAfterProperty AddVoidMethod(string methodName, Expression> methBody); 35 | IAfterProperty AddMethod(string methodName, Expression> methBody); 36 | IAfterProperty AddMethod(string methodName, Expression> methBody); 37 | IAfterProperty AddMethod(string methodName, Expression> methBody); 38 | IAfterProperty AddMethod(string methodName, Expression> methBody); 39 | IAfterProperty AddMethod(string methodName, Expression> methBody); 40 | IAfterProperty AddMethod(string methodName, Expression> methBody); 41 | IAfterProperty AddMethod(string methodName, Expression> methBody); 42 | IAfterProperty AddMethod(string methodName, Expression> methBody); 43 | IAfterProperty AddMethod(string methodName, Expression> methBody); 44 | IAfterProperty AddMethod(string methodName, Expression> methBody); 45 | IAfterProperty AddMethod(string methodName, Expression> methBody); 46 | IAfterProperty AddMethod(string methodName, Expression> methBody); 47 | IAfterProperty AddMethod(string methodName, Expression> methBody); 48 | IAfterProperty AddMethod(string methodName, Expression> methBody); 49 | IAfterProperty AddMethod(string methodName, Expression> methBody); 50 | IAfterProperty AddMethod(string methodName, Expression> methBody); 51 | IAfterProperty AddMethod(string methodName, Expression> methBody); 52 | 53 | IAfterProperty OverrideVoidMethod(string methodName, Expression methBody); 54 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 55 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 56 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 57 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 58 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 59 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 60 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 61 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 62 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 63 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 64 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 65 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 66 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 67 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 68 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 69 | IAfterProperty OverrideVoidMethod(string methodName, Expression> methBody); 70 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 71 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 72 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 73 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 74 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 75 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 76 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 77 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 78 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 79 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 80 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 81 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 82 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 83 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 84 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 85 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 86 | IAfterProperty OverrideMethod(string methodName, Expression> methBody); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/DynamicTypeParsing/OtherInterfaces.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace ConsoleCommon.Tests 5 | { 6 | public interface IBaseObject 7 | { 8 | IEmptyObject AddPassThroughCtors(); 9 | } 10 | 11 | public interface IAfterProperty : IEmptyObject, IFinishBuild 12 | { 13 | IAfterAttribute AddPropertyAttribute(Type attrType, Type[] ctorArgTypes, params object[] ctorArgs); 14 | IAfterAttribute AddPropertyAttribute(Type[] ctorArgTypes, params object[] ctorArgs); 15 | } 16 | public interface ICreatingProperty 17 | { 18 | IGetOrSetAdded AddAutoGet(); 19 | IGetOrSetAdded AddAutoSet(); 20 | IGetOrSetAdded AddGet(Expression> methBody); 21 | } 22 | public interface IGetOrSetAdded : IAfterProperty, ICreatingProperty 23 | { 24 | 25 | } 26 | public interface IAfterAttribute : IEmptyObject, IFinishBuild 27 | { 28 | 29 | } 30 | public interface IFinishBuild 31 | { 32 | Type FinishBuildingType(); 33 | Type FinishBuildingAndSaveType(string assemblyFileName); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/DynamicTypeParsing/PropertyBuilding.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Reflection.Emit; 7 | 8 | namespace ConsoleCommon.Tests 9 | { 10 | public class PropertyBuilding 11 | { 12 | public PropertyBuilding() 13 | { 14 | 15 | } 16 | public PropertyBuilding(PropertyBuilder propertyBuild, MethodBuilder getBuild, MethodBuilder setBuild) 17 | { 18 | propertyBuilder = propertyBuild; 19 | getBuilder = getBuild; 20 | setBuilder = setBuild; 21 | } 22 | public PropertyBuilder propertyBuilder { get; set; } 23 | public MethodBuilder getBuilder { get; set; } 24 | public MethodBuilder setBuilder { get; set; } 25 | public FieldBuilder backFieldBuilder { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/FullExampleTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ConsoleCommon.Entities; 6 | using ConsoleCommon.Parsing; 7 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 8 | using ConsoleCommon.Parsing.TypeParsers; 9 | using NUnit.Framework; 10 | using System.Diagnostics; 11 | 12 | namespace ConsoleCommon.Tests 13 | { 14 | #region Arrange Objects 15 | public enum CustomerRegion 16 | { 17 | None, 18 | Northeast, 19 | Southwest, 20 | Central, 21 | Midwest 22 | } 23 | 24 | [Flags] 25 | public enum CustomerInterests 26 | { 27 | Pizza = 1, 28 | Crabcakes = 2, 29 | Parachuting = 4, 30 | Biking = 8 31 | } 32 | 33 | [TypeParam("pizza shop")] 34 | public class PizzaShopCustomer { } 35 | [TypeParam("bodega")] 36 | public class BodegaCustomer { } 37 | 38 | public enum LastNameEnum 39 | { 40 | Smith, 41 | Johnson, 42 | Nixon, 43 | Lax 44 | } 45 | public class CustomerParamsObject : ParamsObject 46 | { 47 | public CustomerParamsObject(string[] args) 48 | : base(args) 49 | { 50 | } 51 | 52 | #region Switch Properties 53 | [Switch("F", true, 1)] 54 | [SwitchHelpText("First name of customer")] 55 | public string firstName { get; set; } 56 | 57 | [Switch("L", true, 2)] 58 | [SwitchHelpText("Last name of customer")] 59 | public LastNameEnum lastName { get; set; } 60 | 61 | [SwitchHelpText("The date of birth of customer")] 62 | [Switch("DOB", false, 3)] 63 | public DateTime DOB { get; set; } 64 | 65 | [Switch("T", false, 4)] 66 | [SwitchHelpText("Customer type")] 67 | public Type CustomerType { get; set; } 68 | 69 | [Switch("Case")] 70 | [SwitchHelpText("Specifies whether to do a case sensitive search")] 71 | public bool CaseSensitiveSearch { get; set; } 72 | 73 | [Switch("Regions")] 74 | [SwitchHelpText("Specifies the regions the customer does business in")] 75 | public CustomerRegion[] CustRegions { get; set; } 76 | [Switch("Int")] 77 | [SwitchHelpText("Specifies customer's interests")] 78 | public CustomerInterests Interests { get; set; } 79 | 80 | [Switch("Pets")] 81 | public KeyValuePair[] PetCount { get; set; } 82 | #endregion 83 | 84 | #region Other Functions 85 | 86 | public override Dictionary, string> GetParamExceptionDictionary() 87 | { 88 | Dictionary, string> _exceptionChecks = new Dictionary, string>(); 89 | 90 | Func _isDateInFuture = new Func(() => DateTime.Now <= this.DOB); 91 | 92 | _exceptionChecks.Add(_isDateInFuture, "Please choose a date of birth that is not in the future!"); 93 | return _exceptionChecks; 94 | } 95 | 96 | [HelpText(0)] 97 | public string Description 98 | { 99 | get { return "Finds a customer in the database."; } 100 | } 101 | [HelpText(1, "Example")] 102 | public string ExampleText 103 | { 104 | get { return "This is an example: CustomerFinder.exe Yisrael Lax 11-28-1987"; } 105 | } 106 | [HelpText(2)] 107 | public override string Usage 108 | { 109 | get { return base.Usage; } 110 | } 111 | [HelpText(3, "Parameters")] 112 | public override string SwitchHelp 113 | { 114 | get { return base.SwitchHelp; } 115 | } 116 | #endregion 117 | } 118 | #endregion 119 | 120 | [TestFixture] 121 | public class FullExampleTest 122 | { 123 | [Test] 124 | public void FullExampleCode() 125 | { 126 | try 127 | { 128 | string[] args = new string[] { "Yisrael", "Lax", "/Int:Pizza,Parachuting", "/Pets:dog:5,cat:3,bird:1", "/T:pizza shop", "/DOB:11-28-1987", "/Case", "/Regions:Northeast,Central" }; 129 | //This step will do type validation 130 | //and automatically cast the string args to a strongly typed object: 131 | CustomerParamsObject _customer = new CustomerParamsObject(args); 132 | //This step does additional validation 133 | _customer.CheckParams(); 134 | //Get help if user requested it 135 | string _helptext = _customer.GetHelpIfNeeded(); 136 | //Print help to console if requested 137 | if (!string.IsNullOrEmpty(_helptext)) 138 | { 139 | Console.WriteLine(_helptext); 140 | Environment.Exit(0); 141 | } 142 | string _fname = _customer.firstName; 143 | string _lname = _customer.lastName.ToString(); 144 | string _dob = _customer.DOB.ToString("MM-dd-yyyy"); 145 | string _ctype = _customer.CustomerType == null ? "None" : _customer.CustomerType.Name; 146 | string _caseSensitive = _customer.CaseSensitiveSearch ? "Yes" : "No"; 147 | string _regions = _customer.CustRegions == null ? "None" : string.Concat(_customer.CustRegions.Select(r => "," + r.ToString())).Substring(1); 148 | string _interests = _customer.Interests.ToString(); 149 | string _petCount = _customer.PetCount == null || _customer.PetCount.Length == 0 ? "None" : string.Concat(_customer.PetCount.Select(pc => ", " + pc.Key + ": " + pc.Value)).Substring(2); 150 | 151 | Debug.WriteLine(""); 152 | Debug.WriteLine("First Name: {0}", new object[] { _fname }); 153 | Debug.WriteLine("Last Name: {0}", new object[] { _lname }); 154 | Debug.WriteLine("DOB: {0}", new object[] { _dob }); 155 | Debug.WriteLine("Customer Type: {0}", new object[] { _ctype }); 156 | Debug.WriteLine("Case sensitive: {0}", new object[] { _caseSensitive }); 157 | Debug.WriteLine("Regions: {0}", new object[] { _regions }); 158 | Debug.WriteLine("Interests: {0}", new object[] { _interests }); 159 | Debug.WriteLine("Pet count: {0}", new object[] { _petCount }); 160 | 161 | //Get help 162 | args = new string[] { "/?" }; 163 | _customer = new CustomerParamsObject(args); 164 | _customer.CheckParams(); 165 | _helptext = _customer.GetHelpIfNeeded(); 166 | //Print help to console if requested 167 | if (!string.IsNullOrEmpty(_helptext)) 168 | { 169 | Debug.WriteLine(_helptext); 170 | } 171 | } 172 | catch (Exception ex) 173 | { 174 | Debug.WriteLine($"ConsoleCommon fatal error: {ex.Message}"); 175 | } 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/Helpers/DynamicParamsCreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Reflection; 7 | using System.Reflection.Emit; 8 | using ConsoleCommon.Parsing; 9 | using ConsoleCommon.Parsing.TypeParsers; 10 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 11 | 12 | namespace ConsoleCommon.Tests 13 | { 14 | public interface IEmptyParams 15 | { 16 | IAfterPropertyParams AddSwitch(string name, Type type, string switchName = "", bool required = false, int defaultOrdinal = -1, params string[] switchValues); 17 | IAfterPropertyParams AddSwitch(string name, string switchName = "", bool required = false, int defaultOrdinal = -1, params string[] switchValues); 18 | IAfterPropertyParams OverrideTypeParsers(Expression> newTypeContainerExpr); 19 | } 20 | public interface IAfterPropertyParams : IEmptyParams, IFinishParams 21 | { 22 | } 23 | 24 | public interface IFinishParams 25 | { 26 | ParamsObject FinishBuilding(params string[] args); 27 | ParamsObject FinishBuilding(StringBuilder commandText); 28 | } 29 | public static class DynamicParamsCreator 30 | { 31 | public static IEmptyParams Create(string className = null) 32 | { 33 | string _className = string.IsNullOrWhiteSpace(className) ? "DynamicParams" : className; 34 | return new DynamicParamsCreatorBase().Create(_className); 35 | } 36 | } 37 | 38 | public class DynamicParamsCreatorBase : IEmptyParams, IAfterPropertyParams 39 | { 40 | DynamicTypeCreatorBase _creatorBase = new DynamicTypeCreatorBase(); 41 | public IEmptyParams Create(string className) 42 | { 43 | _creatorBase 44 | .Create(className, typeof(ParamsObject)) 45 | .AddPassThroughCtors(); 46 | return this; 47 | } 48 | public IAfterPropertyParams AddSwitch(string name, string switchName = "", bool required = false, int defaultOrdinal = -1, params string[] switchValues) 49 | { 50 | return AddSwitch(name, typeof(T), switchName, required, defaultOrdinal, switchValues); 51 | } 52 | public IAfterPropertyParams AddSwitch(string name, Type type, string switchName = "", bool required = false, int defaultOrdinal = -1, params string[] switchValues) 53 | { 54 | string _switchName = string.IsNullOrWhiteSpace(switchName) ? name : switchName; 55 | //string switchName = "", bool required = false, int defaultOrdinal = -1, string helpText = "", params string[] switchValues 56 | Type[] _argTypes = new Type[] { typeof(string), typeof(bool), typeof(int), typeof(string), typeof(string[]) }; 57 | object[] _argVals = new object[] { _switchName, required, defaultOrdinal, "", switchValues }; 58 | 59 | _creatorBase 60 | .AddAutoProperty(name, type) 61 | .AddPropertyAttribute(typeof(SwitchAttribute), _argTypes, _argVals); 62 | return this; 63 | } 64 | public IAfterPropertyParams OverrideTypeParsers(Expression> newTypeContainerExpr) 65 | { 66 | _creatorBase 67 | .OverrideGet("TypeParser", newTypeContainerExpr); 68 | return this; 69 | } 70 | public ParamsObject FinishBuilding(params string[] args) 71 | { 72 | return (ParamsObject) 73 | _creatorBase 74 | .FinishBuildingType() 75 | .GetConstructor(new Type[] { typeof(string[]) }) 76 | .Invoke(new object[] { args }); 77 | } 78 | public ParamsObject FinishBuilding(StringBuilder commandText) 79 | { 80 | return (ParamsObject) 81 | _creatorBase 82 | .FinishBuildingType() 83 | .GetConstructor(new Type[] { typeof(string) }) 84 | .Invoke(new object[] { commandText.ToString() }); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/Helpers/ParamsObjectTestHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | 8 | namespace ConsoleCommon.Tests.Helpers 9 | { 10 | public static class ParamsObjectTestHelpers 11 | { 12 | public static Exception AssertCheckParams(ParamsObject paramObj, string errMsg = "", bool shouldFail = false, string expctdErrMsg = "") 13 | { 14 | bool _hasExpctdErr = !string.IsNullOrWhiteSpace(expctdErrMsg); 15 | string _expctdErrMsg = expctdErrMsg; 16 | string _errMsg = shouldFail ? "Parsing should have failed." : "Parsing failed."; 17 | if (errMsg.Length > 0) _errMsg += $" {errMsg}."; 18 | Exception _ex = null; 19 | 20 | try 21 | { 22 | paramObj.CheckParams(); 23 | } 24 | catch (Exception ex) 25 | { 26 | _ex = ex; 27 | if (!shouldFail) _errMsg += $" Ex: {ex.Message}"; 28 | else if (_hasExpctdErr) 29 | { 30 | _errMsg += $" Expected err '{removePeriod(expctdErrMsg)}' does not match actual err '{removePeriod(ex.Message)}'."; 31 | } 32 | } 33 | 34 | bool _failure = (shouldFail && 35 | ((_hasExpctdErr && _ex != null && removePeriod(_ex.Message).ToLower().Trim() != removePeriod(expctdErrMsg).ToLower().Trim()) || 36 | (!_hasExpctdErr && _ex == null)) 37 | ) || (!shouldFail && _ex!=null); 38 | if (_failure) 39 | { 40 | throw new Exception(_errMsg); 41 | } 42 | return _ex; 43 | } 44 | private static string removePeriod(string text) 45 | { 46 | string _rtrim = text.TrimEnd(); 47 | string _finalText = text; 48 | if (_rtrim.Length > 0 && _rtrim.EndsWith(".")) 49 | { 50 | int _indx = text.Length - (text.Length - _rtrim.Length) - 1; 51 | _finalText = text.Substring(0, _indx); 52 | if (_finalText.Length > 0 && text.Length > 1) _finalText += text.Substring(_indx + 1); 53 | } 54 | return _finalText; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/InlineHelpTextAndDefaultSwitchNameTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | using ConsoleCommon.Tests; 8 | 9 | namespace ConsoleCommon.Tests 10 | { 11 | [TestFixture] 12 | public class InlineHelpTextAndDefaultSwitchNameTest 13 | { 14 | class InlineHelpParams : ParamsObject 15 | { 16 | public InlineHelpParams(string[] args) : base(args) { } 17 | [Switch(required: true, defaultOrdinal: 0, helpText: "This is help text")] 18 | public string FirstName { get; set; } 19 | [HelpText(0)] 20 | public override string SwitchHelp => base.SwitchHelp; 21 | } 22 | [Test] 23 | public void Test_InlineHelpText_And_DefaultSwitchName() 24 | { 25 | //string switchName = "", bool required = false, int defaultOrdinal = -1, string helpText, bool dontAllowValues = false 26 | InlineHelpParams _input = new InlineHelpParams(new string[] { "/FirstName:Yisrael" }); 27 | _input.CheckParams(); 28 | string _help = _input.GetHelp(); 29 | Assert.IsTrue(_help.Contains("This is help")); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/ParamsObjectCreatorTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ConsoleCommon; 7 | using ConsoleCommon.Parsing; 8 | using ConsoleCommon.Parsing.TypeParsers; 9 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 10 | using NUnit.Framework; 11 | using System.Diagnostics; 12 | using System.Reflection; 13 | 14 | namespace ConsoleCommon.Tests 15 | { 16 | [TestFixture] 17 | public class ParamsObjectCreatorTests 18 | { 19 | [Test] 20 | public void CreateParamsObject() 21 | { 22 | /*string _switchName = name; 23 | bool _required = false; 24 | int _defaultOrdinal = -1; 25 | string[] _switchValues = new string[0] { }; */ 26 | ParamsObject _paramsObject = (ParamsObject)DynamicParamsCreator 27 | .Create() 28 | .AddSwitch("fName", typeof(string),"f") 29 | .AddSwitch("lName", typeof(string),"l") 30 | .FinishBuilding("/F:Yisrael", "/L:Lax"); 31 | 32 | Assert.IsTrue(TestValue(_paramsObject, "fName", "Yisrael")); 33 | Assert.IsTrue(TestValue(_paramsObject, "lName", "Lax")); 34 | _paramsObject.CheckParams(); 35 | } 36 | private bool TestValue(object obj, string propName, object propVal) 37 | { 38 | PropertyInfo _prop = obj.GetType().GetProperty(propName); 39 | if (_prop == null) throw new Exception($"Property '{propName}' does not exist on type '{obj.GetType().Name}'"); 40 | object _foundPropVal = _prop.GetValue(obj); 41 | 42 | if (_prop.PropertyType==typeof(string)) 43 | { 44 | string _propValStr = propVal?.ToString(); 45 | string _foundPropValStr = _foundPropVal?.ToString(); 46 | return _foundPropValStr == _propValStr; 47 | } 48 | return _foundPropVal == propVal; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ConsoleCommon.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ConsoleCommon.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("401a0951-a83d-419d-855a-ad7e13a7d65c")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("3.0.0.0")] 36 | [assembly: AssemblyFileVersion("3.0.0.0")] 37 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/TestCommandInputText.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | using ConsoleCommon; 8 | using ConsoleCommon.Tests; 9 | 10 | namespace ConsoleCommon.Tests 11 | { 12 | [TestFixture] 13 | public class TestCommandInputText 14 | { 15 | [Test] 16 | public void TestStringInput() 17 | { 18 | //string switchName = "", bool required = false, int defaultOrdinal = -1, string helpText = "", bool dontAllowValues = false 19 | ParamsObject _input = (ParamsObject)DynamicTypeCreator 20 | .Create("MyParams") 21 | .AddPassThroughCtors() 22 | .AddAutoProperty("FirstName") 23 | .AddPropertyAttribute(new Type[] { typeof(string), typeof(bool), typeof(int), typeof(string), typeof(bool) }, new object[] { "", false, -1, "", false }) 24 | .AddAutoProperty("LastName") 25 | .AddPropertyAttribute(new Type[] { typeof(string), typeof(bool), typeof(int), typeof(string), typeof(bool) }, new object[] { "", false, -1, "", false }) 26 | .AddAutoProperty("Age") 27 | .AddPropertyAttribute(new Type[] { typeof(string), typeof(bool), typeof(int), typeof(string), typeof(bool) }, new object[] { "", false, -1, "", false }) 28 | .AddAutoProperty("IsItTrue") 29 | .AddPropertyAttribute(new Type[] { typeof(string), typeof(bool), typeof(int), typeof(string), typeof(bool) }, new object[] { "", false, -1, "", false }) 30 | .FinishBuildingType() 31 | .GetConstructor(new Type[] { typeof(string) }) 32 | .Invoke(new object[] { "/FirstName:Yisrael /LastName:Lax /IsItTrue /Age:30" }); 33 | 34 | Assert.IsTrue(_input.GetPropertyValue("FirstName") == "Yisrael"); 35 | Assert.IsTrue(_input.GetPropertyValue("LastName") == "Lax"); 36 | Assert.IsTrue(_input.GetPropertyValue("Age") == 30); 37 | Assert.IsTrue(_input.GetPropertyValue("IsItTrue") == true); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ConsoleCommon.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /ConsoleCommon.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2026 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleCommon", "ConsoleCommon\ConsoleCommon.csproj", "{74F5C3D6-192B-4E59-92B6-B351E286DD80}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleCommon.Tests", "ConsoleCommon.Tests\ConsoleCommon.Tests.csproj", "{401A0951-A83D-419D-855A-AD7E13A7D65C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {74F5C3D6-192B-4E59-92B6-B351E286DD80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {74F5C3D6-192B-4E59-92B6-B351E286DD80}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {74F5C3D6-192B-4E59-92B6-B351E286DD80}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {74F5C3D6-192B-4E59-92B6-B351E286DD80}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {401A0951-A83D-419D-855A-AD7E13A7D65C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {401A0951-A83D-419D-855A-AD7E13A7D65C}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {401A0951-A83D-419D-855A-AD7E13A7D65C}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {401A0951-A83D-419D-855A-AD7E13A7D65C}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {BB924D53-0425-4001-B8A7-AA193E7E8995} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /ConsoleCommon/ConsoleCommon.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {74F5C3D6-192B-4E59-92B6-B351E286DD80} 8 | Library 9 | Properties 10 | ConsoleCommon 11 | ConsoleCommon 12 | v4.0 13 | 512 14 | SAK 15 | SAK 16 | SAK 17 | SAK 18 | 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 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 | 98 | -------------------------------------------------------------------------------- /ConsoleCommon/Entities/InputParameterType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace ConsoleCommon.Entities 7 | { 8 | public enum InputParameterType 9 | { 10 | None, 11 | Switch, 12 | Flag, 13 | Default 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ConsoleCommon/Entities/SwitchOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | 6 | namespace ConsoleCommon.Entities 7 | { 8 | public class SwitchOptions 9 | { 10 | IEnumerable _switchStartChars; 11 | public IEnumerable SwitchStartChars { get { return _switchStartChars; } } 12 | IEnumerable _switchEndChars; 13 | public IEnumerable SwitchEndChars { get { return _switchEndChars; } } 14 | string _switchNameRegex; 15 | public string SwitchNameRegex { get { return _switchNameRegex; } } 16 | string _switchRegex; 17 | public string SwitchRegex { get { return _switchRegex; } } 18 | string _flagSwitchRegex; 19 | public string FlagSwitchRegex { get { return _flagSwitchRegex; } } 20 | public SwitchOptions(IEnumerable switchStartChars, IEnumerable switchEndChars, string switchNameRegex) 21 | { 22 | _switchStartChars = switchStartChars; 23 | _switchEndChars = switchEndChars; 24 | _switchNameRegex = switchNameRegex; 25 | SetSwitchRegex(); 26 | } 27 | private void SetSwitchRegex() 28 | { 29 | if (SwitchStartChars.Count() == 0 || SwitchEndChars.Count() == 0 || SwitchNameRegex.Length == 0) 30 | { 31 | throw new Exception("Switch name regex properties must all be set"); 32 | } 33 | string _startChar = "", _endChar = ""; 34 | //start char 35 | foreach (char c in SwitchStartChars) _startChar += "," + c; 36 | _startChar = "[" + _startChar.Substring(1) + "]{1}"; 37 | //end char 38 | foreach (char c in SwitchEndChars) _endChar += "," + c; 39 | _endChar = "[" + _endChar.Substring(1) + "]{1}"; 40 | //middle 41 | string _middle = string.Format("({0})+", SwitchNameRegex); 42 | //ensure middle can't match start or end char regex: 43 | bool _middleFails = Regex.Match(SwitchStartChars.ElementAt(0).ToString(), _middle).Success; 44 | _middleFails = _middleFails && Regex.Match(SwitchEndChars.ElementAt(0).ToString(), _middle).Success; 45 | if (_middleFails) throw new Exception("Switch name regex cannot match switch start or end characters"); 46 | //whole regex 47 | _switchRegex = string.Format("{0}{1}{2}", _startChar, _middle, _endChar); 48 | _flagSwitchRegex = string.Format("{0}{1}\\z", _startChar, _middle); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ConsoleCommon/Entities/SwitchParameterEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Reflection; 6 | 7 | namespace ConsoleCommon.Entities 8 | { 9 | public class SwitchParameterEntity 10 | { 11 | public string InputString { get; set; } 12 | public string InputValue { get; set; } 13 | public object OutputValue { get; set; } 14 | public PropertyInfo SwitchProperty { get; set; } 15 | public SwitchAttribute SwitchAttribute { get; set; } 16 | public InputParameterType ParameterType { get; set; } 17 | public string SwitchNameFromInput { get; set; } 18 | public Exception MatchException { get; set; } 19 | public Exception PropertySetException { get; set; } 20 | public int? DefaultOrdinal 21 | { 22 | get 23 | { 24 | return SwitchAttribute == null ? null : SwitchAttribute.DefaultOrdinal; 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ConsoleCommon/HelpText/BasicHelpTextParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Reflection; 6 | using ConsoleCommon.Parsing; 7 | 8 | namespace ConsoleCommon.HelpText 9 | { 10 | public class BasicHelpTextParser : IHelpTextParser 11 | { 12 | HelpTextOptions _options; 13 | ITypeParserContainer _parser; 14 | public BasicHelpTextParser(HelpTextOptions options, ITypeParserContainer parser) 15 | { 16 | _options = options; 17 | _parser = parser; 18 | } 19 | public virtual string GetUsage(ParamsObject InputParams) 20 | { 21 | string _appName = System.Reflection.Assembly.GetEntryAssembly()?.Location; 22 | if (_appName != null) 23 | { 24 | _appName = System.IO.Path.GetFileName(_appName); 25 | } 26 | else _appName = "AppName.exe"; 27 | string _usage = _appName + " "; 28 | IEnumerable switchAttrs = getOrderedSwitches(InputParams); 29 | foreach (SwitchAttribute switchAttr in switchAttrs) 30 | { 31 | string _name = switchAttr.SwitchName; 32 | _usage += string.Format("/{0}:\"{1}\" ", _name.ToUpper(), InputParams.GetPropertyByAttribute(switchAttr).Name); 33 | } 34 | return _usage; 35 | } 36 | public virtual string GetSwitchHelp(ParamsObject InputParams) 37 | { 38 | int _atLeastOne = 0; 39 | IEnumerable switchAttrs = getOrderedSwitches(InputParams); 40 | ///If allowed to call with no default ordinal 41 | bool defaultOrdinalAllowed = switchAttrs.Where(sa => sa.Required && sa.DefaultOrdinal == null).Count() == 0; 42 | string _defaultOrdinalMessage = ""; 43 | if (!defaultOrdinalAllowed) 44 | { 45 | _defaultOrdinalMessage = "Anonymous parameters not allowed."; 46 | } 47 | else 48 | { 49 | _defaultOrdinalMessage = "Anonymous parameters must be passed in their default ordinal, as listed below."; 50 | } 51 | string _help = _defaultOrdinalMessage + Environment.NewLine; 52 | foreach (SwitchAttribute switchAttr in switchAttrs) 53 | { 54 | string _name = ""; 55 | string _description = switchAttr.HelpText; 56 | if (string.IsNullOrWhiteSpace(_description)) 57 | { 58 | SwitchHelpTextAttribute _helpText = InputParams.GetPropertyByAttribute(switchAttr).GetCustomAttribute(); 59 | if (_helpText == null) continue; 60 | _name = _helpText.Name; 61 | _description = _helpText.Description; 62 | } 63 | _atLeastOne++; 64 | string _isoptional = switchAttr.Required ? "Required" : "Optional"; 65 | string _acceptedValues = ""; 66 | string _noDefaultOrdinal = defaultOrdinalAllowed && switchAttr.DefaultOrdinal.HasValue ? "" : "No default ordinal. "; 67 | string[] _acceptedValArr = _parser.GetAcceptedValues(InputParams.GetPropertyByAttribute(switchAttr).PropertyType); 68 | if (_acceptedValArr.Length == 0) _acceptedValArr = switchAttr.SwitchValues; 69 | if (_acceptedValArr.Length > 0) 70 | { 71 | _acceptedValues = _acceptedValArr.Length == 0 ? "" : string.Format("Accepted Values: {0}`{1}`.", 72 | string.Concat( 73 | _acceptedValArr 74 | .Take(_acceptedValArr.Length - 1) 75 | .Select(s => "`" + s.ToUpper() + "`| ") 76 | ), 77 | _acceptedValArr.ElementAt(_acceptedValArr.Length - 1).ToUpper()); 78 | } 79 | if (string.IsNullOrEmpty(_name)) _name = switchAttr.SwitchName; 80 | if (string.IsNullOrEmpty(_name)) _name = InputParams.GetPropertyByAttribute(switchAttr)?.Name; 81 | _name = _name.ToUpper(); 82 | _help += Environment.NewLine + string.Format("/{0," + (_options.HelpTextIndentLength * -1).ToString() + "} {1}. {2}{3}. {4}", _name + ": ", _isoptional, _noDefaultOrdinal, _description, _acceptedValues); 83 | } 84 | _help += Environment.NewLine; 85 | return _help; 86 | } 87 | public virtual string GetHelp(ParamsObject InputParams) 88 | { 89 | string _helptext = ""; 90 | string _dash = ""; 91 | for (int i = 0; i < _options.HelpTextLength; i++) _dash += "-"; 92 | _helptext += _dash; 93 | //Sort 94 | IEnumerable helpMethods = getHelpMethodsSorted(InputParams); 95 | foreach (PropertyInfo prop in helpMethods) 96 | { 97 | string _propText = ""; 98 | string _name = ""; 99 | HelpTextAttribute attr = prop.GetCustomAttribute(); 100 | if (typeof(SwitchHelpTextAttribute).IsAssignableFrom(attr.GetType())) 101 | { 102 | continue; 103 | } 104 | else 105 | { 106 | _name = attr.Name; 107 | if (string.IsNullOrEmpty(_name)) _name = prop.Name; 108 | _propText = prop.GetValue(InputParams, null).ToString(); 109 | } 110 | _name = _name.ToUpper(); 111 | _helptext += Environment.NewLine + string.Format("{0," + (_options.HelpTextIndentLength * -1).ToString() + "} {1}", _name + ":", _propText); 112 | } 113 | _helptext += _dash; 114 | return string.Join(Environment.NewLine, GetLines(_helptext, 28, _options.HelpTextLength)); 115 | } 116 | public virtual string GetHelpIfNeeded(string[] args, ParamsObject InputParams) 117 | { 118 | bool _needsHelp = args != null && args.Count() > 0 && _options.HelpCommands.Select(c=>c.ToUpper()).Contains(args[0].ToUpper()); 119 | if (_needsHelp) return GetHelp(InputParams); 120 | else return string.Empty; 121 | } 122 | #region Helper Methods 123 | protected IEnumerable getOrderedSwitches(ParamsObject inputParams) 124 | { 125 | IEnumerable allAttrs = 126 | inputParams.GetType().GetProperties() 127 | .Where(pi => pi.GetCustomAttribute() != null) 128 | .Select(pi => pi.GetCustomAttribute()); 129 | 130 | IEnumerable withOrdinals = allAttrs.Where(sa => sa.DefaultOrdinal != null).OrderBy(sa => sa.DefaultOrdinal); 131 | IEnumerable noOrdinals = allAttrs.Where(sa => sa.DefaultOrdinal == null); 132 | return withOrdinals.Concat(noOrdinals); 133 | } 134 | /// 135 | /// Ordinals for properties defined on base class take precedence 136 | /// 137 | /// 138 | protected IEnumerable getHelpMethodsSorted(ParamsObject inputParams) 139 | { 140 | //Sort 141 | List helpMethods = inputParams.GetType().GetProperties() 142 | .Where(p => p.PropertyType.IsAssignableFrom( 143 | typeof(String)) && 144 | p.GetCustomAttribute() != null) 145 | .OrderBy(s => s.GetCustomAttribute().Ordinal) 146 | .ToList(); 147 | 148 | //base class props don't move in ordinal 149 | IEnumerable baseClassMethods = inputParams.GetType().BaseType.GetProperties() 150 | .Where(p => p.PropertyType.IsAssignableFrom( 151 | typeof(String)) && 152 | p.GetCustomAttribute() != null); 153 | 154 | foreach (PropertyInfo pi in baseClassMethods) 155 | { 156 | //find pi in helpMethods 157 | //remove it, then place it in its correct position 158 | HelpTextAttribute attr = pi.GetCustomAttribute(); 159 | PropertyInfo helpMethodPi = helpMethods.Where(p => p.Name == pi.Name).FirstOrDefault(); 160 | bool success = helpMethods.Remove(helpMethodPi); 161 | helpMethods.Insert(attr.Ordinal, helpMethodPi); 162 | } 163 | return helpMethods; 164 | } 165 | protected string GetLines(string text, int wrapMargin, int helpTextLen) 166 | { 167 | int pos, next; 168 | int width = helpTextLen; 169 | StringBuilder sb = new StringBuilder(); 170 | string margin = ""; 171 | for (int i = 0; i < wrapMargin; i++) margin += " "; 172 | 173 | // Lucidity check 174 | if (width < 1) 175 | return text; 176 | 177 | // Parse each line of text 178 | for (pos = 0; pos < text.Length; pos = next) 179 | { 180 | // Find end of line 181 | int eol = text.IndexOf(Environment.NewLine, pos); 182 | if (eol == -1) 183 | next = eol = text.Length; 184 | else 185 | next = eol + Environment.NewLine.Length; 186 | 187 | // Copy this line of text, breaking into smaller lines as needed 188 | if (eol > pos) 189 | { 190 | do 191 | { 192 | int len = eol - pos; 193 | if (len > width) 194 | len = BreakLine(text, pos, width); 195 | sb.Append(text, pos, len); 196 | sb.Append(Environment.NewLine); 197 | 198 | // Trim whitespace following break 199 | pos += len; 200 | if (pos < eol) sb.Append(margin); 201 | while (pos < eol && Char.IsWhiteSpace(text[pos])) 202 | pos++; 203 | } while (eol > pos); 204 | } 205 | else sb.Append(Environment.NewLine); // Empty line 206 | } 207 | return sb.ToString(); 208 | } 209 | /// 210 | /// Locates position to break the given line so as to avoid 211 | /// breaking words. 212 | /// 213 | /// String that contains line of text 214 | /// Index where line of text starts 215 | /// Maximum line length 216 | /// The modified line length 217 | protected int BreakLine(string text, int pos, int max) 218 | { 219 | // Find last whitespace in line 220 | int i = max; 221 | while (i >= 0 && !Char.IsWhiteSpace(text[pos + i])) 222 | i--; 223 | 224 | // If no whitespace found, break at maximum length 225 | if (i < 0) 226 | return max; 227 | 228 | // Find start of whitespace 229 | while (i >= 0 && Char.IsWhiteSpace(text[pos + i])) 230 | i--; 231 | 232 | // Return length of text before whitespace 233 | return i + 1; 234 | } 235 | #endregion 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /ConsoleCommon/HelpText/HelpTextAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ConsoleCommon 8 | { 9 | /// 10 | /// Attribute used to decorate a property on a ParamsObject that returns a help text element. 11 | /// 12 | [AttributeUsage(AttributeTargets.Property)] 13 | 14 | public class HelpTextAttribute : Attribute 15 | { 16 | string _name=""; 17 | int _ordinal = -1; 18 | public string Name 19 | { 20 | get { return _name; } 21 | } 22 | public int Ordinal 23 | { 24 | get { return _ordinal; } 25 | } 26 | public HelpTextAttribute(int ordinal, string name="") 27 | { 28 | _ordinal = ordinal; 29 | _name = name; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ConsoleCommon/HelpText/IHelpTextOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace ConsoleCommon.HelpText 7 | { 8 | public class HelpTextOptions 9 | { 10 | public int HelpTextLength { get; set; } 11 | public int HelpTextIndentLength { get; set; } 12 | public IEnumerable HelpCommands { get; set; } 13 | public HelpTextOptions(int helpTextLength, int helpTextIndentLength, IEnumerable helpCommands) 14 | { 15 | HelpTextLength = helpTextLength; 16 | HelpTextIndentLength = helpTextIndentLength; 17 | HelpCommands = helpCommands; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ConsoleCommon/HelpText/IHelpTextParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ConsoleCommon.Parsing; 6 | using System.Reflection; 7 | 8 | namespace ConsoleCommon.HelpText 9 | { 10 | public interface IHelpTextParser 11 | { 12 | string GetUsage(ParamsObject InputParams); 13 | string GetSwitchHelp(ParamsObject InputParams); 14 | string GetHelp(ParamsObject InputParams); 15 | string GetHelpIfNeeded(string[] args, ParamsObject InputParams); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ConsoleCommon/HelpText/SwitchHelpTextAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ConsoleCommon 8 | { 9 | /// 10 | /// Attribute used to decorate a switch property on a ParamsObject to specify help text to return for the switch property descriptions' section. 11 | /// 12 | [AttributeUsage(AttributeTargets.Property)] 13 | public class SwitchHelpTextAttribute : HelpTextAttribute 14 | { 15 | private string _description; 16 | public string Description 17 | { 18 | get { return _description; } 19 | } 20 | public SwitchHelpTextAttribute(string description, string name = "") 21 | : base(0, name) 22 | { 23 | _description = description; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /ConsoleCommon/Helpers/CommandLineHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | 5 | namespace ConsoleCommon.Helpers 6 | { 7 | public static class CommandLineHelpers 8 | { 9 | public static string RemoveAppNameFromArgs(string CommandLine, string CallingDirectory) 10 | { 11 | //return var 12 | string _newCommandLine = ""; 13 | 14 | //Get current dir, app path, app name, app extension: 15 | Assembly _curAssm = Assembly.GetEntryAssembly(); 16 | string _appPath = _curAssm.Location.ToLower(); 17 | string _curDir = CallingDirectory; 18 | string _appName = _curAssm.GetName().Name.ToLower(); 19 | string _ext = Path.GetExtension(_appPath).ToLower(); 20 | 21 | //Find app name at beginning of commandLine text 22 | string _firstParam = ""; 23 | bool _isApp = false; 24 | int _totalChars = 0; 25 | foreach (char _c in CommandLine) 26 | { 27 | _totalChars++; 28 | //ignore double quotes 29 | if (_c == '"') continue; 30 | 31 | _firstParam += _c; 32 | 33 | _isApp = 34 | //4 ways a user might call app from command line 35 | _appPath == $"{Path.Combine(_curDir, _firstParam).ToLower()}" || 36 | _appPath == $"{Path.Combine(_curDir, _firstParam).ToLower()}{_ext}" || 37 | _appPath == _firstParam.ToLower() || 38 | _appPath == $"{_firstParam.ToLower()}{_ext}"; 39 | if (_isApp) break; 40 | } 41 | //If app name is found 42 | if (_isApp) 43 | { 44 | //check for extension 45 | if (_ext.Length > 0 46 | && CommandLine.Length > _totalChars + _ext.Length 47 | && CommandLine.Substring(_totalChars, _ext.Length).ToLower() == _ext) 48 | { 49 | _totalChars += _ext.Length; 50 | } 51 | ////check for end double quote 52 | if (CommandLine.Length > _totalChars 53 | && CommandLine.Substring(_totalChars, 1) == "\"") 54 | { 55 | _totalChars++; 56 | } 57 | if (CommandLine.Length > _totalChars) _newCommandLine = CommandLine.Substring(_totalChars); 58 | } 59 | else _newCommandLine = CommandLine; 60 | return _newCommandLine; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ConsoleCommon/Helpers/ReflectionExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Reflection; 7 | 8 | namespace ConsoleCommon 9 | { 10 | public static class ReflectionExtensionMethods 11 | { 12 | public static T GetCustomAttribute(this PropertyInfo pi) where T: Attribute 13 | { 14 | return pi.GetCustomAttributes(true).Cast().Where(atr => atr.GetType().IsEquivalentTo(typeof(T))).FirstOrDefault() as T; 15 | } 16 | public static IEnumerable GetCustomAttributes(this PropertyInfo pi) where T : Attribute 17 | { 18 | List _list = 19 | pi.GetCustomAttributes(true) 20 | .Cast() 21 | .Where(atr => atr.GetType().IsEquivalentTo(typeof(T))) 22 | .Cast() 23 | .ToList(); 24 | return _list as IEnumerable; 25 | } 26 | public static T GetCustomAttribute(this Type srcType) where T : Attribute 27 | { 28 | object[] _attrs = srcType.GetCustomAttributes(true); 29 | if (_attrs != null && _attrs.Length > 0) 30 | { 31 | return srcType.GetCustomAttributes(true).Cast().Where(atr => atr.GetType().IsEquivalentTo(typeof(T))).FirstOrDefault() as T; 32 | } 33 | else return null; 34 | } 35 | public static bool MatchesAttributeValueOrName(this Type srcType, string fieldName, Func KeySelector) 36 | where TAttribute : Attribute 37 | { 38 | try 39 | { 40 | bool _match = false; 41 | TAttribute _attr = null; 42 | try 43 | { 44 | _attr = srcType.GetCustomAttribute(); 45 | } 46 | catch(Exception ex) 47 | { 48 | throw new Exception("The error occured while GetCustomAttribute happened", ex); 49 | } 50 | if (_attr != null) 51 | { 52 | string _attrFieldName = ""; 53 | try 54 | { 55 | if (KeySelector == null) throw new Exception("Your key selector is null!"); 56 | _attrFieldName = KeySelector(_attr); 57 | } 58 | catch(Exception ex) 59 | { 60 | throw new Exception("The error happened while selecting a key", ex); 61 | } 62 | _match = !string.IsNullOrWhiteSpace(_attrFieldName) && _attrFieldName.ToLower() == fieldName.ToLower(); 63 | } 64 | if (!_match) _match = fieldName.ToLower() == srcType.Name.ToLower(); 65 | return _match; 66 | } 67 | catch(Exception ex) 68 | { 69 | throw new Exception("Error while reflecting", ex); 70 | } 71 | } 72 | public static PropertyInfo GetPropertyByAttribute(this object src, Attribute attr) 73 | { 74 | return src.GetType().GetProperties().Where(pi => pi.GetCustomAttributes(true).Contains(attr)).FirstOrDefault(); 75 | } 76 | public static object GetPropertyValue(this object src, string propName, int index = -1) 77 | { 78 | object[] _index = getPropIndex(index); 79 | return getProp(src, propName).GetValue(src, _index); 80 | } 81 | public static T GetPropertyValue(this object src, string propName, int index = -1) 82 | { 83 | PropertyInfo _prop = getProp(src, propName); 84 | if (!_prop.PropertyType.IsAssignableFrom(typeof(T))) throw new Exception($"Property '{propName}' does not match type '{typeof(T).Name}'"); 85 | object[] _index = getPropIndex(index); 86 | object _propVal = _prop.GetValue(src, _index); 87 | 88 | //return null if null 89 | if (typeof(T).IsClass && _propVal == null) return default(T); 90 | 91 | //return 92 | return (T)_propVal; 93 | } 94 | public static bool CompareValues(object val1, object val2) 95 | { 96 | bool _isEqual = false; 97 | if (val1 == null || val2 == null) 98 | { 99 | _isEqual = val1 == null && val2 == null; 100 | } 101 | else if(val1!=null && val2!=null && val1.GetType().IsAssignableFrom(typeof(string))) 102 | { 103 | _isEqual = val1.ToString() == val2.ToString(); 104 | } 105 | else 106 | { 107 | _isEqual = val1 == val2; 108 | } 109 | return _isEqual; 110 | } 111 | private static PropertyInfo getProp(object src, string propName) 112 | { 113 | PropertyInfo _prop = src.GetType().GetProperties().FirstOrDefault(p=>p.Name == propName); 114 | if (_prop == null) throw new Exception($"Property '{propName}' not found"); 115 | return _prop; 116 | } 117 | private static object[] getPropIndex(int index = -1) 118 | { 119 | object[] _index = null; 120 | if (index > 0) _index = new object[] { index }; 121 | return _index; 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /ConsoleCommon/Helpers/TypesByInheritanceLevelComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace ConsoleCommon.Helpers 7 | { 8 | public class TypesByInheritanceLevelComparer : IComparer 9 | { 10 | public int Compare(Type x, Type y) 11 | { 12 | Type _yBaseType = y; 13 | int _compare = 0; 14 | int _counter = 0; 15 | bool _foundMatch = false; 16 | while (_yBaseType != null) 17 | { 18 | if (x == _yBaseType) 19 | { 20 | _foundMatch = true; 21 | break; 22 | } 23 | _yBaseType = _yBaseType.BaseType; 24 | _counter++; 25 | } 26 | //before or same 27 | if (_foundMatch) 28 | { 29 | if (_counter != 0) _compare = -1; 30 | } 31 | else _compare = 1; //after 32 | return _compare; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ConsoleCommon/ParamsObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using System.Threading.Tasks; 6 | using System.Reflection; 7 | using System.Drawing; 8 | using System.Text; 9 | using ConsoleCommon.Entities; 10 | using ConsoleCommon.Parsing; 11 | using ConsoleCommon.HelpText; 12 | using ConsoleCommon.Parsing.TypeParsers; 13 | using ConsoleCommon.Helpers; 14 | 15 | namespace ConsoleCommon 16 | { 17 | public abstract class ParamsObject 18 | { 19 | #region Fields and Properties 20 | protected string[] args; 21 | private Dictionary, string> _paramExceptionDictionary; 22 | private IEnumerable _switchMembers 23 | { 24 | get 25 | { 26 | return this.GetType().GetProperties().Where(pi => pi.GetCustomAttributes().Count() > 0); 27 | } 28 | } 29 | 30 | #region Help Options 31 | public virtual int HelpTextLength 32 | { 33 | get { return 160; } 34 | } 35 | public virtual int HelpTextIndentLength 36 | { 37 | get { return 22; } 38 | } 39 | public virtual List HelpCommands 40 | { 41 | get { return new List { "/?", "help", "/help" }; } 42 | } 43 | protected virtual IHelpTextParser HelpTextParser { get { return _defaultHelpTextParser; } } 44 | HelpTextOptions _helpOptions; 45 | IHelpTextParser _defaultHelpTextParser; 46 | #endregion 47 | 48 | #region Parsing Options 49 | SwitchOptions _defaultSwitchOptions; 50 | public virtual SwitchOptions Options { get { return _defaultSwitchOptions; } } 51 | 52 | ISwitchParser _defaultSwitchParser; 53 | protected virtual ISwitchParser SwitchParser { get { return _defaultSwitchParser; } } 54 | 55 | ITypeParserContainer _defaultTypeParserContainer; 56 | protected virtual ITypeParserContainer TypeParser { get { return _defaultTypeParserContainer; } } 57 | 58 | IArgumentCreator _defaultArgMaker; 59 | protected virtual IArgumentCreator ArgMaker { get { return _defaultArgMaker; } } 60 | #endregion 61 | 62 | #endregion 63 | 64 | #region Constructors 65 | private void Initialize() 66 | { 67 | _defaultSwitchOptions = new SwitchOptions(new List { '/' }, new List { ':' }, "[_A-Za-z]+[_A-Za-z0-9]*"); 68 | _defaultTypeParserContainer = new DefaultTypeContainer(); 69 | _defaultSwitchParser = new SwitchParser(TypeParser, this); 70 | _helpOptions = new HelpTextOptions(HelpTextLength, HelpTextIndentLength, HelpCommands); 71 | _defaultHelpTextParser = new BasicHelpTextParser(_helpOptions, TypeParser); 72 | _defaultArgMaker = new DefaultArgumentCreator(); 73 | } 74 | private void PostInitialize() 75 | { 76 | if (GetHelpIfNeeded() == string.Empty) 77 | { 78 | SwitchParser.ParseSwitches(args); 79 | } 80 | _paramExceptionDictionary = new Dictionary, string>(); 81 | AddAdditionalParamChecks(); 82 | foreach (var item in GetParamExceptionDictionary()) _paramExceptionDictionary.Add(item.Key, item.Value); 83 | } 84 | public ParamsObject() : this( 85 | CommandLineHelpers.RemoveAppNameFromArgs(Environment.CommandLine, Environment.CurrentDirectory)) 86 | { 87 | } 88 | public ParamsObject(string commandText) 89 | { 90 | try 91 | { 92 | Initialize(); 93 | this.args = ArgMaker.GetArgs(commandText, Options, HelpTextParser); 94 | PostInitialize(); 95 | } 96 | catch (Exception ex) 97 | { 98 | throw ex; 99 | } 100 | } 101 | public ParamsObject(string[] args) 102 | { 103 | try 104 | { 105 | Initialize(); 106 | this.args = args; 107 | PostInitialize(); 108 | } 109 | catch (Exception ex) 110 | { 111 | throw ex; 112 | } 113 | } 114 | #endregion 115 | 116 | #region Fill Methods 117 | public virtual Dictionary, string> GetParamExceptionDictionary() 118 | { 119 | return new Dictionary, string>(); 120 | } 121 | #endregion 122 | 123 | #region Process Flow Methods 124 | private void AddAdditionalParamChecks() 125 | { 126 | foreach (PropertyInfo pi in _switchMembers) 127 | { 128 | AddRequiredCheck(pi); 129 | AddValueListCheck(pi); 130 | } 131 | } 132 | private void AddRequiredCheck(PropertyInfo switchMember) 133 | { 134 | SwitchAttribute mySwitch = switchMember.GetCustomAttribute(); 135 | Func isRequiredFilled = new Func(() => mySwitch.Required && switchMember.GetValue(this, null) == null); 136 | _paramExceptionDictionary.Add(isRequiredFilled, string.Format("Parameter {0} is required!", switchMember.Name)); 137 | } 138 | private void AddValueListCheck(PropertyInfo switchMember) 139 | { 140 | SwitchAttribute mySwitch = switchMember.GetCustomAttribute(); 141 | Func isRestrictedValue = new Func(() => { 142 | 143 | bool _hasSwitchVals = mySwitch.SwitchValues.Length != 0; 144 | //is null? 145 | bool _isNull = !_hasSwitchVals || _hasSwitchVals && switchMember.GetValue(this, null) == null; 146 | 147 | //or is a Type and Type.FriendlyName = a switch value? 148 | bool _isTypeNameMatch = _isNull 149 | || (!_isNull 150 | && (switchMember.PropertyType.Equals(typeof(Type)) 151 | && mySwitch.SwitchValues.Any(s => 152 | ((Type)switchMember.GetValue(this, null)) 153 | .MatchesAttributeValueOrName(s, attr => attr == null ? "" : attr.FriendlyName)))); 154 | 155 | //or is something else and object.ToString() = a switch value? 156 | bool _isDirectMatch = _isTypeNameMatch || (!_isTypeNameMatch && mySwitch.SwitchValues.Any(s => 157 | s.ToLower() == switchMember.GetValue(this, null).ToString().ToLower())); 158 | 159 | bool _violation = _hasSwitchVals && _isNull || _hasSwitchVals && !_isTypeNameMatch || _hasSwitchVals && !_isDirectMatch; 160 | return _violation; 161 | }); 162 | 163 | _paramExceptionDictionary.Add(isRestrictedValue, string.Format("Invalid value for parameter {0}!", switchMember.Name)); 164 | } 165 | 166 | public void CheckParams() 167 | { 168 | try 169 | { 170 | if (GetHelpIfNeeded() != string.Empty) return; 171 | Exception _ex = SwitchParser.ExceptionList.FirstOrDefault(); 172 | if (_ex != null) throw _ex; 173 | foreach (var item in _paramExceptionDictionary) 174 | { 175 | if (item.Key()) throw new Exception(item.Value); 176 | } 177 | } 178 | catch (Exception ex) 179 | { 180 | throw ex; 181 | } 182 | } 183 | public string GetHelpIfNeeded() 184 | { 185 | return HelpTextParser.GetHelpIfNeeded(args, this); 186 | } 187 | /// 188 | /// Override this method to write custom help text. This will over ride automatic help text generation. 189 | /// 190 | /// 191 | public virtual string GetHelp() 192 | { 193 | return GetHelp2(); 194 | } 195 | 196 | #endregion 197 | 198 | #region Public Methods 199 | public string GetHelp2() 200 | { 201 | 202 | return HelpTextParser.GetHelp(this); 203 | } 204 | 205 | #endregion 206 | 207 | #region Default Help Text Methods 208 | 209 | public virtual string Usage 210 | { 211 | get 212 | { 213 | return HelpTextParser.GetUsage(this); 214 | } 215 | } 216 | public virtual string SwitchHelp 217 | { 218 | get 219 | { 220 | return HelpTextParser.GetSwitchHelp(this); 221 | } 222 | } 223 | 224 | #endregion 225 | } 226 | } -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/ISwitchParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace ConsoleCommon.Parsing 7 | { 8 | public interface ISwitchParser 9 | { 10 | ParamsObject ParseSwitches(string[] args); 11 | IEnumerable ExceptionList { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/ITypeParseContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 6 | 7 | namespace ConsoleCommon.Parsing 8 | { 9 | public interface ITypeParserContainer 10 | { 11 | object Parse(string toParse, Type type); 12 | string[] GetAcceptedValues(Type type); 13 | ITypeParser GetParser(Type typeToParse); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/PrimitiveParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 7 | 8 | namespace ConsoleCommon.Parsing 9 | { 10 | public class PrimitiveParser : ITypeParserContainer 11 | { 12 | #region Implementation 13 | public object Parse(string toParse, Type type) 14 | { 15 | if (type.IsInterface) throw new Exception("Cannot parse interfaces. Must use concrete types"); 16 | if (type.IsArray) 17 | { 18 | return ParseArray(toParse, type); 19 | } 20 | else return ParseElement(toParse, type); 21 | } 22 | public string[] GetAcceptedValues(Type type) 23 | { 24 | string[] _acceptedVals = new string[0]; 25 | Type myUnderLyingType = type; 26 | if (type.IsArray) myUnderLyingType = type.GetElementType(); 27 | bool isNullable = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); 28 | if (isNullable) myUnderLyingType = Nullable.GetUnderlyingType(myUnderLyingType); 29 | 30 | if (myUnderLyingType.IsAssignableFrom(typeof(bool))) 31 | { 32 | _acceptedVals = GetBoolAcceptedValues(); 33 | } 34 | else if (myUnderLyingType.IsEnum) 35 | { 36 | _acceptedVals = GetEnumAcceptedValues(myUnderLyingType); 37 | } 38 | return _acceptedVals; 39 | } 40 | #endregion 41 | 42 | #region Parse Methods 43 | private object ParseArray(string toParse, Type type) 44 | { 45 | Type elementType = type.GetElementType(); 46 | //Comma delimited, or comma+space delimited 47 | string[] splits = toParse.Split(new string[] { ","}, StringSplitOptions.RemoveEmptyEntries); 48 | splits = splits.Select(s => s.Trim()).ToArray(); 49 | Type[] myTypes = new Type[] { typeof(int) }; 50 | object[] myReqs = new object[] { splits.Length }; 51 | 52 | Array returnVals = type.GetConstructor(myTypes).Invoke(myReqs) as Array; 53 | 54 | //object[] returnVals = new object[splits.Length]; 55 | for (int i = 0; i < splits.Length; i++) 56 | { 57 | string split = splits[i]; 58 | object myElement = Parse(split, elementType); 59 | returnVals.SetValue(myElement, i); 60 | } 61 | return returnVals; 62 | } 63 | private object ParseElement(string toParse, Type type) 64 | { 65 | object myVal = null; 66 | Type myPropType = type; 67 | Type myUnderLyingType = myPropType; 68 | bool isNullable = myPropType.IsGenericType && myPropType.GetGenericTypeDefinition() == typeof(Nullable<>); 69 | 70 | if (isNullable) 71 | { 72 | myUnderLyingType = Nullable.GetUnderlyingType(myPropType); 73 | } 74 | if (myUnderLyingType.IsEnum) 75 | { 76 | if (!_parseEnumAsArray && myUnderLyingType.GetCustomAttribute() != null) 77 | { 78 | _parseEnumAsArray = true; 79 | Type _enumArrayType = myUnderLyingType.MakeArrayType(); 80 | Array _enumArray = ParseArray(toParse, _enumArrayType) as Array; 81 | int _totalVal = 0; 82 | int _iter = 0; 83 | 84 | foreach (object enVal in _enumArray) 85 | { 86 | if (_iter == 0) _totalVal = (int)enVal; 87 | else 88 | { 89 | _totalVal = _totalVal | (int)enVal; 90 | } 91 | _iter++; 92 | } 93 | myVal = _totalVal; 94 | _parseEnumAsArray = false; 95 | } 96 | else myVal = Enum.Parse(myUnderLyingType, toParse, true); 97 | } 98 | else if (typeof(System.Security.SecureString).Equals(myUnderLyingType)) 99 | { 100 | var secure = new System.Security.SecureString(); 101 | 102 | foreach (var v in toParse.ToCharArray()) 103 | { 104 | secure.AppendChar(v); 105 | } 106 | myVal = secure; 107 | } 108 | else if(typeof(KeyValuePair<,>).Name.Equals(myUnderLyingType.Name)) 109 | { 110 | Type[] _genericTypes = myUnderLyingType.GetGenericArguments(); 111 | string[] _keyValArr = toParse.Split(':'); 112 | if (_keyValArr == null || _keyValArr.Length != 2) throw new Exception("Failed to parse key value pair"); 113 | object _key = Parse(_keyValArr[0], _genericTypes[0]); 114 | object _val = Parse(_keyValArr[1], _genericTypes[1]); 115 | if (_key == null || _val == null) throw new Exception("Failed to parse key value pair"); 116 | object[] _keyValPair = new object[] { _key, _val }; 117 | myVal = myUnderLyingType.GetConstructor(_genericTypes).Invoke(_keyValPair); 118 | } 119 | else if (typeof(IConvertible).IsAssignableFrom(myUnderLyingType)) 120 | { 121 | if (myUnderLyingType == typeof(bool)) 122 | { 123 | if (BoolFalseValues.Contains(toParse?.ToLower().Trim())) 124 | { 125 | return false; 126 | } 127 | else if (BoolTrueValues.Contains(toParse?.ToLower().Trim())) 128 | { 129 | return true; 130 | } 131 | } 132 | else myVal = Convert.ChangeType(toParse, myUnderLyingType); 133 | } 134 | else if (typeof(Type).IsAssignableFrom(myUnderLyingType)) 135 | { 136 | IEnumerable _types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(asm => asm.GetTypes()); 137 | myVal = _types.SkipWhile(t => 138 | (t.GetCustomAttribute() != null && 139 | t.GetCustomAttribute().Ignore)) 140 | .Where(t => t.MatchesAttributeValueOrName(toParse, attr => (attr == null || string.IsNullOrWhiteSpace(attr.FriendlyName)) ? "" : attr.FriendlyName.ToLower())).FirstOrDefault(); 141 | } 142 | return myVal; 143 | } 144 | #endregion 145 | 146 | #region Private Fields 147 | bool _parseEnumAsArray = false; 148 | #endregion 149 | 150 | #region Helper Methods 151 | private string[] BoolFalseValues 152 | { 153 | get 154 | { 155 | return new string[] { "n", "no", "false", "f", "off" }; 156 | } 157 | } 158 | private string[] BoolTrueValues 159 | { 160 | get 161 | { 162 | return new string[] { "y", "yes", "true", "t", "on", null,"" }; 163 | } 164 | } 165 | 166 | private string[] GetBoolAcceptedValues() 167 | { 168 | return BoolFalseValues.Where(s=>s!=null).Concat(BoolTrueValues.Where(s=>s!=null)).ToArray(); 169 | } 170 | private string[] GetEnumAcceptedValues(Type enumType) 171 | { 172 | return Enum.GetNames(enumType); 173 | } 174 | 175 | public ITypeParser GetParser(Type typeToParse) 176 | { 177 | throw new NotImplementedException(); 178 | } 179 | #endregion 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/StringSwitchParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Reflection; 6 | using System.Text.RegularExpressions; 7 | using ConsoleCommon.Entities; 8 | using ConsoleCommon.HelpText; 9 | 10 | namespace ConsoleCommon.Parsing 11 | { 12 | public interface IArgumentCreator 13 | { 14 | string[] GetArgs(string args, SwitchOptions _options, IHelpTextParser HelpTextParser); 15 | string[] GetArgs(string args, SwitchOptions _options); 16 | } 17 | class BlankParamsObject : ParamsObject 18 | { 19 | public BlankParamsObject() : base(new string[0] { }) { } 20 | } 21 | public class DefaultArgumentCreator : IArgumentCreator 22 | { 23 | #region Separation 24 | public string[] GetArgs(string args, SwitchOptions _options, IHelpTextParser HelpTextParser) 25 | { 26 | //check for help 27 | //if not needed, parse args 28 | string[] _checkForHelp = args.Split(' '); 29 | if (_checkForHelp.Length > 0 && !string.IsNullOrWhiteSpace(HelpTextParser.GetHelpIfNeeded(_checkForHelp, new BlankParamsObject()))) 30 | { 31 | return _checkForHelp; 32 | } 33 | else return GetArgs(args, _options); 34 | } 35 | public string[] GetArgs(string args, SwitchOptions _options) 36 | { 37 | //Two types of args: 38 | //1) no switch (using default ordinal) 39 | //2) has switch-value pair 40 | 41 | List _matchArgs = new List(); 42 | 43 | string _newFlagRegex = _options.FlagSwitchRegex; 44 | if (_newFlagRegex.EndsWith("\\z")) _newFlagRegex = _newFlagRegex.Substring(0, _newFlagRegex.Length - 2); 45 | 46 | string _switchOrFlagRegex = $"({_options.SwitchRegex}|{_newFlagRegex})"; 47 | Match _match = Regex.Match(args, _switchOrFlagRegex); 48 | //_match = Regex.Match(args, $"({_options.FlagSwitchRegex})"); 49 | int _lastStartIndex = 0; 50 | int _lastEndIndex = 0; 51 | Match _lastMatch = _match; 52 | 53 | while(_match.Success) 54 | { 55 | _match = _match.NextMatch(); 56 | if (_match.Success) 57 | { 58 | _lastEndIndex = _match.Index; 59 | _matchArgs.Add(args.Substring(_lastStartIndex, _lastEndIndex-_lastStartIndex).Trim()); 60 | _lastStartIndex = _lastEndIndex; 61 | _lastMatch = _match; 62 | } 63 | else 64 | { 65 | _matchArgs.Add(args.Substring(_lastStartIndex, args.Length - _lastStartIndex).Trim()); 66 | } 67 | } 68 | return _matchArgs.ToArray(); 69 | } 70 | 71 | #endregion 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/SwitchParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Reflection; 6 | using System.Text.RegularExpressions; 7 | using ConsoleCommon.Entities; 8 | 9 | namespace ConsoleCommon.Parsing 10 | { 11 | public class SwitchParser : ISwitchParser 12 | { 13 | protected ITypeParserContainer _typeParser; 14 | protected List _availableSwitchProps; 15 | protected List _inputSwitches; 16 | public List InputSwitches { get { return _inputSwitches; } } 17 | protected SwitchOptions _options; 18 | protected ParamsObject _paramsObject; 19 | 20 | public SwitchParser(ITypeParserContainer typeParser, ParamsObject paramsObject) 21 | { 22 | _typeParser = typeParser; 23 | _paramsObject = paramsObject; 24 | GetSwitchOptions(); 25 | GetAvailableSwitchProperties(); 26 | } 27 | 28 | public ParamsObject ParseSwitches(string[] args) 29 | { 30 | SeparateSwitchesFlagsDefaults(args); 31 | SetPropertyValues(); 32 | return _paramsObject; 33 | } 34 | 35 | public IEnumerable ExceptionList 36 | { 37 | get 38 | { 39 | List _exes = new List(); 40 | foreach(SwitchParameterEntity _switch in _inputSwitches) 41 | { 42 | if (_switch.MatchException != null) _exes.Add(_switch.MatchException); 43 | if (_switch.PropertySetException != null) _exes.Add(_switch.PropertySetException); 44 | } 45 | return _exes; 46 | } 47 | } 48 | protected virtual void SetPropertyValues() 49 | { 50 | foreach(SwitchParameterEntity _switch in _inputSwitches) 51 | { 52 | if (_switch.MatchException != null) continue; 53 | try 54 | { 55 | object _propVal = _typeParser.Parse(_switch.InputValue, _switch.SwitchProperty.PropertyType); 56 | _switch.SwitchProperty.SetValue(_paramsObject, _propVal, new object[0]); 57 | } 58 | catch(Exception ex) 59 | { 60 | _switch.PropertySetException = ex; 61 | } 62 | } 63 | } 64 | 65 | #region Separation 66 | protected virtual void SeparateSwitchesFlagsDefaults(string[] args) 67 | { 68 | //Three types of args: 69 | //1) no switch (using default ordinal) 70 | //2) has switch-value pair 71 | //3) has only switch, no value (ex: /SearchByName) 72 | _inputSwitches = new List(); 73 | foreach (string _arg in args) 74 | { 75 | SwitchParameterEntity _switchProp = null; 76 | //is switch? 77 | Match _match = Regex.Match(_arg, _options.SwitchRegex); 78 | if (_match.Success) 79 | { 80 | _switchProp = GetSwitchOrFlag(_arg, _match, false); 81 | } 82 | else 83 | { 84 | //is flag? 85 | _match = Regex.Match(_arg, _options.FlagSwitchRegex); 86 | if (_match.Success) _switchProp = GetSwitchOrFlag(_arg, _match, true); 87 | } 88 | //default ordinal 89 | if(!_match.Success) _switchProp = GetDefault(_arg); 90 | _inputSwitches.Add(_switchProp); 91 | } 92 | //GetDefault() doesn't get the properties for default args. 93 | GetDefaults(); 94 | } 95 | protected virtual SwitchParameterEntity GetSwitchOrFlag(string arg, Match match, bool isFlag) 96 | { 97 | SwitchParameterEntity _newSwitch = new SwitchParameterEntity() 98 | { 99 | InputString = arg, 100 | ParameterType = isFlag ? InputParameterType.Flag : InputParameterType.Switch 101 | }; 102 | int _length = match.Value.Length - 1; 103 | if (!isFlag) 104 | { 105 | _length--; 106 | _newSwitch.InputValue = arg.Substring(match.Length); 107 | } 108 | _newSwitch.SwitchNameFromInput = match.Value.Substring(1, _length).ToUpper(); 109 | SwitchParameterEntity _pe = _availableSwitchProps.FirstOrDefault(s => s.SwitchAttribute.SwitchName.ToUpper() == _newSwitch.SwitchNameFromInput); 110 | if (_pe == null) _pe = _availableSwitchProps.FirstOrDefault(s => s.SwitchProperty.Name.ToUpper() == _newSwitch.SwitchNameFromInput); 111 | if (_pe != null) 112 | { 113 | _newSwitch.SwitchAttribute = _pe.SwitchAttribute; 114 | _newSwitch.SwitchProperty = _pe.SwitchProperty; 115 | } 116 | else _newSwitch.MatchException = new Exception("Invalid switch!"); 117 | return _newSwitch; 118 | } 119 | protected virtual SwitchParameterEntity GetDefault(string arg) 120 | { 121 | SwitchParameterEntity _newSwitch = new SwitchParameterEntity() 122 | { 123 | InputString = arg, 124 | ParameterType = InputParameterType.Default, 125 | InputValue = arg 126 | }; 127 | return _newSwitch; 128 | } 129 | protected virtual void GetDefaults() 130 | { 131 | try 132 | { 133 | IEnumerable _defaults = 134 | _availableSwitchProps 135 | .Where(s => s.SwitchAttribute.DefaultOrdinal != null) 136 | .OrderBy(s => s.SwitchAttribute.DefaultOrdinal); 137 | 138 | IEnumerable _inputDefaults = _inputSwitches.Where(s => s.ParameterType == InputParameterType.Default); 139 | 140 | //if any default ordinals are used, all switches must appear after default ordinals 141 | //All parameter default ordinal properties must have an ordinal value that precedes the ordinal value of any switches 142 | bool _hasDefaults = _inputDefaults.Count() > 0; 143 | if (!_hasDefaults) return; 144 | int _default = -1; 145 | bool _nonDefaultParamAppeared = false; 146 | foreach (SwitchParameterEntity _switch in _inputSwitches) 147 | { 148 | if (_switch.ParameterType != InputParameterType.Default) 149 | { 150 | _nonDefaultParamAppeared = true; 151 | if (_default == -1) 152 | { 153 | _switch.MatchException = new Exception("Parameters with default ordinals must precede switches!"); 154 | } 155 | else if ( 156 | _switch.DefaultOrdinal.HasValue && 157 | _inputDefaults.Any(i => i.DefaultOrdinal.HasValue && 158 | i.DefaultOrdinal.Value >= _switch.DefaultOrdinal.Value)) 159 | { 160 | _switch.MatchException = new Exception( 161 | "Switched parameters with default ordinals can only be used along with non-switched parameters when their default ordinal is greater than the non-switched parameters' default ordinals"); 162 | } 163 | } 164 | else if (_switch.ParameterType == InputParameterType.Default) 165 | { 166 | if(_nonDefaultParamAppeared) 167 | { 168 | _switch.MatchException = new Exception("Parameters with default ordinals must precede switches!"); 169 | break; 170 | } 171 | _default++; 172 | if (_default > _inputSwitches.Count - 1) 173 | { 174 | _switch.MatchException = new Exception("Too many switches specified!"); 175 | } 176 | else 177 | { 178 | SwitchParameterEntity _defaultParam = _defaults.ElementAt(_default); 179 | _switch.SwitchAttribute = _defaultParam.SwitchAttribute; 180 | _switch.SwitchProperty = _defaultParam.SwitchProperty; 181 | } 182 | } 183 | } 184 | } 185 | catch 186 | { 187 | throw; 188 | } 189 | } 190 | #endregion 191 | 192 | #region Initialization 193 | protected virtual void GetAvailableSwitchProperties() 194 | { 195 | _availableSwitchProps = new List(); 196 | foreach(PropertyInfo _pi in _paramsObject.GetType().GetProperties()) 197 | { 198 | SwitchAttribute _attr = _pi.GetCustomAttribute(); 199 | if (_attr != null) _availableSwitchProps.Add(new SwitchParameterEntity { SwitchProperty = _pi, SwitchAttribute = _attr }); 200 | } 201 | } 202 | protected virtual void GetSwitchOptions() 203 | { 204 | _options = new SwitchOptions(_paramsObject.Options.SwitchStartChars, _paramsObject.Options.SwitchEndChars, _paramsObject.Options.SwitchNameRegex); 205 | } 206 | #endregion 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/TypeParserContainerBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ConsoleCommon.Helpers; 6 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 7 | 8 | namespace ConsoleCommon.Parsing.TypeParsers 9 | { 10 | public abstract class TypeParserContainerBase : ITypeParserContainer 11 | { 12 | protected abstract List _typeParsers { get; } 13 | public IEnumerable TypeParsers => _typeParsers.AsReadOnly(); 14 | 15 | #region Implementation 16 | public object Parse(string toParse, Type type) 17 | { 18 | if (type.IsInterface) throw new Exception("Cannot parse interfaces. Must use concrete types"); 19 | return GetParser(type).Parse(toParse, type, this); 20 | } 21 | public string[] GetAcceptedValues(Type type) 22 | { 23 | return GetParser(type).GetAcceptedValues(type); 24 | } 25 | public ITypeParser GetParser(Type typeToParse) 26 | { 27 | var _comparer = new TypesByInheritanceLevelComparer(); 28 | Type[] _genArgs = typeToParse.IsGenericType ? typeToParse.GetGenericArguments() : new Type[0]; 29 | ITypeParser _parser = 30 | TypeParsers 31 | .Where(t => t.GetTypeToParse(_genArgs).IsAssignableFrom(typeToParse)) 32 | .OrderBy(t => t.GetTypeToParse(_genArgs), _comparer) 33 | .LastOrDefault(); 34 | if (_parser == null) throw new Exception($"Parsing type '{typeToParse.Name}' not handled"); 35 | return _parser; 36 | } 37 | #endregion 38 | 39 | #region Helpers 40 | private void addTypeParser(ITypeParser typeParser, bool overwriteDupes) 41 | { 42 | ITypeParser _match = TypeParsers.FirstOrDefault(tp => tp.GetTypeToParse().Equals(typeParser.GetTypeToParse())); 43 | if (_match != null) 44 | { 45 | if (overwriteDupes) 46 | { 47 | _typeParsers.Remove(_match); 48 | } 49 | else throw new Exception($"Type parser of type '{typeParser.GetTypeToParse()}' already exists"); 50 | } 51 | _typeParsers.Add(typeParser); 52 | } 53 | #endregion 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/TypeParsers/Concrete/ArrayParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 6 | 7 | namespace ConsoleCommon.Parsing.TypeParsers 8 | { 9 | public class ArrayParser : TypeParserBase 10 | { 11 | public override object Parse(string toParse, Type typeToParse, ITypeParserContainer parserContainer) 12 | { 13 | Type elementType = typeToParse.GetElementType(); 14 | //Comma delimited, or comma+space delimited 15 | string[] splits = toParse.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); 16 | splits = splits.Select(s => s.Trim()).ToArray(); 17 | Type[] myTypes = new Type[] { typeof(int) }; 18 | object[] myReqs = new object[] { splits.Length }; 19 | 20 | Array returnVals = typeToParse.GetConstructor(myTypes).Invoke(myReqs) as Array; 21 | 22 | //object[] returnVals = new object[splits.Length]; 23 | for (int i = 0; i < splits.Length; i++) 24 | { 25 | string split = splits[i]; 26 | object myElement = ParseElement(split, elementType, parserContainer); 27 | returnVals.SetValue(myElement, i); 28 | } 29 | return returnVals; 30 | } 31 | private object ParseElement(string toParse, Type type, ITypeParserContainer parserContainer) 32 | { 33 | ITypeParser _parser = parserContainer.GetParser(type); 34 | if (_parser == null) throw new Exception($"Parsing type '{type.Name}' not handled"); 35 | return _parser.Parse(toParse, type, parserContainer); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/TypeParsers/Concrete/BoolParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 6 | 7 | namespace ConsoleCommon.Parsing.TypeParsers 8 | { 9 | public class BoolParser : TypeParserBase 10 | { 11 | public override object Parse(string toParse, Type typeToParse, ITypeParserContainer parserContainer) 12 | { 13 | if (BoolFalseValues.Contains(toParse?.ToLower().Trim())) 14 | { 15 | return false; 16 | } 17 | else if (BoolTrueValues.Contains(toParse?.ToLower().Trim())) 18 | { 19 | return true; 20 | } 21 | else throw new Exception("Boolean value invalid"); 22 | } 23 | private string[] BoolFalseValues => new string[] { "n", "no", "false", "f", "off" }; 24 | private string[] BoolTrueValues => new string[] { "y", "yes", "true", "t", "on", null, "" }; 25 | public override string[] GetAcceptedValues(Type typeToParse) 26 | { 27 | return BoolFalseValues.Where(s => s != null).Concat(BoolTrueValues.Where(s => s != null)).ToArray(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/TypeParsers/Concrete/DefaultTypeContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace ConsoleCommon.Parsing.TypeParsers 7 | { 8 | public class DefaultTypeContainer : TypeParserContainer 9 | { 10 | public DefaultTypeContainer() : base(true, 11 | new ArrayParser(), new BoolParser(), new EnumParser(), 12 | new KeyValueParser(), new ObjectParser(), 13 | new NullableParser(), 14 | new SecureStringParser(), new TypeTypeParser()) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/TypeParsers/Concrete/EnumParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 6 | 7 | namespace ConsoleCommon.Parsing.TypeParsers 8 | { 9 | public class EnumParser : TypeParserBase 10 | { 11 | bool _parseEnumAsArray = false; 12 | public override object Parse(string toParse, Type typeToParse, ITypeParserContainer parserContainer) 13 | { 14 | object _returnVal; 15 | if (!_parseEnumAsArray && typeToParse.GetCustomAttribute() != null) 16 | { 17 | _parseEnumAsArray = true; 18 | Type _enumArrayType = typeToParse.MakeArrayType(); 19 | ITypeParser _arrayParser = parserContainer.GetParser(_enumArrayType); 20 | Array _enumArray = _arrayParser.Parse(toParse, _enumArrayType, parserContainer) as Array; 21 | int _totalVal = 0; 22 | int _iter = 0; 23 | 24 | foreach (object enVal in _enumArray) 25 | { 26 | if (_iter == 0) _totalVal = (int)enVal; 27 | else 28 | { 29 | _totalVal = _totalVal | (int)enVal; 30 | } 31 | _iter++; 32 | } 33 | _returnVal = _totalVal; 34 | _parseEnumAsArray = false; 35 | } 36 | else _returnVal = Enum.Parse(typeToParse, toParse, true); 37 | return _returnVal; 38 | } 39 | public override string[] GetAcceptedValues(Type typeToParse) 40 | { 41 | if (typeToParse.IsEnum) 42 | { 43 | return Enum.GetNames(typeToParse); 44 | } 45 | else return new string[0] { }; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/TypeParsers/Concrete/KeyValueParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 6 | 7 | namespace ConsoleCommon.Parsing.TypeParsers 8 | { 9 | public class KeyValueParser : ITypeParser 10 | { 11 | public Type GetTypeToParse(params Type[] genericTypeParams) 12 | { 13 | Type _returnType = typeof(KeyValuePair<,>); 14 | if (genericTypeParams.Count() == 2) _returnType = _returnType.MakeGenericType(genericTypeParams); 15 | return _returnType; 16 | } 17 | 18 | public string[] GetAcceptedValues(Type typeToParse) 19 | { 20 | return new string[0]; 21 | } 22 | 23 | public object Parse(string toParse, Type typeToParse, ITypeParserContainer parserContainer) 24 | { 25 | Type[] _genericTypes = typeToParse.GetGenericArguments(); 26 | string[] _keyValArr = toParse.Split(':'); 27 | if (_keyValArr == null || _keyValArr.Length < 1 || _keyValArr.Length > 2) throw new Exception("Failed to parse key value pair"); 28 | object _key = ParseElement(_keyValArr[0], _genericTypes[0], parserContainer); 29 | object _val = null; 30 | if(_keyValArr.Length > 1) _val = ParseElement(_keyValArr[1], _genericTypes[1], parserContainer); 31 | if (_key == null) throw new Exception("Failed to parse key value pair"); 32 | object[] _keyValPair = new object[] { _key, _val }; 33 | object myVal = typeToParse.GetConstructor(_genericTypes).Invoke(_keyValPair); 34 | return myVal; 35 | } 36 | 37 | private object ParseElement(string toParse, Type type, ITypeParserContainer parserContainer) 38 | { 39 | ITypeParser _parser = parserContainer.GetParser(type); 40 | if (_parser == null) throw new Exception($"Parsing type '{type.Name}' not handled"); 41 | return _parser.Parse(toParse, type, parserContainer); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/TypeParsers/Concrete/NullableParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 6 | 7 | namespace ConsoleCommon.Parsing.TypeParsers 8 | { 9 | public class NullableParser : ITypeParser 10 | { 11 | public string[] GetAcceptedValues(Type typeToParse) 12 | { 13 | return new string[0]; 14 | } 15 | 16 | public Type GetTypeToParse(params Type[] genericTypeParams) 17 | { 18 | Type _returnType = typeof(Nullable<>); 19 | if (genericTypeParams.Count() == 1) _returnType = typeof(Nullable<>).MakeGenericType(genericTypeParams[0]); 20 | return _returnType; 21 | 22 | } 23 | 24 | public object Parse(string toParse, Type typeToParse, ITypeParserContainer parserContainer) 25 | { 26 | Type _myUnderLyingType = Nullable.GetUnderlyingType(typeToParse); 27 | object _rtrnVal = parserContainer.GetParser(_myUnderLyingType)?.Parse(toParse, _myUnderLyingType, parserContainer); 28 | return _rtrnVal; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/TypeParsers/Concrete/ObjectParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 6 | 7 | namespace ConsoleCommon.Parsing.TypeParsers 8 | { 9 | public class ObjectParser : TypeParserBase 10 | { 11 | public override object Parse(string toParse, Type typeToParse, ITypeParserContainer parserContainer) 12 | { 13 | return Convert.ChangeType(toParse, typeToParse); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/TypeParsers/Concrete/SecureStringParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 6 | 7 | namespace ConsoleCommon.Parsing.TypeParsers 8 | { 9 | public class SecureStringParser : TypeParserBase 10 | { 11 | public override object Parse(string toParse, Type typeToParse, ITypeParserContainer parserContainer) 12 | { 13 | var secure = new System.Security.SecureString(); 14 | foreach (var v in toParse.ToCharArray()) 15 | { 16 | secure.AppendChar(v); 17 | } 18 | return secure; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/TypeParsers/Concrete/TypeParserContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ConsoleCommon.Helpers; 6 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 7 | 8 | namespace ConsoleCommon.Parsing.TypeParsers 9 | { 10 | public class TypeParserContainer : ITypeParserContainer 11 | { 12 | private List _typeParsers = new List(); 13 | public IEnumerable TypeParsers => _typeParsers.AsReadOnly(); 14 | 15 | #region Cstors 16 | public TypeParserContainer(bool overwriteDupes, TypeParserContainer parserContainer, IEnumerable typeParsers) 17 | : this(overwriteDupes, parserContainer.TypeParsers.Concat(typeParsers)) 18 | { 19 | } 20 | public TypeParserContainer(bool overwriteDupes, TypeParserContainer parserContainer, params ITypeParser[] typeParsers) 21 | : this(overwriteDupes, parserContainer.TypeParsers.Concat(typeParsers)) 22 | { 23 | } 24 | public TypeParserContainer(bool overwriteDupes, params ITypeParser[] typeParsers) 25 | { 26 | foreach (ITypeParser _tp in typeParsers) addTypeParser(_tp, overwriteDupes); 27 | } 28 | public TypeParserContainer(bool overwriteDupes, IEnumerable typeParsers) 29 | { 30 | foreach (ITypeParser _tp in typeParsers) addTypeParser(_tp, overwriteDupes); 31 | } 32 | #endregion 33 | 34 | #region Implementation 35 | public object Parse(string toParse, Type type) 36 | { 37 | if (type.IsInterface) throw new Exception("Cannot parse interfaces. Must use concrete types"); 38 | return GetParser(type).Parse(toParse, type, this); 39 | } 40 | public string[] GetAcceptedValues(Type type) 41 | { 42 | return GetParser(type).GetAcceptedValues(type); 43 | } 44 | public ITypeParser GetParser(Type typeToParse) 45 | { 46 | var _comparer = new TypesByInheritanceLevelComparer(); 47 | Type[] _genArgs = typeToParse.IsGenericType ? typeToParse.GetGenericArguments() : new Type[0]; 48 | ITypeParser _parser = 49 | TypeParsers 50 | .Where(t => t.GetTypeToParse(_genArgs).IsAssignableFrom(typeToParse)) 51 | .OrderBy(t => t.GetTypeToParse(_genArgs), _comparer) 52 | .LastOrDefault(); 53 | if (_parser == null) throw new Exception($"Parsing type '{typeToParse.Name}' not handled"); 54 | return _parser; 55 | } 56 | #endregion 57 | 58 | #region Helpers 59 | private void addTypeParser(ITypeParser typeParser, bool overwriteDupes) 60 | { 61 | ITypeParser _match = TypeParsers.FirstOrDefault(tp => tp.GetTypeToParse().Equals(typeParser.GetTypeToParse())); 62 | if(_match!=null) 63 | { 64 | if (overwriteDupes) 65 | { 66 | _typeParsers.Remove(_match); 67 | } 68 | else throw new Exception($"Type parser of type '{typeParser.GetTypeToParse()}' already exists"); 69 | } 70 | _typeParsers.Add(typeParser); 71 | } 72 | #endregion 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/TypeParsers/Concrete/TypeTypeParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ConsoleCommon.Parsing.TypeParsers.Interfaces; 6 | 7 | namespace ConsoleCommon.Parsing.TypeParsers 8 | { 9 | public class TypeTypeParser : TypeParserBase 10 | { 11 | public override object Parse(string toParse, Type typeToParse, ITypeParserContainer parserContainer) 12 | { 13 | IEnumerable _types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(asm => asm.GetTypes()); 14 | Type _returnType = _types.SkipWhile(t => 15 | (t.GetCustomAttribute() != null && 16 | t.GetCustomAttribute().Ignore)) 17 | .Where(t => t.MatchesAttributeValueOrName(toParse, attr => (attr == null || string.IsNullOrWhiteSpace(attr.FriendlyName)) ? "" : attr.FriendlyName.ToLower())).FirstOrDefault(); 18 | 19 | return _returnType; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/TypeParsers/Interfaces/ITypeParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace ConsoleCommon.Parsing.TypeParsers.Interfaces 7 | { 8 | public interface ITypeParser 9 | { 10 | Type GetTypeToParse(params Type[] genericTypeParams); 11 | object Parse(string toParse, Type typeToParse, ITypeParserContainer parserContainer); 12 | string[] GetAcceptedValues(Type typeToParse); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ConsoleCommon/Parsing/TypeParsers/Interfaces/TypeParserBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace ConsoleCommon.Parsing.TypeParsers.Interfaces 7 | { 8 | public abstract class TypeParserBase : ITypeParser 9 | { 10 | public Type GetTypeToParse(params Type[] genericTypeParams) 11 | { 12 | Type _returnType = typeof(T); 13 | if(genericTypeParams.Count() > 0 && _returnType.IsGenericType) 14 | { 15 | _returnType = _returnType.MakeGenericType(genericTypeParams); 16 | } 17 | return _returnType; 18 | } 19 | public abstract object Parse(string toParse, Type typeToParse, ITypeParserContainer parserContainer); 20 | public virtual string[] GetAcceptedValues(Type typeToParse) 21 | { 22 | return new string[0]; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ConsoleCommon/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ConsoleCommon")] 9 | [assembly: AssemblyDescription("ConsoleCommon is a .net library that provides a set of helper tools intended for use with console applications. These tools focus on automating argument typing and validation, and creating help text.")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ConsoleCommon")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("5b372442-5f51-4f2e-96c9-150a4e887c26")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("3.0.0.0")] 36 | [assembly: AssemblyFileVersion("3.0.0.0")] 37 | -------------------------------------------------------------------------------- /ConsoleCommon/SwitchAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ConsoleCommon 8 | { 9 | public class SwitchAttribute : Attribute 10 | { 11 | private string[] _switchValues; 12 | private string _switchName; 13 | private bool _dontallowvalues; 14 | private bool _required; 15 | private int? _defaultordinal; 16 | private string _helpText; 17 | 18 | public string[] SwitchValues { get { return _switchValues; } } 19 | public string SwitchName { get { return _switchName; } } 20 | public bool DontAllowValues { get { return _dontallowvalues; } } 21 | public bool Required { get { return _required; } } 22 | public int? DefaultOrdinal { get { return _defaultordinal; } } 23 | public string HelpText { get { return _helpText; } } 24 | 25 | /// 26 | /// Specifies properties for a switch property on a console params object. 27 | /// 28 | /// The syntax of the switch. Often a single letter. 29 | /// Specifies whether the switch is required. 30 | /// Help text used in Usage property. 31 | /// If set to true, then switch cannot contain input values. 32 | public SwitchAttribute(string switchName = "", bool required = false, int defaultOrdinal = -1, string helpText = "", bool dontAllowValues = false) 33 | : this(switchName, required, defaultOrdinal, helpText, new string[0]) 34 | { 35 | _dontallowvalues = dontAllowValues; 36 | } 37 | /// 38 | /// Specifies properties for a switch property on a console params object. 39 | /// 40 | /// The syntax of the switch. Often a single letter. 41 | /// Specifies whether the switch is required. 42 | /// Help text used in Usage property. 43 | /// If values specified, the switch can only contain 44 | /// the values specified. Otherwise, the switch can contain any value. 45 | public SwitchAttribute(string switchName = "", bool required = false, int defaultOrdinal = -1, string helpText = "", params string[] switchValues) 46 | { 47 | _switchName = switchName; 48 | _switchValues = switchValues; 49 | _required = required; 50 | if (defaultOrdinal != -1) _defaultordinal = defaultOrdinal; 51 | _helpText = helpText; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /ConsoleCommon/TypeParamAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace ConsoleCommon 7 | { 8 | public class TypeParamAttribute : Attribute 9 | { 10 | public string FriendlyName { get; set; } 11 | public bool Ignore { get; set; } 12 | public TypeParamAttribute() { } 13 | public TypeParamAttribute(string FriendlyName) 14 | { 15 | this.FriendlyName = FriendlyName; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ConsoleCommon/bin/Release/ConsoleCommon.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylax/ConsoleCommon/c4cc3c52bef3d7910c9924f6b9e7579b31b6bf66/ConsoleCommon/bin/Release/ConsoleCommon.dll -------------------------------------------------------------------------------- /ConsoleCommon/obj/Debug/ConsoleCommon.csproj.FileListAbsolute.txt: -------------------------------------------------------------------------------- 1 | C:\Projects\ConsoleCommon\ConsoleCommon\bin\Debug\ConsoleCommon.exe 2 | C:\Projects\ConsoleCommon\ConsoleCommon\bin\Debug\ConsoleCommon.pdb 3 | C:\Projects\ConsoleCommon\ConsoleCommon\obj\Debug\ConsoleCommon.exe 4 | C:\Projects\ConsoleCommon\ConsoleCommon\obj\Debug\ConsoleCommon.pdb 5 | -------------------------------------------------------------------------------- /ConsoleCommon/obj/Debug/ConsoleCommon.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylax/ConsoleCommon/c4cc3c52bef3d7910c9924f6b9e7579b31b6bf66/ConsoleCommon/obj/Debug/ConsoleCommon.exe -------------------------------------------------------------------------------- /ConsoleCommon/obj/Debug/ConsoleCommon.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylax/ConsoleCommon/c4cc3c52bef3d7910c9924f6b9e7579b31b6bf66/ConsoleCommon/obj/Debug/ConsoleCommon.pdb -------------------------------------------------------------------------------- /ConsoleCommon/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylax/ConsoleCommon/c4cc3c52bef3d7910c9924f6b9e7579b31b6bf66/ConsoleCommon/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache -------------------------------------------------------------------------------- /ConsoleCommon/obj/Release/ConsoleCommon.csproj.FileListAbsolute.txt: -------------------------------------------------------------------------------- 1 | C:\Projects\ConsoleCommon\ConsoleCommon\bin\Release\ConsoleCommon.pdb 2 | C:\Projects\ConsoleCommon\ConsoleCommon\obj\Release\ConsoleCommon.pdb 3 | C:\Projects\ConsoleCommon\ConsoleCommon\obj\Release\ConsoleCommon.csprojResolveAssemblyReference.cache 4 | C:\Projects\ConsoleCommon\ConsoleCommon\bin\Release\ConsoleCommon.dll 5 | C:\Projects\ConsoleCommon\ConsoleCommon\obj\Release\ConsoleCommon.dll 6 | -------------------------------------------------------------------------------- /ConsoleCommon/obj/Release/ConsoleCommon.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylax/ConsoleCommon/c4cc3c52bef3d7910c9924f6b9e7579b31b6bf66/ConsoleCommon/obj/Release/ConsoleCommon.dll -------------------------------------------------------------------------------- /ConsoleCommon/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /NugetInstructions.txt: -------------------------------------------------------------------------------- 1 | 1) Update .nuspec file. Save in proj base dir. 2 | 2) From command prompt, navigate to project base folder 3 | 3) Enter "NuGet pack" => output is a nupkg file 4 | 4) Validate nupkg file by changing ext to .zip, opening, and inspecting contents (should have .dll and .nuspec file). Make sure to change ext back to .nupkg 5 | 5) On Nuget.Org: upload => select nupkg => submit 6 | 6) OR: "Nuget push {nupkg filename} {access key}" -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | For instructions on usage, please read my article on codeproject: 2 | https://www.codeproject.com/Articles/1179863/Csharp-net-Console-Argument-Parser-and-Validation --------------------------------------------------------------------------------