Skip to content

Commit 73d88d7

Browse files
authored
Add value transformers for parsed options (#47)
* Add generic IOptionBuilder<T> * Add unit test * Fix test * Add IOptionBuilder<T> for Command options as well. * Improve test coverage * Update packages * .NET Core 2.2 * Update to .NET Core 2.2 * Revert to .NET Core 2.0 otherwise tests don't run in VS 2019 * Update codecov * Update some config * Update config once more * Update xml location * Update nuspec
1 parent f3dfdb3 commit 73d88d7

23 files changed

+504
-147
lines changed

CommandLineParser.Tests/CommandLineParser.Tests.csproj

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp2.1</TargetFramework>
4+
<TargetFramework>netcoreapp2.0</TargetFramework>
55

66
<IsPackable>false</IsPackable>
77

@@ -11,14 +11,14 @@
1111
</PropertyGroup>
1212

1313
<ItemGroup>
14-
<PackageReference Include="coverlet.msbuild" Version="2.5.0">
14+
<PackageReference Include="coverlet.msbuild" Version="2.6.3">
1515
<PrivateAssets>all</PrivateAssets>
1616
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
1717
</PackageReference>
18-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
19-
<PackageReference Include="Moq" Version="4.10.0" />
20-
<PackageReference Include="xunit" Version="2.4.0" />
21-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0">
18+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
19+
<PackageReference Include="Moq" Version="4.12.0" />
20+
<PackageReference Include="xunit" Version="2.4.1" />
21+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
2222
<PrivateAssets>all</PrivateAssets>
2323
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
2424
</PackageReference>

CommandLineParser.Tests/CommandLineParserTests.cs

+75
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,76 @@ public void ConfigureTests()
459459
Assert.False(option.HasDefault);
460460
}
461461

462+
[Theory]
463+
[InlineData(new string[] { "" }, "defaulttransformed", false)]
464+
[InlineData(new string[] { "-m", "test" }, "testtransformed", false)]
465+
[InlineData(new string[] { "--message", "test" }, "testtransformed", false)]
466+
public void TransformationWorksAsExpected(string[] args, string expected, bool errors)
467+
{
468+
var parser = new CommandLineParser<AddOption>();
469+
470+
parser.Configure(a => a.Message)
471+
.Name("m", "message")
472+
.Required()
473+
.Transform(msg => $"{msg}transformed")
474+
.Default("default");
475+
476+
var result = parser.Parse(args);
477+
478+
Assert.Equal(errors, result.AssertNoErrors(false));
479+
480+
Assert.Equal(expected, result.Result.Message);
481+
}
482+
483+
[Theory]
484+
[InlineData(new string[] { "" }, 11, false)]
485+
[InlineData(new string[] { "-i", "10" }, 20, false)]
486+
[InlineData(new string[] { "--int", "10" }, 20, false)]
487+
public void TransformationWorksAsExpectedForInts(string[] args, int expected, bool errors)
488+
{
489+
var parser = new CommandLineParser<IntOptions>();
490+
491+
parser.Configure(a => a.SomeInt)
492+
.Name("i", "int")
493+
.Required()
494+
.Transform(value => value + 10)
495+
.Default(1);
496+
497+
var result = parser.Parse(args);
498+
499+
Assert.Equal(errors, result.AssertNoErrors(false));
500+
501+
Assert.Equal(expected, result.Result.SomeInt);
502+
}
503+
504+
[Theory]
505+
[InlineData(new string[] { "cmd" }, 11, false)]
506+
[InlineData(new string[] { "cmd", "-i", "10" }, 20, false)]
507+
[InlineData(new string[] { "cmd", "--int", "10" }, 20, false)]
508+
public void TransformationWorksAsExpectedForCommandOptions(string[] args, int expected, bool errors)
509+
{
510+
int outcome = -1;
511+
512+
var parser = new CommandLineParser();
513+
514+
var cmd = parser.AddCommand<IntOptions>()
515+
.Name("cmd")
516+
.Required()
517+
.OnExecuting((_, i) => outcome = i.SomeInt);
518+
519+
cmd.Configure(a => a.SomeInt)
520+
.Name("i", "int")
521+
.Required()
522+
.Transform(value => value + 10)
523+
.Default(1);
524+
525+
var result = parser.Parse(args);
526+
527+
Assert.Equal(errors, result.AssertNoErrors(false));
528+
529+
Assert.Equal(expected, outcome);
530+
}
531+
462532
private class ObjOption
463533
{
464534
[Name("p"), Required]
@@ -493,5 +563,10 @@ private class OptionsWithThreeParams<T>
493563
public T Option2 { get; set; }
494564
public T Option3 { get; set; }
495565
}
566+
567+
private class IntOptions
568+
{
569+
public int SomeInt { get; set; }
570+
}
496571
}
497572
}

