Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions src/Tests/ArgParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,100 @@ public void Version_with_a_value_and_no_package_is_still_an_error()
Assert.Contains("PACKAGE_NAME", parsed.Error);
}

[Theory]
[InlineData("stop", "--version")]
[InlineData("stop", "--VERSION")]
public void Bare_version_after_a_package_is_forwarded_to_the_tool(params string[] args)
{
var parsed = ArgParser.Parse(args);
var viaSeparator = ArgParser.Parse(args[0], "--", args[1]);

Assert.True(parsed.Success);
Assert.False(parsed.ShowVersion);
Assert.Equal("stop", parsed.PackageId);
Assert.Null(parsed.Version);
Assert.Equal(viaSeparator.ForwardedArguments, parsed.ForwardedArguments);
Assert.Equal([args[1]], parsed.ForwardedArguments);
}

[Fact]
public void Bare_version_after_a_pinned_package_keeps_the_pin_and_forwards()
{
var parsed = ArgParser.Parse("stop@1.2.3", "--version");

Assert.True(parsed.Success);
Assert.Equal("stop", parsed.PackageId);
Assert.Equal("1.2.3", parsed.Version);
Assert.Equal(["--version"], parsed.ForwardedArguments);
}

[Fact]
public void Bare_version_after_other_options_still_forwards_only_the_flag()
{
var parsed = ArgParser.Parse(
"stop", "--prerelease", "--source", "https://feed.example", "--version");

Assert.True(parsed.Success);
Assert.False(parsed.ShowVersion);
Assert.Equal("stop", parsed.PackageId);
Assert.Null(parsed.Version);
Assert.True(parsed.Prerelease);
Assert.Equal(["https://feed.example"], parsed.Sources);
Assert.Equal(["--version"], parsed.ForwardedArguments);
}

[Fact]
public void Version_value_after_a_package_still_selects_that_version()
{
var parsed = ArgParser.Parse("stop", "--version", "1.2.3", "--", "--version");

Assert.True(parsed.Success);
Assert.Equal("stop", parsed.PackageId);
Assert.Equal("1.2.3", parsed.Version);
Assert.Equal(["--version"], parsed.ForwardedArguments);
}

[Fact]
public void Second_bare_version_is_forwarded_when_the_first_already_has_a_value()
{
var parsed = ArgParser.Parse("stop", "--version", "1.2.3", "--version");

Assert.True(parsed.Success);
Assert.Equal("1.2.3", parsed.Version);
Assert.Equal(["--version"], parsed.ForwardedArguments);
}

[Theory]
[InlineData("--yes", "--version")]
[InlineData("--update", "--version")]
[InlineData("--update", "1.2.3", "--version")]
public void Bare_version_without_a_package_still_requires_a_value(params string[] args)
{
var parsed = ArgParser.Parse(args);

Assert.False(parsed.Success);
Assert.False(parsed.ShowVersion);
Assert.Contains("Missing value for --version", parsed.Error);
}

[Fact]
public void Version_followed_by_another_token_is_still_that_tokens_value()
{
var parsed = ArgParser.Parse("stop", "--version", "--help");

Assert.False(parsed.Success);
Assert.Contains("Invalid version '--help'", parsed.Error);
}

[Fact]
public void Empty_version_assignment_is_still_missing_a_value()
{
var parsed = ArgParser.Parse("stop", "--version=");

Assert.False(parsed.Success);
Assert.Contains("Missing value for --version", parsed.Error);
}

[Fact]
public void Update_alone_is_a_self_update_to_latest()
{
Expand Down
22 changes: 18 additions & 4 deletions src/ndx/ArgParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ namespace ndx;
/// dnx.cmd-compatible argv split: first operand is PACKAGE[@VERSION], listed
/// flags are consumed by ndx, everything else (including tokens after --) is
/// forwarded to the child. <c>--update [VERSION]</c> is a standalone self-update.
/// A lone <c>--version</c> prints the ndx version.
/// A lone <c>--version</c> prints the ndx version. After a package id, a
/// <c>--version</c> with no value is forwarded to the tool, as in
/// <c>ndx stop -- --version</c>.
/// </summary>
public static class ArgParser
{
Expand Down Expand Up @@ -106,7 +108,18 @@ public static Invocation Parse(IReadOnlyList<string> args)
if (value is null)
{
if (i + 1 >= args.Count)
{
// `ndx stop --version` names the tool, not a package version.
// A following token is still the value (`--version 1.2.3`,
// or an invalid one such as `--version --help`).
if (IsVersionOption(option) && packageId is not null && !update)
{
forwarded.Add(arg);
continue;
}

return Invocation.Failed($"Missing value for {option}.");
}

value = args[++i];
}
Expand Down Expand Up @@ -332,11 +345,12 @@ static bool TryParseIdentity(string token, out string? packageId, out string? ve
static bool IsBareVersionFlag(string token)
{
var (option, inline) = SplitOption(token);
return option is not null
&& option.Equals("--version", StringComparison.OrdinalIgnoreCase)
&& inline is null;
return IsVersionOption(option) && inline is null;
}

static bool IsVersionOption(string? option)
=> option is not null && option.Equals("--version", StringComparison.OrdinalIgnoreCase);

static (string? Name, string? InlineValue) SplitOption(string token)
{
if (token.Length < 2 || token[0] != '-')
Expand Down
Loading