Skip to content

Commit

Permalink
Add support for reading positional command-line arguments.
Browse files Browse the repository at this point in the history
  • Loading branch information
arodus authored and matkoch committed Mar 24, 2018
1 parent aacce32 commit 1319395
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 39 deletions.
21 changes: 18 additions & 3 deletions source/Nuke.Core.Tests/ParameterServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@
// https://github.com/nuke-build/nuke/blob/master/LICENSE

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using FluentAssertions;
using Nuke.Core.Execution;
using Nuke.Core.Utilities.Collections;
using Xunit;

namespace Nuke.Core.Tests
{
public class ParameterServiceTest
{
private ParameterService GetService(string[] commandLineArguments = null, IDictionary environmentVariables = null)
private ParameterService GetService(string[] commandLineArguments = null, IDictionary<string, string> environmentVariables = null)
{
commandLineArguments = commandLineArguments ?? new string[0];
environmentVariables = environmentVariables ?? new Dictionary<string, string>();

return new ParameterService(() => commandLineArguments, () => environmentVariables);
return new ParameterService(commandLineArguments, environmentVariables.AsReadOnly());
}

[Theory]
Expand Down Expand Up @@ -121,5 +121,20 @@ public void TestConversionCollections()
service.GetParameter<string[]>("files").Should().BeEquivalentTo("C:\\new folder\\file.txt", "C:\\file.txt");
service.GetParameter<string[]>("values", separator: '+').Should().BeEquivalentTo("A", "B", "C");
}

[Theory]
[InlineData(new[] { "arg1" }, 0, typeof(string), "arg1")]
[InlineData(new[] { "true" }, 0, typeof(bool), true)]
[InlineData(new[] { "arg1" }, 1, typeof(string), null)]
[InlineData(new[] { "arg1", "arg2" }, 1, typeof(string), "arg2")]
[InlineData(new[] { "posArg", "-NamedArg", "value" }, 0, typeof(string), "posArg")]
[InlineData(new[] { "posArg", "-NamedArg", "value" }, 1, typeof(string), null)]
[InlineData(new[] { "posArg", "-NamedArg", "value" }, 2, typeof(string), null)]
[InlineData(new[] { "arg1", "arg2", "arg3" }, -1, typeof(string), "arg3")]
public void TestPositionalCommandLineArguments(string[] commandLineArgs, int position, Type destinationType, object expectedValue)
{
var service = GetService(commandLineArgs);
service.GetCommandLineArgument(position, destinationType).Should().Be(expectedValue);
}
}
}
6 changes: 3 additions & 3 deletions source/Nuke.Core/EnvironmentInfo.Internal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ internal static string[] InvokedTargets
{
get
{
var argument = Environment.GetCommandLineArgs()
.Skip(count: 1).Take(count: 1)
.SingleOrDefault(x => !x.StartsWith("-"));
var argument = ParameterService.Instance.GetCommandLineArgument<string>(position: 1);
argument = argument == null || argument.StartsWith("-") ? null : argument;

if (argument != null)
return argument.Split(new[] { '+' }, StringSplitOptions.RemoveEmptyEntries);

Expand Down
26 changes: 12 additions & 14 deletions source/Nuke.Core/EnvironmentInfo.Parameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ namespace Nuke.Core
[DebuggerStepThrough]
public static partial class EnvironmentInfo
{
private static readonly ParameterService s_parameterService = new ParameterService();

public static void SetVariable(string name, string value)
{
Environment.SetEnvironmentVariable(name, value);
Expand All @@ -36,7 +34,7 @@ public static void SetVariable<T>(string name, IEnumerable<T> values, char separ
/// </summary>
public static bool ParameterSwitch(string name)
{
return s_parameterService.GetParameter<bool>(name);
return ParameterService.Instance.GetParameter<bool>(name);
}

/// <summary>
Expand All @@ -45,7 +43,7 @@ public static bool ParameterSwitch(string name)
[CanBeNull]
public static string Parameter(string name)
{
return s_parameterService.GetParameter<string>(name);
return ParameterService.Instance.GetParameter<string>(name);
}

/// <summary>
Expand All @@ -54,7 +52,7 @@ public static string Parameter(string name)
[CanBeNull]
public static T Parameter<T>(string name)
{
return s_parameterService.GetParameter<T>(name);
return ParameterService.Instance.GetParameter<T>(name);
}

/// <summary>
Expand All @@ -63,7 +61,7 @@ public static T Parameter<T>(string name)
[CanBeNull]
public static T[] ParameterSet<T>(string name, char? separator = null)
{
return s_parameterService.GetParameter<T[]>(name, separator);
return ParameterService.Instance.GetParameter<T[]>(name, separator);
}

/// <summary>
Expand Down Expand Up @@ -99,7 +97,7 @@ public static T[] EnsureParameterSet<T>(string name, char? separator = null)
/// </summary>
public static bool VariableSwitch(string name)
{
return s_parameterService.GetEnvironmentVariable<bool>(name);
return ParameterService.Instance.GetEnvironmentVariable<bool>(name);
}

/// <summary>
Expand All @@ -108,7 +106,7 @@ public static bool VariableSwitch(string name)
[CanBeNull]
public static string Variable(string name)
{
return s_parameterService.GetEnvironmentVariable<string>(name);
return ParameterService.Instance.GetEnvironmentVariable<string>(name);
}

/// <summary>
Expand All @@ -117,7 +115,7 @@ public static string Variable(string name)
[CanBeNull]
public static T Variable<T>(string name)
{
return s_parameterService.GetEnvironmentVariable<T>(name);
return ParameterService.Instance.GetEnvironmentVariable<T>(name);
}

/// <summary>
Expand All @@ -126,7 +124,7 @@ public static T Variable<T>(string name)
[CanBeNull]
public static T[] VariableSet<T>(string name, char? separator = null)
{
return s_parameterService.GetEnvironmentVariable<T[]>(name, separator);
return ParameterService.Instance.GetEnvironmentVariable<T[]>(name, separator);
}

/// <summary>
Expand Down Expand Up @@ -162,7 +160,7 @@ public static T[] EnsureVariableSet<T>(string name, char? separator = null)
/// </summary>
public static bool ArgumentSwitch(string name)
{
return s_parameterService.GetCommandLineArgument<bool>(name);
return ParameterService.Instance.GetCommandLineArgument<bool>(name);
}

/// <summary>
Expand All @@ -171,7 +169,7 @@ public static bool ArgumentSwitch(string name)
[CanBeNull]
public static string Argument(string name)
{
return s_parameterService.GetCommandLineArgument<string>(name);
return ParameterService.Instance.GetCommandLineArgument<string>(name);
}

/// <summary>
Expand All @@ -180,7 +178,7 @@ public static string Argument(string name)
[CanBeNull]
public static T Argument<T>(string name)
{
return s_parameterService.GetCommandLineArgument<T>(name);
return ParameterService.Instance.GetCommandLineArgument<T>(name);
}

/// <summary>
Expand All @@ -189,7 +187,7 @@ public static T Argument<T>(string name)
[CanBeNull]
public static T[] ArgumentSet<T>(string name, char? separator = null)
{
return s_parameterService.GetCommandLineArgument<T[]>(name, separator);
return ParameterService.Instance.GetCommandLineArgument<T[]>(name, separator);
}

/// <summary>
Expand Down
69 changes: 53 additions & 16 deletions source/Nuke.Core/Execution/ParameterService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// https://github.com/nuke-build/nuke/blob/master/LICENSE

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
Expand All @@ -15,16 +14,21 @@ namespace Nuke.Core.Execution
{
public class ParameterService
{
private readonly Func<string[]> _commandLineArgumentsProvider;
private readonly Func<IDictionary> _environmentVariablesProvider;
private static ParameterService s_instance;

public ParameterService(Func<string[]> commandLineArgumentsProvider = null, Func<IDictionary> environmentVariablesProvider = null)
private readonly string[] _commandLineArguments;
private readonly Func<IReadOnlyDictionary<string, string>> _environmentVariablesProvider;

public ParameterService(
[CanBeNull] string[] commandLineArguments = null,
[CanBeNull] IReadOnlyDictionary<string, string> environmentVariables = null)
{
// TODO: refactor to IDictionary<string, string> and string[]
_environmentVariablesProvider = environmentVariablesProvider ?? Environment.GetEnvironmentVariables;
_commandLineArgumentsProvider = commandLineArgumentsProvider ?? Environment.GetCommandLineArgs;
_environmentVariablesProvider = () => environmentVariables ?? EnvironmentInfo.Variables;
_commandLineArguments = commandLineArguments ?? EnvironmentInfo.CommandLineArguments;
}

public static ParameterService Instance => s_instance ?? (s_instance = new ParameterService());

[CanBeNull]
public T GetParameter<T>(string parameterName, char? separator = null)
{
Expand All @@ -37,6 +41,12 @@ public T GetCommandLineArgument<T>(string parameterName, char? separator = null)
return (T) GetCommandLineArgument(parameterName, typeof(T), separator);
}

[CanBeNull]
public T GetCommandLineArgument<T>(int position, char? separator = null)
{
return (T) GetCommandLineArgument(position, typeof(T), separator);
}

[CanBeNull]
public T GetEnvironmentVariable<T>(string parameterName, char? separator = null)
{
Expand All @@ -54,12 +64,41 @@ public object GetParameter(string parameterName, Type destinationType, char? sep
[CanBeNull]
public object GetCommandLineArgument(string argumentName, Type destinationType, char? separator = null)
{
var args = _commandLineArgumentsProvider.Invoke();
var index = Array.FindLastIndex(args, x => x.EqualsOrdinalIgnoreCase($"-{argumentName}"));
var index = Array.FindLastIndex(_commandLineArguments, x => x.EqualsOrdinalIgnoreCase($"-{argumentName}"));
if (index == -1)
return GetDefaultValue(destinationType);

var values = args.Skip(index + 1).TakeWhile(x => !x.StartsWith("-")).ToArray();
var values = _commandLineArguments.Skip(index + 1).TakeWhile(x => !x.StartsWith("-")).ToArray();
return ConvertCommandLineArguments(argumentName, values, destinationType, _commandLineArguments, separator);
}

[CanBeNull]
public object GetCommandLineArgument(int position, Type destinationType, char? separator = null)
{
var positionalParametersCount = _commandLineArguments.TakeWhile(x => !x.StartsWith("-")).Count();

if (position < 0)
position = positionalParametersCount - 1;

if (positionalParametersCount <= position)
return null;

return ConvertCommandLineArguments(
$"positional[{position}]",
new[] { _commandLineArguments[position] },
destinationType,
_commandLineArguments,
separator);
}

[CanBeNull]
public object ConvertCommandLineArguments(
string argumentName,
string[] values,
Type destinationType,
string[] commandLineArguments,
char? separator = null)
{
ControlFlow.Assert(values.Length == 1 || !separator.HasValue || values.All(x => !x.Contains(separator.Value)),
$"Command-line argument '{argumentName}' with value [ {values.JoinComma()} ] cannot be split with separator '{separator}'.");
values = separator.HasValue && values.Any(x => x.Contains(separator.Value))
Expand All @@ -74,7 +113,7 @@ public object GetCommandLineArgument(string argumentName, Type destinationType,
{
ControlFlow.Fail(
new[] { ex.Message, "Command-line arguments were:" }
.Concat(args.Select((x, i) => $" [{i}] = {x}"))
.Concat(commandLineArguments.Select((x, i) => $" [{i}] = {x}"))
.JoinNewLine());
// ReSharper disable once HeuristicUnreachableCode
return null;
Expand All @@ -83,16 +122,15 @@ public object GetCommandLineArgument(string argumentName, Type destinationType,

private bool HasCommandLineArgument(string argumentName)
{
var args = _commandLineArgumentsProvider.Invoke();
return Array.FindLastIndex(args, x => x.EqualsOrdinalIgnoreCase($"-{argumentName}")) != -1;
return Array.FindLastIndex(_commandLineArguments, x => x.EqualsOrdinalIgnoreCase($"-{argumentName}")) != -1;
}

[CanBeNull]
public object GetEnvironmentVariable(string variableName, Type destinationType, char? separator = null)
{
var variables = _environmentVariablesProvider.Invoke();
var value = variables.Contains(variableName) ? (string) variables[variableName] : null;
if (value == null)

if (!variables.TryGetValue(variableName, out var value))
return GetDefaultValue(destinationType);

try
Expand All @@ -112,7 +150,6 @@ private object GetDefaultValue(Type type)
{
if (Nullable.GetUnderlyingType(type) == null && type != typeof(string) && !type.IsArray)
return Activator.CreateInstance(type);

return null;
}

Expand Down
4 changes: 1 addition & 3 deletions source/Nuke.Core/ParameterAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ namespace Nuke.Core
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
public class ParameterAttribute : InjectionAttributeBase
{
private static readonly ParameterService s_parameterService = new ParameterService();

public ParameterAttribute(string description = null)
{
Description = description;
Expand All @@ -56,7 +54,7 @@ public override object GetValue(string memberName, Type memberType)
!memberType.IsArray
? typeof(Nullable<>).MakeGenericType(memberType)
: memberType;
return s_parameterService.GetParameter(Name ?? memberName, memberType, (Separator ?? string.Empty).SingleOrDefault());
return ParameterService.Instance.GetParameter(Name ?? memberName, memberType, (Separator ?? string.Empty).SingleOrDefault());
}
}
}

0 comments on commit 1319395

Please sign in to comment.