Skip to content

Commit

Permalink
Merge branch 'ExposeValueAndErrorOnParserResult' of https://github.co…
Browse files Browse the repository at this point in the history
…m/johnjaylward/commandline into johnjaylward-ExposeValueAndErrorOnParserResult
  • Loading branch information
moh-hassan committed Jun 15, 2020
2 parents 4d00c9e + d3b6315 commit 25315f2
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 29 deletions.
57 changes: 28 additions & 29 deletions src/CommandLine/ParserResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace CommandLine
public sealed class TypeInfo
{
private readonly Type current;
private readonly IEnumerable<Type> choices;
private readonly IEnumerable<Type> choices;

private TypeInfo(Type current, IEnumerable<Type> choices)
{
Expand Down Expand Up @@ -64,10 +64,20 @@ public abstract class ParserResult<T>
private readonly ParserResultType tag;
private readonly TypeInfo typeInfo;

internal ParserResult(ParserResultType tag, TypeInfo typeInfo)
internal ParserResult(IEnumerable<Error> errors, TypeInfo typeInfo)
{
this.tag = tag;
this.typeInfo = typeInfo;
this.tag = ParserResultType.NotParsed;
this.typeInfo = typeInfo ?? TypeInfo.Create(typeof(T));
Errors = errors ?? new Error[0];
Value = default;
}

internal ParserResult(T value, TypeInfo typeInfo)
{
Value = value ?? throw new ArgumentNullException(nameof(value));
this.tag = ParserResultType.Parsed;
this.typeInfo = typeInfo ?? TypeInfo.Create(value.GetType());
Errors = new Error[0];
}

/// <summary>
Expand All @@ -82,6 +92,16 @@ public TypeInfo TypeInfo
{
get { return typeInfo; }
}

/// <summary>
/// Gets the instance with parsed values. If one or more errors occures, <see langword="default"/> is returned.
/// </summary>
public T Value { get; }

/// <summary>
/// Gets the sequence of parsing errors. If there are no errors, then an empty IEnumerable is returned.
/// </summary>
public IEnumerable<Error> Errors { get; }
}

/// <summary>
Expand All @@ -90,26 +110,16 @@ public TypeInfo TypeInfo
/// <typeparam name="T">The type with attributes that define the syntax of parsing rules.</typeparam>
public sealed class Parsed<T> : ParserResult<T>, IEquatable<Parsed<T>>
{
private readonly T value;

internal Parsed(T value, TypeInfo typeInfo)
: base(ParserResultType.Parsed, typeInfo)
: base(value, typeInfo)
{
this.value = value;
}

internal Parsed(T value)
: this(value, TypeInfo.Create(value.GetType()))
{
}

/// <summary>
/// Gets the instance with parsed values.
/// </summary>
public T Value
{
get { return value; }
}

/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to the current <see cref="System.Object"/>.
Expand All @@ -118,8 +128,7 @@ public T Value
/// <returns><value>true</value> if the specified <see cref="System.Object"/> is equal to the current <see cref="System.Object"/>; otherwise, <value>false</value>.</returns>
public override bool Equals(object obj)
{
var other = obj as Parsed<T>;
if (other != null)
if (obj is Parsed<T> other)
{
return Equals(other);
}
Expand Down Expand Up @@ -159,21 +168,12 @@ public bool Equals(Parsed<T> other)
/// <typeparam name="T">The type with attributes that define the syntax of parsing rules.</typeparam>
public sealed class NotParsed<T> : ParserResult<T>, IEquatable<NotParsed<T>>
{
private readonly IEnumerable<Error> errors;

internal NotParsed(TypeInfo typeInfo, IEnumerable<Error> errors)
: base(ParserResultType.NotParsed, typeInfo)
: base(errors, typeInfo)
{
this.errors = errors;
}

/// <summary>
/// Gets the sequence of parsing errors.
/// </summary>
public IEnumerable<Error> Errors
{
get { return errors; }
}

/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to the current <see cref="System.Object"/>.
Expand All @@ -182,8 +182,7 @@ public IEnumerable<Error> Errors
/// <returns><value>true</value> if the specified <see cref="System.Object"/> is equal to the current <see cref="System.Object"/>; otherwise, <value>false</value>.</returns>
public override bool Equals(object obj)
{
var other = obj as NotParsed<T>;
if (other != null)
if (obj is NotParsed<T> other)
{
return Equals(other);
}
Expand Down
108 changes: 108 additions & 0 deletions tests/CommandLine.Tests/Unit/Issue543Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System.Collections.Generic;
using System.Linq;
using Xunit;
using CommandLine.Text;

namespace CommandLine.Tests.Unit
{
//Reference: PR# 634
public class Issue543Tests
{

private const int ERROR_SUCCESS = 0;

[Fact]
public void Parser_GiveHelpArgument_ExpectSuccess()
{
var result = Parser.Default.ParseArguments<Options>(new[] { "--help" });

Assert.Equal(ParserResultType.NotParsed, result.Tag);
Assert.Null(result.Value);
Assert.NotEmpty(result.Errors);
}

[Fact]
public void Parser_GiveConnectionStringAndJobId_ExpectSuccess()
{
var result = Parser.Default.ParseArguments<Options>(new[] {
"-c", "someConnectionString",
"-j", "1234",
});

Assert.Equal(ParserResultType.Parsed, result.Tag);
Assert.NotNull(result.Value);
Assert.Empty(result.Errors);
Assert.Equal("someConnectionString", result.Value.ConnectionString);
Assert.Equal(1234, result.Value.JobId);
}

[Fact]
public void Parser_GiveVerb1_ExpectSuccess()
{
var result = Parser.Default.ParseArguments<Verb1Options, Verb2Options>(new[] {
"verb1",
"-j", "1234",
});

Assert.Equal(ParserResultType.Parsed, result.Tag);
Assert.Empty(result.Errors);
Assert.NotNull(result.Value);
Assert.NotNull(result.Value as Verb1Options);
Assert.Equal(1234, (result.Value as Verb1Options).JobId);
}

[Fact]
public void Parser_GiveVerb2_ExpectSuccess()
{
var result = Parser.Default.ParseArguments<Verb1Options, Verb2Options>(new[] {
"verb2",
"-c", "someConnectionString",
});

Assert.Equal(ParserResultType.Parsed, result.Tag);
Assert.Empty(result.Errors);
Assert.NotNull(result.Value);
Assert.NotNull(result.Value as Verb2Options);
Assert.Equal("someConnectionString", (result.Value as Verb2Options).ConnectionString);
}

// Options
internal class Options
{
[Option('c', "connectionString", Required = true, HelpText = "Texts.ExplainConnection")]
public string ConnectionString { get; set; }

[Option('j', "jobId", Required = true, HelpText = "Texts.ExplainJob")]
public int JobId { get; set; }

[Usage(ApplicationAlias = "Importer.exe")]
public static IEnumerable<Example> Examples
{
get => new[] {
new Example("Texts.ExplainExampleExecution", new Options() {
ConnectionString="Server=MyServer;Database=MyDatabase",
JobId = 5
}),
};
}
}

// Options
[Verb("verb1")]
internal class Verb1Options
{
[Option('j', "jobId", Required = false, HelpText = "Texts.ExplainJob")]
public int JobId { get; set; }
}

// Options
[Verb("verb2")]
internal class Verb2Options
{
[Option('c', "connectionString", Required = false, HelpText = "Texts.ExplainConnection")]
public string ConnectionString { get; set; }
}

}
}

0 comments on commit 25315f2

Please sign in to comment.