CommandLineParser.Tests/XUnitExtensions.cs

+13-3
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,24 @@ public static LambdaExpression CreateLambda<TSource, TProperty>(Expression<Func<
1111
return expression;
1212
}
1313

14-
public static void AssertNoErrors<T>(this IParserResult<T> result)
14+
public static bool AssertNoErrors<T>(this IParserResult<T> result, bool shouldThrow = true)
1515
{
1616
if (result == null)
1717
throw new NullReferenceException("Parsing result was null");
1818

1919
foreach (var err in result.Errors)
20-
throw err;
21-
}
20+
{
21+
if (shouldThrow)
22+
{
23+
throw err;
24+
}
25+
else
26+
{
27+
return true;
28+
}
29+
}
2230

31+
return false;
32+
}
2333
}
2434
}

CommandLineParser.sln

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 15
4-
VisualStudioVersion = 15.0.28010.2016
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.29111.141
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommandLineParser", "CommandLineParser\CommandLineParser.csproj", "{02D41856-3E21-427E-94C3-2AF34F1C2264}"
77
EndProject
88
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommandLineParser.Tests", "CommandLineParser.Tests\CommandLineParser.Tests.csproj", "{72442EFD-1112-4081-A910-8A792F870AD1}"
99
EndProject
10-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApp", "SampleApp\SampleApp.csproj", "{6871A016-97E8-48C5-B797-DD0FA3DD6288}"
10+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApp", "SampleApp\SampleApp.csproj", "{6871A016-97E8-48C5-B797-DD0FA3DD6288}"
11+
EndProject
12+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DA88EFDF-AD86-4772-B310-35A606D6D643}"
13+
ProjectSection(SolutionItems) = preProject
14+
.gitignore = .gitignore
15+
appveyor.yml = appveyor.yml
16+
build.cake = build.cake
17+
LICENSE = LICENSE
18+
README.md = README.md
19+
EndProjectSection
1120
EndProject
1221
Global
1322
GlobalSection(SolutionConfigurationPlatforms) = preSolution

CommandLineParser/Abstractions/Command/ICommandBuilder'.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public interface ICommandBuilder<TOption, TSource> : ICommandConfigurationBuilde
1818
/// <typeparam name="TProperty">Type of the property</typeparam>
1919
/// <param name="selector">Model property to configure</param>
2020
/// <returns><see cref="IOptionBuilder"/></returns>
21-
IOptionBuilder Configure<TProperty>(Expression<Func<TSource, TProperty>> selector);
21+
IOptionBuilder<TProperty> Configure<TProperty>(Expression<Func<TSource, TProperty>> selector);
2222

2323
/// <summary>
2424
/// Configures if the command is required

