diff --git a/Beverage/App.config b/Beverage_old/App.config
similarity index 100%
rename from Beverage/App.config
rename to Beverage_old/App.config
diff --git a/Beverage/Beverage.csproj b/Beverage_old/Beverage.csproj
similarity index 92%
rename from Beverage/Beverage.csproj
rename to Beverage_old/Beverage.csproj
index 8ce0968..b7214ad 100644
--- a/Beverage/Beverage.csproj
+++ b/Beverage_old/Beverage.csproj
@@ -52,12 +52,6 @@
-
-
- {2839AA55-B40A-4BB8-BDA0-C5057E5A683F}
- GoCommando
-
-
-
\ No newline at end of file
+
+
diff --git a/GoCommando.Tests_old/GoCommando.Tests.csproj b/GoCommando.Tests_old/GoCommando.Tests.csproj
new file mode 100644
index 0000000..011b1e5
--- /dev/null
+++ b/GoCommando.Tests_old/GoCommando.Tests.csproj
@@ -0,0 +1,68 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {5B0BCF64-81B6-4A33-8D00-C673A0EF3551}
+ Library
+ Properties
+ GoCommando.Tests
+ GoCommando.Tests
+ v4.5.2
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\NUnit.2.6.4\lib\nunit.framework.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {2839AA55-B40A-4BB8-BDA0-C5057E5A683F}
+ GoCommando
+
+
+
+
+
\ No newline at end of file
diff --git a/GoCommando.Tests/Properties/AssemblyInfo.cs b/GoCommando.Tests_old/Properties/AssemblyInfo.cs
similarity index 100%
rename from GoCommando.Tests/Properties/AssemblyInfo.cs
rename to GoCommando.Tests_old/Properties/AssemblyInfo.cs
diff --git a/GoCommando.Tests_old/TestArgParser.cs b/GoCommando.Tests_old/TestArgParser.cs
new file mode 100644
index 0000000..a334c81
--- /dev/null
+++ b/GoCommando.Tests_old/TestArgParser.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using GoCommando.Internals;
+using NUnit.Framework;
+
+namespace GoCommando.Tests
+{
+ [TestFixture]
+ public class TestArgParser
+ {
+ [Test]
+ public void CanReturnSimpleCommand()
+ {
+ var arguments = Parse(new[] { "run" });
+
+ Assert.That(arguments.Command, Is.EqualTo("run"));
+ }
+
+ [Test]
+ public void CommandIsNullWhenNoCommandIsGiven()
+ {
+ var arguments = Parse(new[] { "-file", @"""C:\temp\file.json""" });
+
+ Assert.That(arguments.Command, Is.Null);
+ }
+
+ [Test, Ignore("arguments.Command should just be null")]
+ public void DoesNotAcceptSwitchAsCommand()
+ {
+ var ex = Assert.Throws(() =>
+ {
+ Parse(new[] { "-file", @"""C:\temp\file.json""" });
+ });
+
+ Console.WriteLine(ex);
+ }
+
+ [Test]
+ public void CanParseOrdinaryArguments()
+ {
+ var args = @"run
+-path
+c:\Program Files
+-dir
+c:\Windows\Microsoft.NET\Framework
+-flag
+-moreflag".Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
+
+ var arguments = Parse(args);
+
+ Console.WriteLine(arguments);
+
+ Assert.That(arguments.Command, Is.EqualTo("run"));
+ Assert.That(arguments.Switches.Count(), Is.EqualTo(4));
+ Assert.That(arguments.Get("path"), Is.EqualTo(@"c:\Program Files"));
+ Assert.That(arguments.Get("dir"), Is.EqualTo(@"c:\Windows\Microsoft.NET\Framework"));
+
+ Assert.That(arguments.Get("flag"), Is.True);
+ Assert.That(arguments.Get("moreflag"), Is.True);
+ Assert.That(arguments.Get("flag_not_specified_should_default_to_false"), Is.False);
+ }
+
+ [TestCase(@"-path:""c:\temp""")]
+ [TestCase(@"-path=""c:\temp""")]
+ [TestCase(@"-path""c:\temp""")]
+ public void SupportsVariousSingleTokenAliases(string alias)
+ {
+ var arguments = Parse(new[] { alias });
+
+ Assert.That(arguments.Switches.Count(), Is.EqualTo(1));
+ Assert.That(arguments.Switches.Single().Key, Is.EqualTo("path"));
+ Assert.That(arguments.Switches.Single().Value, Is.EqualTo(@"c:\temp"));
+ }
+
+ [TestCase(@"-n23")]
+ public void SupportsShortFormWithNumber(string alias)
+ {
+ var arguments = Parse(new[] { alias });
+
+ Assert.That(arguments.Switches.Count(), Is.EqualTo(1));
+ Assert.That(arguments.Switches.Single().Key, Is.EqualTo("n"));
+ Assert.That(arguments.Switches.Single().Value, Is.EqualTo(@"23"));
+ }
+
+ static Arguments Parse(IEnumerable args)
+ {
+ return Go.Parse(args, new Settings());
+ }
+ }
+}
diff --git a/GoCommando.Tests_old/TestCommand.cs b/GoCommando.Tests_old/TestCommand.cs
new file mode 100644
index 0000000..6f3c3ab
--- /dev/null
+++ b/GoCommando.Tests_old/TestCommand.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using GoCommando.Internals;
+using NUnit.Framework;
+
+namespace GoCommando.Tests
+{
+ [TestFixture]
+ public class TestCommand
+ {
+ [TestCase("-switch:value2")]
+ [TestCase("-switch=value2")]
+ [TestCase(@"-switch""value2""")]
+ public void CanCorrectlyHandleDifferentAlternativeSwitchFormatsFoundInOneSingleTokenOnly(string switchText)
+ {
+ var settings = new Settings();
+ var invoker = new CommandInvoker("bimse", settings, new Bimse());
+ var arguments = Go.Parse(new[] { switchText }, settings);
+
+ invoker.Invoke(arguments.Switches, EnvironmentSettings.Empty);
+
+ var bimseInstance = (Bimse)invoker.CommandInstance;
+
+ Assert.That(bimseInstance.Switch, Is.EqualTo("value2"));
+ }
+
+ [TestCase("-s:value2")]
+ [TestCase("-s=value2")]
+ [TestCase(@"-s""value2""")]
+ public void CanCorrectlyHandleDifferentAlternativeSwitchFormatsFoundInOneSingleTokenOnly_Shortname(string switchText)
+ {
+ var settings = new Settings();
+ var invoker = new CommandInvoker("bimse", settings, new Bimse());
+ var arguments = Go.Parse(new[] { switchText }, settings);
+
+ invoker.Invoke(arguments.Switches, EnvironmentSettings.Empty);
+
+ var bimseInstance = (Bimse)invoker.CommandInstance;
+
+ Assert.That(bimseInstance.Switch, Is.EqualTo("value2"));
+ }
+
+ [Command("bimse")]
+ class Bimse : ICommand
+ {
+ [Parameter("switch", shortName: "s")]
+ public string Switch { get; set; }
+
+ public void Run()
+ {
+ }
+ }
+
+ [Test]
+ public void CanUseSuppliedCommandFactory()
+ {
+ var commandFactory = new CustomFactory();
+
+ var commandInvoker = new CommandInvoker("null", typeof(CreatedByFactory), new Settings(), commandFactory: commandFactory);
+
+ commandInvoker.Invoke(Enumerable.Empty(), new EnvironmentSettings());
+
+ Assert.That(commandInvoker.CommandInstance, Is.TypeOf());
+
+ var createdByFactory = (CreatedByFactory)commandInvoker.CommandInstance;
+ Assert.That(createdByFactory.CtorInjectedValue, Is.EqualTo("ctor!!"));
+
+ Assert.That(commandFactory.WasProperlyReleased, Is.True, "The created command instance was NOT properly released after use!");
+ }
+
+ class CustomFactory : ICommandFactory
+ {
+ CreatedByFactory _instance;
+
+ public bool WasProperlyReleased { get; set; }
+
+ public ICommand Create(Type type)
+ {
+ if (type == typeof(CreatedByFactory))
+ {
+ _instance = new CreatedByFactory("ctor!!");
+ return _instance;
+ }
+
+ throw new ArgumentException($"Unknown command type: {type}");
+ }
+
+ public void Release(ICommand command)
+ {
+ if (_instance == command)
+ {
+ WasProperlyReleased = true;
+ }
+ }
+ }
+
+ class CreatedByFactory : ICommand
+ {
+ public string CtorInjectedValue { get; }
+
+ public CreatedByFactory(string ctorInjectedValue)
+ {
+ CtorInjectedValue = ctorInjectedValue;
+ }
+
+ public void Run()
+ {
+ }
+ }
+
+ [Test]
+ public void CanGetParameterFromAppSettingsAndConnectionStrings()
+ {
+ var invoker = new CommandInvoker("null", typeof(CanUseAppSetting), new Settings());
+
+ var appSettings = new Dictionary
+ {
+ {"my-setting", "my-value"}
+ };
+
+ var connectionStrings = new Dictionary
+ {
+ {"my-conn", "my-value"}
+ };
+
+ var environmentVariables = new Dictionary
+ {
+ {"my-env", "my-value"}
+ };
+
+ invoker.Invoke(Enumerable.Empty(), new EnvironmentSettings(appSettings, connectionStrings, environmentVariables));
+
+ var instance = (CanUseAppSetting)invoker.CommandInstance;
+
+ Assert.That(instance.AppSetting, Is.EqualTo("my-value"));
+ Assert.That(instance.ConnectionString, Is.EqualTo("my-value"));
+ Assert.That(instance.EnvironmentVariable, Is.EqualTo("my-value"));
+ }
+
+ class CanUseAppSetting : ICommand
+ {
+ [Parameter("my-setting", allowAppSetting: true)]
+ public string AppSetting { get; set; }
+
+ [Parameter("my-conn", allowConnectionString: true)]
+ public string ConnectionString { get; set; }
+
+ [Parameter("my-env", allowEnvironmentVariable: true)]
+ public string EnvironmentVariable { get; set; }
+
+ public void Run()
+ {
+
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando.Tests/packages.config b/GoCommando.Tests_old/packages.config
similarity index 100%
rename from GoCommando.Tests/packages.config
rename to GoCommando.Tests_old/packages.config
diff --git a/GoCommando.sln b/GoCommando.sln
index 46def31..030255c 100644
--- a/GoCommando.sln
+++ b/GoCommando.sln
@@ -1,24 +1,18 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.25123.0
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30330.147
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoCommando", "GoCommando\GoCommando.csproj", "{2839AA55-B40A-4BB8-BDA0-C5057E5A683F}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "TestApp\TestApp.csproj", "{1DC7365E-E01D-477A-82F7-2BC5EFC51640}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoCommando.Tests", "GoCommando.Tests\GoCommando.Tests.csproj", "{5B0BCF64-81B6-4A33-8D00-C673A0EF3551}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "stuff", "stuff", "{4777EF91-8FBF-42DC-A31F-2025CC5841F8}"
ProjectSection(SolutionItems) = preProject
Package.nuspec = Package.nuspec
README.md = README.md
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Beverage", "Beverage\Beverage.csproj", "{324FC1A5-749A-44BF-BDB7-EB093F5DD88F}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{94F2785A-49E5-447F-BAFF-BE8F783EC71F}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp2", "TestApp2\TestApp2.csproj", "{07010B53-B1CB-4696-8BB4-DCA2943B6B59}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoCommando", "GoCommando\GoCommando.csproj", "{91A9A887-0298-4B72-9D24-C2438F469192}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoCommando.Tests", "GoCommando.Tests\GoCommando.Tests.csproj", "{FBD7AE5E-C842-448B-9A6B-81513E60F357}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -26,33 +20,19 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {2839AA55-B40A-4BB8-BDA0-C5057E5A683F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2839AA55-B40A-4BB8-BDA0-C5057E5A683F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2839AA55-B40A-4BB8-BDA0-C5057E5A683F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2839AA55-B40A-4BB8-BDA0-C5057E5A683F}.Release|Any CPU.Build.0 = Release|Any CPU
- {1DC7365E-E01D-477A-82F7-2BC5EFC51640}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1DC7365E-E01D-477A-82F7-2BC5EFC51640}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1DC7365E-E01D-477A-82F7-2BC5EFC51640}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1DC7365E-E01D-477A-82F7-2BC5EFC51640}.Release|Any CPU.Build.0 = Release|Any CPU
- {5B0BCF64-81B6-4A33-8D00-C673A0EF3551}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5B0BCF64-81B6-4A33-8D00-C673A0EF3551}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5B0BCF64-81B6-4A33-8D00-C673A0EF3551}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5B0BCF64-81B6-4A33-8D00-C673A0EF3551}.Release|Any CPU.Build.0 = Release|Any CPU
- {324FC1A5-749A-44BF-BDB7-EB093F5DD88F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {324FC1A5-749A-44BF-BDB7-EB093F5DD88F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {324FC1A5-749A-44BF-BDB7-EB093F5DD88F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {324FC1A5-749A-44BF-BDB7-EB093F5DD88F}.Release|Any CPU.Build.0 = Release|Any CPU
- {07010B53-B1CB-4696-8BB4-DCA2943B6B59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {07010B53-B1CB-4696-8BB4-DCA2943B6B59}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {07010B53-B1CB-4696-8BB4-DCA2943B6B59}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {07010B53-B1CB-4696-8BB4-DCA2943B6B59}.Release|Any CPU.Build.0 = Release|Any CPU
+ {91A9A887-0298-4B72-9D24-C2438F469192}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {91A9A887-0298-4B72-9D24-C2438F469192}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {91A9A887-0298-4B72-9D24-C2438F469192}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {91A9A887-0298-4B72-9D24-C2438F469192}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FBD7AE5E-C842-448B-9A6B-81513E60F357}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FBD7AE5E-C842-448B-9A6B-81513E60F357}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FBD7AE5E-C842-448B-9A6B-81513E60F357}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FBD7AE5E-C842-448B-9A6B-81513E60F357}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {1DC7365E-E01D-477A-82F7-2BC5EFC51640} = {94F2785A-49E5-447F-BAFF-BE8F783EC71F}
- {324FC1A5-749A-44BF-BDB7-EB093F5DD88F} = {94F2785A-49E5-447F-BAFF-BE8F783EC71F}
- {07010B53-B1CB-4696-8BB4-DCA2943B6B59} = {94F2785A-49E5-447F-BAFF-BE8F783EC71F}
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {F99A8DD1-DE53-43D3-A0E0-BE222C826E90}
EndGlobalSection
EndGlobal
diff --git a/GoCommando/Go.cs b/GoCommando/Go.cs
index 4b64256..4685204 100644
--- a/GoCommando/Go.cs
+++ b/GoCommando/Go.cs
@@ -1,11 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
-using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
-using System.Runtime.InteropServices;
using System.Security;
using GoCommando.Internals;
using Switch = GoCommando.Internals.Switch;
@@ -283,22 +281,23 @@ static void InnerRun(ICommandFactory commandFactory, bool supportImpersonation)
{availableCommands}");
}
- var appSettings = ConfigurationManager.AppSettings.AllKeys
- .Select(key => new
- {
- Key = key,
- Value = ConfigurationManager.AppSettings[key]
- })
- .ToDictionary(a => a.Key, a => a.Value);
+ //var appSettings = ConfigurationManager.AppSettings.AllKeys
+ // .Select(key => new
+ // {
+ // Key = key,
+ // Value = ConfigurationManager.AppSettings[key]
+ // })
+ // .ToDictionary(a => a.Key, a => a.Value);
- var connectionStrings = ConfigurationManager.ConnectionStrings.Cast()
- .ToDictionary(a => a.Name, a => a.ConnectionString);
+ //var connectionStrings = ConfigurationManager.ConnectionStrings.Cast()
+ // .ToDictionary(a => a.Name, a => a.ConnectionString);
var environmentVariables = Environment.GetEnvironmentVariables()
.Cast()
.ToDictionary(a => (string)a.Key, a => (string)a.Value);
- var environmentSettings = new EnvironmentSettings(appSettings, connectionStrings, environmentVariables);
+ //var environmentSettings = new EnvironmentSettings(appSettings, connectionStrings, environmentVariables);
+ var environmentSettings = new EnvironmentSettings(new Dictionary(), new Dictionary(), environmentVariables);
commandToRun.Invoke(arguments.Switches, environmentSettings);
}
diff --git a/GoCommando/GoCommando.csproj b/GoCommando/GoCommando.csproj
index 8611a30..9f5c4f4 100644
--- a/GoCommando/GoCommando.csproj
+++ b/GoCommando/GoCommando.csproj
@@ -1,76 +1,7 @@
-
-
-
+
+
- Debug
- AnyCPU
- {2839AA55-B40A-4BB8-BDA0-C5057E5A683F}
- Library
- Properties
- GoCommando
- GoCommando
- v4.5
- 512
-
+ netstandard2.0
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
- bin\Release\GoCommando.XML
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
diff --git a/GoCommando/Internals/InternalsVisibleTo.cs b/GoCommando/Internals/InternalsVisibleTo.cs
new file mode 100644
index 0000000..b8b5556
--- /dev/null
+++ b/GoCommando/Internals/InternalsVisibleTo.cs
@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("GoCommando.Tests")]
\ No newline at end of file
diff --git a/GoCommando_old/BannerAttribute.cs b/GoCommando_old/BannerAttribute.cs
new file mode 100644
index 0000000..ae86b51
--- /dev/null
+++ b/GoCommando_old/BannerAttribute.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace GoCommando
+{
+ ///
+ /// Apply this attribute to the class that has your Main
method in order to have a nice banner printed out when the program starts
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+ public class BannerAttribute : Attribute
+ {
+ ///
+ /// Gets the banner text
+ ///
+ public string BannerText { get; }
+
+ ///
+ /// Constructs the attribute
+ ///
+ public BannerAttribute(string bannerText)
+ {
+ BannerText = bannerText;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/CommandAttribute.cs b/GoCommando_old/CommandAttribute.cs
new file mode 100644
index 0000000..c204ab4
--- /dev/null
+++ b/GoCommando_old/CommandAttribute.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace GoCommando
+{
+ ///
+ /// Attribute that can be applied to a class that represents a command. The class must implement
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+ public class CommandAttribute : Attribute
+ {
+ ///
+ /// Gets the name of the command
+ ///
+ public string Command { get; }
+
+ ///
+ /// Gets the name of the command's group (if any). Grouping commands affects how they are presented when printing
+ /// help texts
+ ///
+ public string Group { get; }
+
+ ///
+ /// Constructs the attribute
+ ///
+ public CommandAttribute(string command, string group = null)
+ {
+ Command = command;
+ Group = group ?? "";
+ }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/DescriptionAttribute.cs b/GoCommando_old/DescriptionAttribute.cs
new file mode 100644
index 0000000..d585504
--- /dev/null
+++ b/GoCommando_old/DescriptionAttribute.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace GoCommando
+{
+ ///
+ /// Apply this attribute to a property of a command class (which is also decorated with ) in
+ /// order to provide a description of the parameter
+ ///
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
+ public class DescriptionAttribute : Attribute
+ {
+ ///
+ /// Gets the description text
+ ///
+ public string DescriptionText { get; }
+
+ ///
+ /// Constructs the attribute
+ ///
+ public DescriptionAttribute(string descriptionText)
+ {
+ DescriptionText = descriptionText;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/ExampleAttribute.cs b/GoCommando_old/ExampleAttribute.cs
new file mode 100644
index 0000000..9f46135
--- /dev/null
+++ b/GoCommando_old/ExampleAttribute.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace GoCommando
+{
+ ///
+ /// Apply one or more of these to a command property to show examples on how this particular parameter can be used
+ ///
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
+ public class ExampleAttribute : Attribute
+ {
+ ///
+ /// Gets the example text
+ ///
+ public string ExampleValue { get; }
+
+ ///
+ /// Constructs the attribute
+ ///
+ public ExampleAttribute(string exampleValue)
+ {
+ ExampleValue = exampleValue;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/ExitCodeException.cs b/GoCommando_old/ExitCodeException.cs
new file mode 100644
index 0000000..f0150c1
--- /dev/null
+++ b/GoCommando_old/ExitCodeException.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace GoCommando
+{
+ ///
+ /// Exception that can be used to exit the program with a custom exit code
+ ///
+ [Serializable]
+ public class CustomExitCodeException : Exception
+ {
+ ///
+ /// Constructs the exception
+ ///
+ protected CustomExitCodeException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+
+ ///
+ /// Constructs the exception
+ ///
+ public CustomExitCodeException(int exitCode, string message) : base(message)
+ {
+ ExitCode = exitCode;
+ }
+
+ ///
+ /// Gets the exit code that the program must exit with
+ ///
+ public int ExitCode { get; }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/Go.cs b/GoCommando_old/Go.cs
new file mode 100644
index 0000000..4b64256
--- /dev/null
+++ b/GoCommando_old/Go.cs
@@ -0,0 +1,507 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Security;
+using GoCommando.Internals;
+using Switch = GoCommando.Internals.Switch;
+// ReSharper disable ArgumentsStyleNamedExpression
+
+namespace GoCommando
+{
+ ///
+ /// Here's how to go commando: 1. Go.Run()
+ ///
+ public static class Go
+ {
+ ///
+ /// Call this method from your Main
method if you want to supply a custom which
+ /// will be used to create command instances
+ ///
+ public static void Run() where TCommandFactory : ICommandFactory, new()
+ {
+ var commandFactory = CreateCommandFactory();
+
+ Run(commandFactory);
+ }
+
+ ///
+ /// Call this method from your Main
method
+ ///
+ public static void Run()
+ {
+ Run(null);
+ }
+
+ static void Run(ICommandFactory commandFactory)
+ {
+ try
+ {
+ var declaringType = Assembly.GetEntryAssembly()?.EntryPoint?.DeclaringType;
+ var supportImpersonation = declaringType?.GetCustomAttribute() != null;
+
+ if (supportImpersonation)
+ {
+ var args = Environment.GetCommandLineArgs().Skip(1).ToList();
+ var settings = new Settings();
+ var arguments = Parse(args, settings);
+
+ var impersonateNow = arguments.Switches.Any(s => s.Key == "username");
+
+ if (impersonateNow)
+ {
+ Impersonate(arguments);
+ return;
+ }
+ }
+
+ var bannerAttribute = declaringType?.GetCustomAttribute();
+
+ if (bannerAttribute != null)
+ {
+ Console.WriteLine(bannerAttribute.BannerText);
+ }
+
+ InnerRun(commandFactory, supportImpersonation);
+ }
+ catch (GoCommandoException friendlyException)
+ {
+ Environment.ExitCode = -1;
+ Console.WriteLine(friendlyException.Message);
+ Console.WriteLine();
+ Console.WriteLine("Invoke with -help to get help for each command.");
+ Console.WriteLine();
+ Console.WriteLine("Exit code: -1");
+ Console.WriteLine();
+ }
+ catch (CustomExitCodeException customExitCodeException)
+ {
+ FailAndExit(customExitCodeException, customExitCodeException.ExitCode);
+ }
+ catch (Exception exception)
+ {
+ FailAndExit(exception, -2);
+ }
+ }
+
+ static void Impersonate(Arguments arguments)
+ {
+ var username = arguments.Switches.First(s => s.Key == "username");
+ var password = arguments.Switches.FirstOrDefault(s => s.Key == "password")
+ ?? throw new ArgumentException("Please remember to also specify the -password switch when you use the -username switch");
+ var domain = arguments.Switches.FirstOrDefault(s => s.Key == "domain")?.Value;
+
+ var keysToRemove = new[] { "username", "password", "domain" };
+
+ Impersonate(username.Value, password.Value, domain, arguments.Switches.Where(s => !keysToRemove.Contains(s.Key)), arguments.Command);
+ }
+
+ static void Impersonate(string username, string password, string domain, IEnumerable switches, string command)
+ {
+ var commandLineArgs = string.Join(" ", switches.Select(s => $"-{s.Key} {EnsureQuoted(s.Value)}"));
+ var ohSoSecureString = new SecureString();
+
+ foreach (var @char in password) { ohSoSecureString.AppendChar(@char); }
+
+ var processStartInfo = new ProcessStartInfo
+ {
+ FileName = $"{Assembly.GetEntryAssembly().GetName().Name}.exe",
+ Arguments = $"{command} {string.Join(" ", commandLineArgs)}",
+ WorkingDirectory = Environment.CurrentDirectory,
+
+ UserName = username,
+ Domain = domain,
+ Password = ohSoSecureString,
+
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+
+ ErrorDialog = false,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ LoadUserProfile = false
+ };
+
+ var process = new Process
+ {
+ StartInfo = processStartInfo,
+ };
+
+ void Write(string str, string prefix = "")
+ {
+ if (string.IsNullOrWhiteSpace(str))
+ {
+ Console.WriteLine();
+ }
+ else if (string.IsNullOrWhiteSpace(prefix))
+ {
+ Console.WriteLine(str);
+ }
+ else
+ {
+ Console.WriteLine($"{prefix} {str}");
+ }
+ }
+
+ process.OutputDataReceived += (s, ea) => Write(ea.Data);
+ process.ErrorDataReceived += (s, ea) => Write(ea.Data, "ERR");
+
+ try
+ {
+ Console.WriteLine($"Invoking command as {username}...");
+
+ process.Start();
+
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+
+ process.WaitForExit();
+ }
+ catch (Exception exception)
+ {
+ throw new ApplicationException("An error occurred when running the command under impersonation", exception);
+ }
+
+ if (process.ExitCode != 0)
+ {
+ throw new ApplicationException($"Impersonated process exited with code {process.ExitCode}");
+ }
+ }
+
+ static string EnsureQuoted(string str)
+ {
+ if (str == null) return str;
+ const string doubleQuote = "\"";
+ return str.StartsWith(doubleQuote) && str.EndsWith(doubleQuote) ? str : $"\"{str}\"";
+ }
+
+
+ static TCommandFactory CreateCommandFactory() where TCommandFactory : ICommandFactory, new()
+ {
+ try
+ {
+ return new TCommandFactory();
+ }
+ catch (Exception exception)
+ {
+ throw new ApplicationException($"Could not create command factory of type {typeof(TCommandFactory)}", exception);
+ }
+ }
+
+ static void FailAndExit(Exception customExitCodeException, int exitCode)
+ {
+ Console.WriteLine();
+ Console.Error.WriteLine(customExitCodeException);
+ Console.WriteLine();
+ Console.WriteLine("Exit code: {0}", exitCode);
+ Console.WriteLine();
+
+ Environment.ExitCode = exitCode;
+ }
+
+ static void InnerRun(ICommandFactory commandFactory, bool supportImpersonation)
+ {
+ var args = Environment.GetCommandLineArgs().Skip(1).ToList();
+ var settings = new Settings();
+ var arguments = Parse(args, settings);
+ var commandTypes = GetCommands(commandFactory, settings, supportImpersonation: supportImpersonation);
+
+ var helpSwitch = arguments.Switches.FirstOrDefault(s => s.Key == "?")
+ ?? arguments.Switches.FirstOrDefault(s => s.Key == "help");
+
+ if (helpSwitch != null)
+ {
+ var exe = Assembly.GetEntryAssembly().GetName().Name + ".exe";
+
+ if (helpSwitch.Value != null)
+ {
+ var command = commandTypes.FirstOrDefault(c => c.Command == helpSwitch.Value);
+
+ if (command != null)
+ {
+ if (command.Parameters.Any())
+ {
+ Console.WriteLine(
+ $@"{command.Description}
+
+Type
+
+ {exe} {command.Command}
+
+where can consist of the following parameters:
+
+{string
+ .Join(Environment.NewLine,
+ command.Parameters.Select(parameter => FormatParameter(parameter, settings)))}");
+ }
+ else
+ {
+ Console.WriteLine(@"Type
+
+ {0} {1}
+
+", exe, command.Command);
+ }
+ return;
+ }
+
+ throw new GoCommandoException($"Unknown command: '{helpSwitch.Value}'");
+ }
+
+ var availableCommands = GetAvailableCommandsHelpText(commandTypes);
+
+ Console.WriteLine($@"The following commands are available:
+
+{availableCommands}
+
+Type
+
+ {exe} -help
+
+to get help for a command.
+");
+ return;
+ }
+
+ var commandToRun = commandTypes.FirstOrDefault(c => c.Command == arguments.Command);
+
+ if (commandToRun == null)
+ {
+ var errorText = !string.IsNullOrWhiteSpace(arguments.Command)
+ ? $"Could not find command '{arguments.Command}'"
+ : "Please invoke with a command";
+
+ var availableCommands = GetAvailableCommandsHelpText(commandTypes);
+
+ throw new GoCommandoException($@"{errorText} - the following commands are available:
+
+{availableCommands}");
+ }
+
+ var appSettings = ConfigurationManager.AppSettings.AllKeys
+ .Select(key => new
+ {
+ Key = key,
+ Value = ConfigurationManager.AppSettings[key]
+ })
+ .ToDictionary(a => a.Key, a => a.Value);
+
+ var connectionStrings = ConfigurationManager.ConnectionStrings.Cast()
+ .ToDictionary(a => a.Name, a => a.ConnectionString);
+
+ var environmentVariables = Environment.GetEnvironmentVariables()
+ .Cast()
+ .ToDictionary(a => (string)a.Key, a => (string)a.Value);
+
+ var environmentSettings = new EnvironmentSettings(appSettings, connectionStrings, environmentVariables);
+
+ commandToRun.Invoke(arguments.Switches, environmentSettings);
+ }
+
+ static string GetAvailableCommandsHelpText(List commandTypes)
+ {
+ var commandGroups = commandTypes
+ .GroupBy(c => c.Group)
+ .ToList();
+
+ if (commandGroups.Count == 1)
+ {
+ return string.Join(Environment.NewLine, commandTypes.Select(c => $" {c.Command} - {c.Description}"));
+ }
+
+ return string.Join(Environment.NewLine + Environment.NewLine,
+ commandGroups
+ .Select(g => $@" {g.Key}:
+
+{string.Join(Environment.NewLine, g.Select(c => $" {c.Command} - {c.Description}"))}"));
+
+ }
+
+ static string FormatParameter(Parameter parameter, Settings settings)
+ {
+ var shorthand = parameter.Shortname != null
+ ? $" / {settings.SwitchPrefix}{parameter.Shortname}"
+ : "";
+
+ var additionalProperties = new List();
+
+ var isFlag = parameter.IsFlag;
+
+ if (isFlag)
+ {
+ additionalProperties.Add("flag");
+ }
+
+ if (parameter.Optional)
+ {
+ additionalProperties.Add("optional");
+ }
+
+ var additionalPropertiesText = additionalProperties.Any()
+ ? $" ({string.Join("/", additionalProperties)})"
+ : "";
+
+ var helpText = " " + (parameter.DescriptionText ?? "(no help text available)");
+
+ var examplesText = !parameter.ExampleValues.Any()
+ ? ""
+ : FormatExamples(parameter, settings);
+
+ var switchText = $"{settings.SwitchPrefix}{parameter.Name}{shorthand}{additionalPropertiesText}";
+
+ return $@" {switchText}
+{helpText}
+{examplesText}";
+ }
+
+ static string FormatExamples(Parameter parameter, Settings settings)
+ {
+ var examples = string.Join(Environment.NewLine, parameter.ExampleValues
+ .Select(e => $" {settings.SwitchPrefix}{parameter.Name} {e}"));
+
+ return $@"
+ Examples:
+{examples}
+";
+ }
+
+ internal static List GetCommands(ICommandFactory commandFactory, Settings settings, bool supportImpersonation)
+ {
+ var commandAttributes = Assembly.GetEntryAssembly().GetTypes()
+ .Select(t => new
+ {
+ Type = t,
+ Attribute = t.GetCustomAttribute()
+ })
+ .Where(a => a.Attribute != null)
+ .GroupBy(a => a.Attribute.Command)
+ .ToList();
+
+ var duplicateCommands = commandAttributes
+ .Where(g => g.Count() > 1)
+ .ToList();
+
+ if (duplicateCommands.Any())
+ {
+ throw new GoCommandoException($@"The following commands are duplicates:
+
+{string.Join(Environment.NewLine, duplicateCommands.SelectMany(g => g.Select(a => $" {a.Attribute.Command}: {a.Type}")))}");
+ }
+
+ return commandAttributes
+ .Select(g =>
+ {
+ var a = g.First();
+
+ return new CommandInvoker(a.Attribute.Command, a.Type, settings, a.Attribute.Group, commandFactory);
+ })
+ .ToList();
+ }
+
+ internal static Arguments Parse(IEnumerable args, Settings settings)
+ {
+ var list = args.ToList();
+
+ if (!list.Any()) return new Arguments(null, Enumerable.Empty(), settings);
+
+ var first = list.First();
+
+ string command;
+ List switchArgs;
+
+ if (first.StartsWith(settings.SwitchPrefix))
+ {
+ command = null;
+ switchArgs = list;
+ }
+ else
+ {
+ command = first;
+ switchArgs = list.Skip(1).ToList();
+ }
+
+ var switches = new List();
+
+ string key = null;
+
+ foreach (var arg in switchArgs)
+ {
+ if (arg.StartsWith(settings.SwitchPrefix))
+ {
+ if (key != null)
+ {
+ switches.Add(Switch.Flag(key));
+ }
+
+ key = arg.Substring(settings.SwitchPrefix.Length);
+
+ if (HasKeyAndValue(key))
+ {
+ var keyAndValue = GetKeyAndValueFromKey(key);
+ if (keyAndValue == null)
+ {
+ throw new ApplicationException($"Expected to get key-value-pair from key '{key}'");
+ }
+ switches.Add(Switch.KeyValue(keyAndValue.Value.Key, keyAndValue.Value.Value));
+ key = null;
+ }
+
+ continue;
+ }
+
+ var value = arg;
+
+ if (key == null)
+ {
+ throw new GoCommandoException($"Got command line argument '{value}' without a switch in front of it - please specify switches like this: '{settings.SwitchPrefix}switch some-value'");
+ }
+
+ switches.Add(Switch.KeyValue(key, value));
+
+ key = null;
+ }
+
+ if (key != null)
+ {
+ switches.Add(Switch.Flag(key));
+ }
+
+ return new Arguments(command, switches, settings);
+ }
+
+ static bool HasKeyAndValue(string key)
+ {
+ return GetKeyAndValueFromKey(key) != null;
+ }
+
+ static KeyValuePair? GetKeyAndValueFromKey(string key)
+ {
+ for (var index = 0; index < key.Length; index++)
+ {
+ var c = key[index];
+
+ if (c == ':')
+ {
+ return new KeyValuePair(key.Substring(0, index), key.Substring(index + 1));
+ }
+
+ if (c == '=')
+ {
+ return new KeyValuePair(key.Substring(0, index), key.Substring(index + 1));
+ }
+
+ if (!char.IsLetter(c))
+ {
+ return new KeyValuePair(key.Substring(0, index), key.Substring(index));
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/GoCommando_old/GoCommando.csproj b/GoCommando_old/GoCommando.csproj
new file mode 100644
index 0000000..8611a30
--- /dev/null
+++ b/GoCommando_old/GoCommando.csproj
@@ -0,0 +1,76 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {2839AA55-B40A-4BB8-BDA0-C5057E5A683F}
+ Library
+ Properties
+ GoCommando
+ GoCommando
+ v4.5
+ 512
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ bin\Release\GoCommando.XML
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GoCommando_old/GoCommandoException.cs b/GoCommando_old/GoCommandoException.cs
new file mode 100644
index 0000000..94ce5a9
--- /dev/null
+++ b/GoCommando_old/GoCommandoException.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace GoCommando
+{
+ ///
+ /// Friendly exception that can be thrown in cases where you want the program to exit with
+ /// a nice, human-readable message. Only the message will be shown.
+ ///
+ [Serializable]
+ public class GoCommandoException : Exception
+ {
+ ///
+ /// Constructs the exception
+ ///
+ protected GoCommandoException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+
+ ///
+ /// Constructs the exception
+ ///
+ public GoCommandoException(string message) : base(message)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/ICommand.cs b/GoCommando_old/ICommand.cs
new file mode 100644
index 0000000..d2f1b61
--- /dev/null
+++ b/GoCommando_old/ICommand.cs
@@ -0,0 +1,13 @@
+namespace GoCommando
+{
+ ///
+ /// Implement this interface on each command
+ ///
+ public interface ICommand
+ {
+ ///
+ /// Main run method that is invoked by GoCommando
+ ///
+ void Run();
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/ICommandFactory.cs b/GoCommando_old/ICommandFactory.cs
new file mode 100644
index 0000000..8a6fc53
--- /dev/null
+++ b/GoCommando_old/ICommandFactory.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace GoCommando
+{
+ ///
+ /// Can be implemented to supply a custom command factory that will be given a chance to create command instances
+ /// and dispose of them after use
+ ///
+ public interface ICommandFactory
+ {
+ ///
+ /// Should create a new command instance of the given
+ ///
+ ICommand Create(Type commandType);
+
+ ///
+ /// Should release the command instance - probably by disposing it or delegating the disposal to an IoC container
+ ///
+ void Release(ICommand command);
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/Internals/Arguments.cs b/GoCommando_old/Internals/Arguments.cs
new file mode 100644
index 0000000..862be68
--- /dev/null
+++ b/GoCommando_old/Internals/Arguments.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace GoCommando.Internals
+{
+ class Arguments
+ {
+ readonly Settings _settings;
+
+ public Arguments(string command, IEnumerable switches, Settings settings)
+ {
+ _settings = settings;
+ var switchList = switches.ToList();
+ var duplicateSwitchKeys = switchList.GroupBy(s => s.Key).Where(g => g.Count() > 1).ToList();
+
+ if (duplicateSwitchKeys.Any())
+ {
+ var dupes = string.Join(", ", duplicateSwitchKeys.Select(g => $"{settings.SwitchPrefix}{g.Key}"));
+
+ throw new GoCommandoException($"The following switches have been specified more than once: {dupes}");
+ }
+
+ Command = command;
+ Switches = switchList;
+ }
+
+ public string Command { get; }
+
+ public IEnumerable Switches { get; }
+
+ public TValue Get(string key)
+ {
+ var desiredType = typeof(TValue);
+
+ try
+ {
+ if (desiredType == typeof(bool))
+ {
+ return (TValue)Convert.ChangeType(Switches.Any(s => s.Key == key), desiredType);
+ }
+
+ var relevantSwitch = Switches.FirstOrDefault(s => s.Key == key);
+
+ if (relevantSwitch != null)
+ {
+ return (TValue)Convert.ChangeType(relevantSwitch.Value, desiredType);
+ }
+
+ throw new GoCommandoException($"Could not find switch '{key}'");
+ }
+ catch (Exception exception)
+ {
+ throw new FormatException($"Could not get switch '{key}' as a {desiredType}", exception);
+ } }
+
+ public override string ToString()
+ {
+ return $@"{Command}
+
+{string.Join(Environment.NewLine, Switches.Select(s => " " + s))}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/Internals/CommandInvoker.cs b/GoCommando_old/Internals/CommandInvoker.cs
new file mode 100644
index 0000000..a85f520
--- /dev/null
+++ b/GoCommando_old/Internals/CommandInvoker.cs
@@ -0,0 +1,298 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace GoCommando.Internals
+{
+ class CommandInvoker
+ {
+ readonly Settings _settings;
+ readonly ICommand _commandInstance;
+ readonly Action _releaser;
+ readonly List _parameters;
+
+ public CommandInvoker(string command, Type type, Settings settings, string @group = null,
+ ICommandFactory commandFactory = null)
+ : this(command, settings, CreateInstance(type, GetFactoryMethod(commandFactory)), group, GetReleaseMethod(commandFactory))
+ {
+ }
+
+ public CommandInvoker(string command, Settings settings, ICommand commandInstance, string group = null, Action releaseMethod = null)
+ {
+ if (command == null) throw new ArgumentNullException(nameof(command));
+ if (settings == null) throw new ArgumentNullException(nameof(settings));
+ if (commandInstance == null) throw new ArgumentNullException(nameof(commandInstance));
+
+ _settings = settings;
+ _commandInstance = commandInstance;
+ _releaser = releaseMethod ?? DefaultReleaseMethod;
+
+ Command = command;
+ Group = group;
+
+ _parameters = GetParameters(Type).ToList();
+ }
+
+ static void DefaultReleaseMethod(ICommand command)
+ {
+ var disposable = command as IDisposable;
+
+ disposable?.Dispose();
+ }
+
+ static Func GetFactoryMethod(ICommandFactory commandFactory)
+ {
+ if (commandFactory == null) return null;
+
+ return commandFactory.Create;
+ }
+
+ static Action GetReleaseMethod(ICommandFactory commandFactory)
+ {
+ if (commandFactory == null) return null;
+
+ return commandFactory.Release;
+ }
+
+ static ICommand CreateInstance(Type type, Func commandFactory = null)
+ {
+ try
+ {
+ var instance = commandFactory?.Invoke(type)
+ ?? Activator.CreateInstance(type);
+
+ if (!(instance is ICommand))
+ {
+ throw new ApplicationException($"{instance} does not implement ICommand!");
+ }
+
+ return (ICommand)instance;
+ }
+ catch (Exception exception)
+ {
+ throw new ApplicationException($"Could not use type {type} as a GoCommando command", exception);
+ }
+ }
+
+ static IEnumerable GetParameters(Type type)
+ {
+ return type
+ .GetProperties()
+ .Select(p => new
+ {
+ Property = p,
+ ParameterAttribute = GetSingleAttributeOrNull(p),
+ DescriptionAttribute = GetSingleAttributeOrNull(p),
+ ExampleAttributes = p.GetCustomAttributes()
+ })
+ .Where(a => a.ParameterAttribute != null)
+ .Select(a => new Parameter(a.Property,
+ a.ParameterAttribute.Name,
+ a.ParameterAttribute.ShortName,
+ a.ParameterAttribute.Optional,
+ a.DescriptionAttribute?.DescriptionText,
+ a.ExampleAttributes.Select(e => e.ExampleValue),
+ a.ParameterAttribute.DefaultValue,
+ a.ParameterAttribute.AllowAppSetting,
+ a.ParameterAttribute.AllowConnectionString,
+ a.ParameterAttribute.AllowEnvironmentVariable))
+ .ToList();
+ }
+
+ static TAttribute GetSingleAttributeOrNull(PropertyInfo p) where TAttribute : Attribute
+ {
+ return p.GetCustomAttributes(typeof(TAttribute), false)
+ .Cast()
+ .FirstOrDefault();
+ }
+
+ public string Group { get; }
+
+ public string Command { get; }
+
+ public Type Type => _commandInstance.GetType();
+
+ public IEnumerable Parameters => _parameters;
+
+ public string Description => Type.GetCustomAttribute()?.DescriptionText ??
+ "(no help text for this command)";
+
+ public ICommand CommandInstance => _commandInstance;
+
+ public void Invoke(IEnumerable switches, EnvironmentSettings environmentSettings)
+ {
+ try
+ {
+ InnerInvoke(switches, environmentSettings);
+ }
+ finally
+ {
+ _releaser(CommandInstance);
+ }
+ }
+
+ void InnerInvoke(IEnumerable switches, EnvironmentSettings environmentSettings)
+ {
+ var commandInstance = _commandInstance;
+
+ var requiredParametersMissing = Parameters
+ .Where(p => !p.Optional
+ && !p.HasDefaultValue
+ && !CanBeResolvedFromSwitches(switches, p)
+ && !CanBeResolvedFromEnvironmentSettings(environmentSettings, p))
+ .ToList();
+
+ var optionalParamtersNotSpecified = Parameters
+ .Where(p => p.Optional
+ && !CanBeResolvedFromSwitches(switches, p)
+ && !CanBeResolvedFromEnvironmentSettings(environmentSettings, p))
+ .ToList();
+
+ if (requiredParametersMissing.Any())
+ {
+ var requiredParametersMissingString = string.Join(Environment.NewLine,
+ requiredParametersMissing.Select(p => $" {_settings.SwitchPrefix}{p.Name} - {p.DescriptionText}"));
+
+ var text = $@"The following required parameters are missing:
+
+{requiredParametersMissingString}";
+
+ if (optionalParamtersNotSpecified.Any())
+ {
+ var optionalParamtersNotSpecifiedString = string.Join(Environment.NewLine,
+ optionalParamtersNotSpecified.Select(p => $" {_settings.SwitchPrefix}{p.Name} - {p.DescriptionText}"));
+
+ var moreText = $@"The following optional parameters are also available:
+
+{optionalParamtersNotSpecifiedString}";
+
+ throw new GoCommandoException(string.Concat(
+ text,
+ Environment.NewLine,
+ Environment.NewLine,
+ moreText
+ ));
+ }
+
+ throw new GoCommandoException(text);
+ }
+
+ var switchesWithoutMathingParameter = switches
+ .Where(s => !Parameters.Any(p => p.MatchesKey(s.Key)))
+ .ToList();
+
+ if (switchesWithoutMathingParameter.Any())
+ {
+ var switchesWithoutMathingParameterString = string.Join(Environment.NewLine,
+ switchesWithoutMathingParameter.Select(p => p.Value != null
+ ? $" {_settings.SwitchPrefix}{p.Key} = {p.Value}"
+ : $" {_settings.SwitchPrefix}{p.Key}"));
+
+ throw new GoCommandoException(
+ $@"The following switches do not have a corresponding parameter:
+
+{switchesWithoutMathingParameterString}");
+ }
+
+ var setParameters = new HashSet();
+
+ ResolveParametersFromSwitches(switches, commandInstance, setParameters);
+
+ ResolveParametersFromEnvironmentSettings(environmentSettings, commandInstance, setParameters, Parameters);
+
+ ResolveParametersWithDefaultValues(setParameters, commandInstance);
+
+ commandInstance.Run();
+ }
+
+ static void ResolveParametersFromEnvironmentSettings(EnvironmentSettings environmentSettings, ICommand commandInstance, HashSet setParameters, IEnumerable parameters)
+ {
+ foreach (var parameter in parameters.Where(p => p.AllowAppSetting && !setParameters.Contains(p)))
+ {
+ if (!environmentSettings.HasAppSetting(parameter.Name)) continue;
+
+ var appSettingValue = environmentSettings.GetAppSetting(parameter.Name);
+
+ SetParameter(commandInstance, setParameters, parameter, appSettingValue);
+ }
+
+ foreach (var parameter in parameters.Where(p => p.AllowConnectionString && !setParameters.Contains(p)))
+ {
+ if (!environmentSettings.HasConnectionString(parameter.Name)) continue;
+
+ var appSettingValue = environmentSettings.GetConnectionString(parameter.Name);
+
+ SetParameter(commandInstance, setParameters, parameter, appSettingValue);
+ }
+
+ foreach (var parameter in parameters.Where(p => p.AllowEnvironmentVariable && !setParameters.Contains(p)))
+ {
+ if (!environmentSettings.HasEnvironmentVariable(parameter.Name)) continue;
+
+ var appSettingValue = environmentSettings.GetEnvironmentVariable(parameter.Name);
+
+ SetParameter(commandInstance, setParameters, parameter, appSettingValue);
+ }
+ }
+
+ void ResolveParametersWithDefaultValues(IEnumerable setParameters, ICommand commandInstance)
+ {
+ foreach (var parameterWithDefaultValue in Parameters.Where(p => p.HasDefaultValue).Except(setParameters))
+ {
+ parameterWithDefaultValue.ApplyDefaultValue(commandInstance);
+ }
+ }
+
+ void ResolveParametersFromSwitches(IEnumerable switches, ICommand commandInstance, ISet setParameters)
+ {
+ foreach (var switchToSet in switches)
+ {
+ var correspondingParameter = Parameters.FirstOrDefault(p => p.MatchesKey(switchToSet.Key));
+
+ if (correspondingParameter == null)
+ {
+ throw new GoCommandoException(
+ $"The switch {_settings}{switchToSet.Key} does not correspond to a parameter of the '{Command}' command!");
+ }
+
+ var value = switchToSet.Value;
+
+ SetParameter(commandInstance, setParameters, correspondingParameter, value);
+ }
+ }
+
+ static void SetParameter(ICommand commandInstance, ISet setParameters, Parameter parameter, string value)
+ {
+ parameter.SetValue(commandInstance, value);
+ setParameters.Add(parameter);
+ }
+
+ static bool CanBeResolvedFromEnvironmentSettings(EnvironmentSettings environmentSettings, Parameter parameter)
+ {
+ var name = parameter.Name;
+
+ if (parameter.AllowAppSetting && environmentSettings.HasAppSetting(name))
+ {
+ return true;
+ }
+
+ if (parameter.AllowConnectionString && environmentSettings.HasConnectionString(name))
+ {
+ return true;
+ }
+
+ if (parameter.AllowEnvironmentVariable && environmentSettings.HasEnvironmentVariable(name))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ static bool CanBeResolvedFromSwitches(IEnumerable switches, Parameter p)
+ {
+ return switches.Any(s => p.MatchesKey(s.Key));
+ }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/Internals/EnvironmentSettings.cs b/GoCommando_old/Internals/EnvironmentSettings.cs
new file mode 100644
index 0000000..dce6ff4
--- /dev/null
+++ b/GoCommando_old/Internals/EnvironmentSettings.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+
+namespace GoCommando.Internals
+{
+ class EnvironmentSettings
+ {
+ static readonly IDictionary None = new Dictionary();
+ public static readonly EnvironmentSettings Empty = new EnvironmentSettings(None, None);
+
+ readonly IDictionary _appSettings;
+ readonly IDictionary _connectionStrings;
+ readonly IDictionary _environmentVariables;
+
+ public EnvironmentSettings(IDictionary appSettings = null, IDictionary connectionStrings = null, IDictionary environmentVariables = null)
+ {
+ _environmentVariables = environmentVariables ?? None;
+ _appSettings = appSettings ?? None;
+ _connectionStrings = connectionStrings ?? None;
+ }
+
+ public bool HasAppSetting(string name)
+ {
+ return _appSettings.ContainsKey(name);
+ }
+
+ public bool HasConnectionString(string name)
+ {
+ return _connectionStrings.ContainsKey(name);
+ }
+
+ public bool HasEnvironmentVariable(string name)
+ {
+ return _environmentVariables.ContainsKey(name);
+ }
+
+ public string GetAppSetting(string key)
+ {
+ try
+ {
+ return _appSettings[key];
+ }
+ catch (Exception exception)
+ {
+ throw new KeyNotFoundException($"Could not find appSetting with key '{key}'", exception);
+ }
+ }
+
+ public string GetEnvironmentVariable(string name)
+ {
+ try
+ {
+ return _environmentVariables[name];
+ }
+ catch (Exception exception)
+ {
+ throw new KeyNotFoundException($"Could not find environment variable with the name '{name}'", exception);
+ }
+ }
+
+ public string GetConnectionString(string name)
+ {
+ try
+ {
+ return _connectionStrings[name];
+ }
+ catch (Exception exception)
+ {
+ throw new KeyNotFoundException($"Could not find connectionString with key '{name}'", exception);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/Internals/Parameter.cs b/GoCommando_old/Internals/Parameter.cs
new file mode 100644
index 0000000..14a2bb2
--- /dev/null
+++ b/GoCommando_old/Internals/Parameter.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace GoCommando.Internals
+{
+ class Parameter : IEquatable
+ {
+ public PropertyInfo PropertyInfo { get; }
+ public string Name { get; }
+ public string Shortname { get; }
+ public bool Optional { get; }
+ public string DescriptionText { get; }
+ public string DefaultValue { get; }
+ public bool AllowAppSetting { get; }
+ public bool AllowConnectionString { get; }
+ public bool AllowEnvironmentVariable { get; }
+ public string[] ExampleValues { get; }
+
+ public bool IsFlag => PropertyInfo.PropertyType == typeof(bool);
+
+ public bool HasDefaultValue => DefaultValue != null;
+
+ public Parameter(PropertyInfo propertyInfo, string name, string shortname, bool optional, string descriptionText, IEnumerable exampleValues, string defaultValue, bool allowAppSetting, bool allowConnectionString, bool allowEnvironmentVariable)
+ {
+ PropertyInfo = propertyInfo;
+ Name = name;
+ Shortname = shortname;
+ Optional = optional;
+ DescriptionText = GetText(descriptionText, allowAppSetting, allowConnectionString, allowEnvironmentVariable);
+ DefaultValue = defaultValue;
+ AllowAppSetting = allowAppSetting;
+ AllowConnectionString = allowConnectionString;
+ AllowEnvironmentVariable = allowEnvironmentVariable;
+ ExampleValues = exampleValues.ToArray();
+ }
+
+ private string GetText(string descriptionText, bool allowAppSetting, bool allowConnectionString, bool allowEnvironmentVariable)
+ {
+ if (!allowAppSetting && !allowConnectionString && !allowEnvironmentVariable)
+ {
+ return $"{descriptionText ?? ""}";
+ }
+
+ var autoBindings = new List();
+
+ if (allowEnvironmentVariable)
+ {
+ autoBindings.Add("ENV");
+ }
+
+ if (allowAppSetting)
+ {
+ autoBindings.Add("APP");
+ }
+
+ if (allowConnectionString)
+ {
+ autoBindings.Add("CONN");
+ }
+
+ return $"{descriptionText ?? ""} ({string.Join(", ", autoBindings)})";
+ }
+
+ public bool MatchesKey(string key)
+ {
+ return key == Name
+ || (Shortname != null && key == Shortname);
+ }
+
+ public void SetValue(object commandInstance, string value)
+ {
+ try
+ {
+ var valueInTheRightType = PropertyInfo.PropertyType == typeof(bool)
+ ? true
+ : Convert.ChangeType(value, PropertyInfo.PropertyType);
+
+ PropertyInfo.SetValue(commandInstance, valueInTheRightType);
+ }
+ catch (Exception exception)
+ {
+ throw new FormatException($"Could not set value '{value}' on property named '{PropertyInfo.Name}' on {PropertyInfo.DeclaringType}", exception);
+ }
+ }
+
+ public void ApplyDefaultValue(ICommand commandInstance)
+ {
+ if (!HasDefaultValue)
+ {
+ throw new InvalidOperationException($"Cannot apply default value of '{Name}' parameter because it has no default!");
+ }
+
+ SetValue(commandInstance, DefaultValue);
+ }
+
+ public bool Equals(Parameter other)
+ {
+ return Name.Equals(other.Name);
+ }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/Internals/Settings.cs b/GoCommando_old/Internals/Settings.cs
new file mode 100644
index 0000000..f9c1edb
--- /dev/null
+++ b/GoCommando_old/Internals/Settings.cs
@@ -0,0 +1,12 @@
+namespace GoCommando.Internals
+{
+ class Settings
+ {
+ public Settings()
+ {
+ SwitchPrefix = "-";
+ }
+
+ public string SwitchPrefix { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/Internals/StringExtensions.cs b/GoCommando_old/Internals/StringExtensions.cs
new file mode 100644
index 0000000..4f8aaaa
--- /dev/null
+++ b/GoCommando_old/Internals/StringExtensions.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Linq;
+using System.Text;
+
+namespace GoCommando.Internals
+{
+ static class StringExtensions
+ {
+ public static string WrappedAt(this string str, int width)
+ {
+ var twoLineBreaks = Environment.NewLine + Environment.NewLine;
+
+ var sections = str.Split(new[] { twoLineBreaks },
+ StringSplitOptions.RemoveEmptyEntries);
+
+ return string.Join(twoLineBreaks, sections.Select(section => WrapSection(section, width)));
+ }
+
+ static string WrapSection(string section, int width)
+ {
+ var oneLongString = string.Join(" ",
+ section.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries));
+
+ var words = oneLongString.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
+
+ var builder = new StringBuilder();
+
+ var currentLineLength = 0;
+
+ for (var index = 0; index < words.Length; index++)
+ {
+ var word = words[index];
+ builder.Append(word);
+ currentLineLength += word.Length;
+
+ if (index < words.Length - 1)
+ {
+ var nextWord = words[index];
+
+ var spaceLeftOnCurrentLine = width - currentLineLength - 1; // -1 to leave room for space...
+ var nextWordIsTooLong = nextWord.Length > spaceLeftOnCurrentLine;
+
+ if (nextWordIsTooLong)
+ {
+ builder.AppendLine();
+ currentLineLength = 0;
+ }
+ else
+ {
+ builder.Append(" ");
+ currentLineLength++;
+ }
+ }
+ }
+
+ return builder.ToString();
+ }
+
+ public static string Indented(this string str, int indent)
+ {
+ var indentedLines = str
+ .Split(new[] { Environment.NewLine }, StringSplitOptions.None)
+ .Select(line => string.Concat(new string(' ', indent), line));
+
+ return string.Join(Environment.NewLine, indentedLines);
+ }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/Internals/Switch.cs b/GoCommando_old/Internals/Switch.cs
new file mode 100644
index 0000000..185c6f8
--- /dev/null
+++ b/GoCommando_old/Internals/Switch.cs
@@ -0,0 +1,55 @@
+// ReSharper disable LoopCanBeConvertedToQuery
+namespace GoCommando.Internals
+{
+ class Switch
+ {
+ static readonly char[] AcceptedQuoteCharacters = { '"', '\'' };
+
+ public static Switch KeyValue(string key, string value)
+ {
+ return new Switch(key, value);
+ }
+
+ public static Switch Flag(string key)
+ {
+ return new Switch(key, null);
+ }
+
+ Switch(string key, string value)
+ {
+ Key = key;
+ Value = Unquote(value);
+ }
+
+ static string Unquote(string value)
+ {
+ if (value == null) return null;
+
+ // can't be quoted
+ if (value.Length < 2) return value;
+
+ foreach (var quoteChar in AcceptedQuoteCharacters)
+ {
+ var quote = quoteChar.ToString();
+
+ if (value.StartsWith(quote)
+ && value.EndsWith(quote))
+ {
+ return value.Substring(1, value.Length - 2);
+ }
+ }
+
+ return value;
+ }
+
+ public string Key { get; }
+ public string Value { get; }
+
+ public override string ToString()
+ {
+ return Value == null
+ ? Key
+ : $"{Key} = {Value}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando_old/ParameterAttribute.cs b/GoCommando_old/ParameterAttribute.cs
new file mode 100644
index 0000000..e8b5c39
--- /dev/null
+++ b/GoCommando_old/ParameterAttribute.cs
@@ -0,0 +1,79 @@
+using System;
+
+namespace GoCommando
+{
+ ///
+ /// Apply this attribute to a property of a command class (i.e. one that implements )
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public class ParameterAttribute : Attribute
+ {
+ ///
+ /// Gets the primary parameter name
+ ///
+ public string Name { get; }
+
+ ///
+ /// Gets a shorthand for the parameter (or null if none has been specified)
+ ///
+ public string ShortName { get; }
+
+ ///
+ /// Gets whether this parameter is optional
+ ///
+ public bool Optional { get; }
+
+ ///
+ /// Gets a default value for the parameter (or null if none has been specified)
+ ///
+ public string DefaultValue { get; }
+
+ ///
+ /// Gets whether this parameter can have its value resolved from the <appSettings>
section of the application configuration file.
+ /// If the value is provided with a command-line switch, the provided value takes precedence.
+ ///
+ public bool AllowAppSetting { get; }
+
+ ///
+ /// Gets whether this parameter can have its value resolved from the <connectionStrings>
section of the application configuration file.
+ /// If the value is provided with a command-line switch, the provided value takes precedence.
+ ///
+ public bool AllowConnectionString { get; }
+
+ ///
+ /// Gets whether this parameter can have its value resolved from an environment variable with the same name as specified by
+ /// If the value is provided with a command-line switch, the provided value takes precedence.
+ ///
+ public bool AllowEnvironmentVariable { get; }
+
+ ///
+ /// Constructs the parameter attribute
+ ///
+ /// Primary name of the parameter
+ /// Optional shorthand of the parameter
+ /// Indicates whether the parameter MUST be specified or can be omitted
+ /// Provides a default value to use when other values could not be found
+ ///
+ /// Indicates whether parameter value resolution can go and look in the <appSettings>
section of
+ /// the current application configuration file for a value. Will look for the key specified by
+ ///
+ ///
+ /// Indicates whether parameter value resolution can go and look in the <connectionStrings>
section of
+ /// the current application configuration file for a value. Will look for the name specified by
+ ///
+ ///
+ /// Indicates whether parameter value resolution can go and look for an environment variable for a value.
+ /// Will look for the name specified by
+ ///
+ public ParameterAttribute(string name, string shortName = null, bool optional = false, string defaultValue = null, bool allowAppSetting = false, bool allowConnectionString = false, bool allowEnvironmentVariable = false)
+ {
+ Name = name;
+ ShortName = shortName;
+ Optional = optional;
+ DefaultValue = defaultValue;
+ AllowAppSetting = allowAppSetting;
+ AllowConnectionString = allowConnectionString;
+ AllowEnvironmentVariable = allowEnvironmentVariable;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GoCommando/Properties/AssemblyInfo.cs b/GoCommando_old/Properties/AssemblyInfo.cs
similarity index 100%
rename from GoCommando/Properties/AssemblyInfo.cs
rename to GoCommando_old/Properties/AssemblyInfo.cs
diff --git a/GoCommando_old/SupportImpersonationAttribute.cs b/GoCommando_old/SupportImpersonationAttribute.cs
new file mode 100644
index 0000000..a657567
--- /dev/null
+++ b/GoCommando_old/SupportImpersonationAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace GoCommando
+{
+ ///
+ /// Apply this attribute to the class that has your Main
method in order to support impersonation
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+ public class SupportImpersonationAttribute : Attribute
+ {
+ }
+}
\ No newline at end of file
diff --git a/TestApp2/App.config b/TestApp2_old/App.config
similarity index 100%
rename from TestApp2/App.config
rename to TestApp2_old/App.config
diff --git a/TestApp2/ElCommandante.cs b/TestApp2_old/ElCommandante.cs
similarity index 100%
rename from TestApp2/ElCommandante.cs
rename to TestApp2_old/ElCommandante.cs
diff --git a/TestApp2/Program.cs b/TestApp2_old/Program.cs
similarity index 100%
rename from TestApp2/Program.cs
rename to TestApp2_old/Program.cs
diff --git a/TestApp2/Properties/AssemblyInfo.cs b/TestApp2_old/Properties/AssemblyInfo.cs
similarity index 100%
rename from TestApp2/Properties/AssemblyInfo.cs
rename to TestApp2_old/Properties/AssemblyInfo.cs
diff --git a/TestApp2/TestApp2.csproj b/TestApp2_old/TestApp2.csproj
similarity index 92%
rename from TestApp2/TestApp2.csproj
rename to TestApp2_old/TestApp2.csproj
index b4e8c44..adb1d2f 100644
--- a/TestApp2/TestApp2.csproj
+++ b/TestApp2_old/TestApp2.csproj
@@ -49,12 +49,6 @@
-
-
- {2839AA55-B40A-4BB8-BDA0-C5057E5A683F}
- GoCommando
-
-