CommandLineParser/Abstractions/Command/ICommandConfigurationBuilder`.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public interface ICommandConfigurationBuilder<TSource>
1717
/// <typeparam name="TProperty">Type of the property</typeparam>
1818
/// <param name="selector">Model property to configure</param>
1919
/// <returns><see cref="IOptionBuilder"/></returns>
20-
IOptionBuilder Configure<TProperty>(Expression<Func<TSource, TProperty>> selector);
20+
IOptionBuilder<TProperty> Configure<TProperty>(Expression<Func<TSource, TProperty>> selector);
2121

2222
/// <summary>
2323
/// Configures if the command is required

CommandLineParser/Abstractions/ICommandLineParser'.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
namespace MatthiWare.CommandLine.Abstractions
99
{
10+
/// <summary>
11+
/// Command line parser
12+
/// </summary>
13+
/// <typeparam name="TOption">Argument options model</typeparam>
1014
public interface ICommandLineParser<TOption>
1115
where TOption : class, new()
1216
{
@@ -49,7 +53,7 @@ public interface ICommandLineParser<TOption>
4953
/// <typeparam name="TProperty">The property type</typeparam>
5054
/// <param name="selector">Property selector</param>
5155
/// <returns><see cref="IOptionBuilder"/></returns>
52-
IOptionBuilder Configure<TProperty>(Expression<Func<TOption, TProperty>> selector);
56+
IOptionBuilder<TProperty> Configure<TProperty>(Expression<Func<TOption, TProperty>> selector);
5357

5458
/// <summary>
5559
/// Adds a new command and allowes to configure it.

CommandLineParser/Abstractions/IOptionBuilder'.cs renamed to CommandLineParser/Abstractions/IOptionBuilder.cs

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
namespace MatthiWare.CommandLine.Abstractions
1+
using System;
2+
using System.Linq.Expressions;
3+
4+
namespace MatthiWare.CommandLine.Abstractions
25
{
36
/// <summary>
47
/// API for configuring options
@@ -15,30 +18,30 @@ public interface IOptionBuilder
1518
/// <summary>
1619
/// Help text to be displayed for this option
1720
/// </summary>
18-
/// <param name="help"></param>
19-
/// <returns></returns>
21+
/// <param name="description">The description of the option</param>
22+
/// <returns><see cref="IOptionBuilder"></see></returns>
2023
IOptionBuilder Description(string description);
2124

2225
/// <summary>
2326
/// Specify the default value for this option
2427
/// </summary>
2528
/// <param name="defaultValue"></param>
26-
/// <returns></returns>
29+
/// <returns><see cref="IOptionBuilder"></see></returns>
2730
IOptionBuilder Default(object defaultValue);
2831

2932
/// <summary>
3033
/// Configures the name for the option
3134
/// </summary>
3235
/// <param name="shortName">short name</param>
33-
/// <returns></returns>
36+
/// <returns><see cref="IOptionBuilder"></see></returns>
3437
IOptionBuilder Name(string shortName);
3538

3639
/// <summary>
3740
/// Configures the name for the option
3841
/// </summary>
3942
/// <param name="shortName">Short name</param>
4043
/// <param name="longName">Long name</param>
41-
/// <returns></returns>
44+
/// <returns><see cref="IOptionBuilder"></see></returns>
4245
IOptionBuilder Name(string shortName, string longName);
4346
}
4447
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System;
2+
using System.Linq.Expressions;
3+
4+
namespace MatthiWare.CommandLine.Abstractions
5+
{
6+
/// <summary>
7+
/// API for configuring options
8+
/// </summary>
9+
public interface IOptionBuilder<TOption> : IOptionBuilder
10+
{
11+
/// <summary>
12+
/// Sets if the option is required
13+
/// </summary>
14+
/// <param name="required">Required or not</param>
15+
/// <returns><see cref="IOptionBuilder"/></returns>
16+
new IOptionBuilder<TOption> Required(bool required = true);
17+
18+
/// <summary>
19+
/// Help text to be displayed for this option
20+
/// </summary>
21+
/// <param name="description">The description of the option</param>
22+
/// <returns><see cref="IOptionBuilder{TOption}"></see></returns>
23+
new IOptionBuilder<TOption> Description(string description);
24+
25+
/// <summary>
26+
/// Specify the default value for this option
27+
/// </summary>
28+
/// <param name="defaultValue"></param>
29+
/// <returns><see cref="IOptionBuilder{TOption}"></see></returns>
30+
IOptionBuilder<TOption> Default(TOption defaultValue);
31+
32+
/// <summary>
33+
/// Configures the name for the option
34+
/// </summary>
35+
/// <param name="shortName">short name</param>
36+
/// <returns><see cref="IOptionBuilder{TOption}"></see></returns>
37+
new IOptionBuilder<TOption> Name(string shortName);
38+
39+
/// <summary>
40+
/// Configures the name for the option
41+
/// </summary>
42+
/// <param name="shortName">Short name</param>
43+
/// <param name="longName">Long name</param>
44+
/// <returns><see cref="IOptionBuilder{TOption}"></see></returns>
45+
new IOptionBuilder<TOption> Name(string shortName, string longName);
46+
47+
/// <summary>
48+
/// Transforms the parsed value using the transform function
49+
/// </summary>
50+
/// <param name="transformation">Transformation function</param>
51+
/// <returns><see cref="IOptionBuilder{TOption}"></see></returns>
52+
IOptionBuilder<TOption> Transform(Expression<Func<TOption, TOption>> transformation);
53+
}
54+
}

CommandLineParser/Abstractions/IOptionConfigurator.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ public interface IOptionConfigurator<TSource>
1010
/// </summary>
1111
/// <param name="selector">Property to configure</param>
1212
/// <returns><see cref="IOptionBuilder"/></returns>
13-
IOptionBuilder Configure<TProperty>(Expression<Func<TSource, TProperty>> selector);
13+
IOptionBuilder<TProperty> Configure<TProperty>(Expression<Func<TSource, TProperty>> selector);
1414
}
1515
}

CommandLineParser/CommandLineParser.csproj

+9-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<TargetFramework>netstandard2.0</TargetFramework>
55
<RootNamespace>MatthiWare.CommandLine</RootNamespace>
66
<PackageId>MatthiWare.CommandLineParser</PackageId>
7-
<Version>0.2.2</Version>
7+
<Version>0.2.3</Version>
88
<Authors>Matthias Beerens</Authors>
99
<Company>MatthiWare</Company>
1010
<Product>Command Line Parser</Product>
@@ -14,18 +14,22 @@
1414
<RepositoryUrl>https://github.com/MatthiWare/CommandLineParser.Core</RepositoryUrl>
1515
<PackageTags>Commandline parser</PackageTags>
1616
<LangVersion>7.3</LangVersion>
17-
<AssemblyVersion>0.2.1.0</AssemblyVersion>
18-
<FileVersion>0.1.1.0</FileVersion>
17+
<AssemblyVersion>0.2.3.0</AssemblyVersion>
18+
<FileVersion>0.2.3.0</FileVersion>
1919
</PropertyGroup>
2020

2121
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
2222
<DebugType>full</DebugType>
2323
<DebugSymbols>true</DebugSymbols>
24-
<DocumentationFile>D:\Source\Repos\CommandLineParser\CommandLineParser\CommandLineParser.xml</DocumentationFile>
24+
<DocumentationFile>.\CommandLineParser.xml</DocumentationFile>
25+
</PropertyGroup>
26+
27+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
28+
<DocumentationFile>.\CommandLineParser.xml</DocumentationFile>
2529
</PropertyGroup>
2630

2731
<ItemGroup>
28-
<PackageReference Include="System.Memory" Version="4.5.1" />
32+
<PackageReference Include="System.Memory" Version="4.5.3" />
2933
</ItemGroup>
3034

3135
</Project>

CommandLineParser/CommandLineParser.nuspec

+9-6
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,25 @@
22
<package>
33
<metadata>
44
<id>MatthiWare.CommandLineParser</id>
5-
<version>0.2.2</version>
5+
<version>0.2.3</version>
66
<title>CommandLineParser.Core</title>
77
<authors>Matthias Beerens</authors>
88
<owners>Matthiee</owners>
99
<licenseUrl>https://github.com/MatthiWare/CommandLineParser.Core/blob/master/LICENSE</licenseUrl>
1010
<projectUrl>https://github.com/MatthiWare/CommandLineParser.Core</projectUrl>
1111
<requireLicenseAcceptance>false</requireLicenseAcceptance>
1212
<description>
13-
Command Line Parser for .NET Core written in .NET Standard.
13+
Command Line Parser for .NET Core written in .NET Standard 2.0.
1414

15-
Configuration is done through a option model class using attributes or fluent API can be used to configure the properties of the class.
15+
Configuration is done through a strongly typed option model class using attributes or a fluent API. Both can be used to configure the properties of the options class.
1616
This library allows to add commands with their own set of options as well.
1717
</description>
1818
<summary>A simple, light-weight and strongly typed command line parser. Configuration using Fluent API, Attributes and model classes.</summary>
19-
<releaseNotes>Fix issue with bool parsing, fix indenting, fix auto printing of errors</releaseNotes>
20-
<copyright>Copyright Matthias Beerens 2018</copyright>
21-
<tags>commandline parser commandline-parser</tags>
19+
<releaseNotes>Fix bug with alignment, add postfix and value transformers</releaseNotes>
20+
<copyright>Copyright Matthias Beerens 2018</copyright>
21+
<tags>commandline parser commandline-parser</tags>
2222
</metadata>
23+
<files>
24+
<file src=".\CommandLineParser.xml" target="lib\netstandard2.0" />
25+
</files>
2326
</package>

0 commit comments

Comments
 (0)