Skip to content

Commit 7599642

Browse files
committed
Added capability to initialize from a target type.
1 parent f97fcf0 commit 7599642

37 files changed

+1190
-155
lines changed

AutofacCommandLine/AutofacCommandLine.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
</PropertyGroup>
2222

2323
<ItemGroup>
24-
<PackageReference Include="Autofac" Version="5.2.0" />
25-
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="6.0.0" />
24+
<PackageReference Include="Autofac" Version="6.1.0" />
25+
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.1.0" />
2626
</ItemGroup>
2727

2828
<ItemGroup>

Binder.Tests/BaseTest.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Binder.Tests;
7+
using J4JSoftware.Logging;
8+
using Microsoft.Extensions.Hosting;
9+
10+
namespace J4JSoftware.Binder.Tests
11+
{
12+
public class BaseTest
13+
{
14+
protected BaseTest()
15+
{
16+
Logger = CompositionRoot.Default.Logger;
17+
}
18+
19+
protected IJ4JLogger Logger { get; }
20+
}
21+
}

Binder.Tests/Binder.Tests.csproj

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net5.0</TargetFramework>
5+
6+
<IsPackable>false</IsPackable>
7+
8+
<AssemblyName>J4JSoftware.J4JCommandLine.Binder.Tests</AssemblyName>
9+
10+
<RootNamespace>J4JSoftware.Binder.Tests</RootNamespace>
11+
12+
<Nullable>enable</Nullable>
13+
</PropertyGroup>
14+
15+
<ItemGroup>
16+
<PackageReference Include="Autofac" Version="6.1.0" />
17+
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.1.0" />
18+
<PackageReference Include="FluentAssertions" Version="5.10.3" />
19+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
20+
<PackageReference Include="xunit" Version="2.4.1" />
21+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
22+
<PrivateAssets>all</PrivateAssets>
23+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
24+
</PackageReference>
25+
<PackageReference Include="coverlet.collector" Version="1.3.0">
26+
<PrivateAssets>all</PrivateAssets>
27+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
28+
</PackageReference>
29+
</ItemGroup>
30+
31+
<ItemGroup>
32+
<ProjectReference Include="..\..\J4JLogging\AutoFacJ4JLogging\AutoFacJ4JLogging.csproj" />
33+
<ProjectReference Include="..\..\J4JLogging\J4JLogging\J4JLogging.csproj" />
34+
<ProjectReference Include="..\..\ProgrammingUtilities\DependencyInjection\DependencyInjection.csproj" />
35+
<ProjectReference Include="..\Binder\Binder.csproj" />
36+
</ItemGroup>
37+
38+
<ItemGroup>
39+
<None Update="appConfig.json">
40+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
41+
</None>
42+
</ItemGroup>
43+
44+
</Project>

Binder.Tests/CompositionRoot.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Autofac;
7+
using Autofac.Extensions.DependencyInjection;
8+
using J4JSoftware.Binder.Tests;
9+
using J4JSoftware.CommandLine;
10+
using J4JSoftware.DependencyInjection;
11+
using J4JSoftware.Logging;
12+
using Microsoft.Extensions.Configuration;
13+
using Microsoft.Extensions.DependencyInjection;
14+
using Microsoft.Extensions.Hosting;
15+
using Twilio.Rest.Api.V2010.Account.Usage.Record;
16+
using Xunit.Sdk;
17+
18+
namespace Binder.Tests
19+
{
20+
public class CompositionRoot : J4JCompositionRoot
21+
{
22+
private static readonly CompositionRoot _root = new CompositionRoot();
23+
24+
public static CompositionRoot Default => _root;
25+
26+
protected override void ConfigureHostBuilder( IHostBuilder hostBuilder )
27+
{
28+
hostBuilder.ConfigureAppConfiguration( ( hc, b ) =>
29+
b.SetBasePath( Environment.CurrentDirectory )
30+
.AddJsonFile( "appConfig.json" )
31+
);
32+
33+
hostBuilder.ConfigureContainer<ContainerBuilder>( ConfigureDependencyInjection );
34+
}
35+
36+
private void ConfigureDependencyInjection( HostBuilderContext context, ContainerBuilder builder )
37+
{
38+
builder.RegisterJ4JLogging();
39+
40+
builder.Register( c =>
41+
{
42+
var retVal = context.Configuration
43+
.GetSection("Logger" )
44+
.Get<J4JLoggerConfiguration<LogChannelsConfig>>();
45+
46+
return retVal;
47+
} )
48+
.As<IJ4JLoggerConfiguration>();
49+
50+
builder.RegisterType<Options>()
51+
.AsSelf();
52+
53+
builder.RegisterType<MasterTextCollection>()
54+
.OnActivated( x =>
55+
{
56+
x.Instance.Initialize( StringComparison.OrdinalIgnoreCase );
57+
x.Instance.AddRange( TextUsageType.Prefix, "-", "--" );
58+
x.Instance.AddRange( TextUsageType.Quote, "\"", "'" );
59+
x.Instance.AddRange( TextUsageType.ValueEncloser, "=" );
60+
} )
61+
.AsSelf()
62+
.SingleInstance();
63+
64+
builder.RegisterType<DefaultTypeInitializer>()
65+
.AsImplementedInterfaces();
66+
67+
builder.RegisterType<Allocator>()
68+
.AsImplementedInterfaces();
69+
70+
builder.RegisterType<ElementTerminator>()
71+
.AsImplementedInterfaces();
72+
73+
builder.RegisterType<KeyPrefixer>()
74+
.AsImplementedInterfaces();
75+
}
76+
77+
public IJ4JLogger Logger => Host!.Services.GetRequiredService<IJ4JLogger>();
78+
public Options Options => Host!.Services.GetRequiredService<Options>();
79+
public IAllocator Allocator => Host!.Services.GetRequiredService<IAllocator>();
80+
}
81+
}

Binder.Tests/LogChannelsConfig.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System.Collections.Generic;
2+
using J4JSoftware.Logging;
3+
4+
#pragma warning disable 8618
5+
6+
namespace J4JSoftware.Binder.Tests
7+
{
8+
public class LogChannelsConfig : LogChannels
9+
{
10+
public DebugConfig Debug { get; set; }
11+
12+
public override IEnumerator<IChannelConfig> GetEnumerator()
13+
{
14+
yield return Debug;
15+
}
16+
}
17+
}

Binder.Tests/TestOne.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Binder.Tests;
7+
using J4JSoftware.CommandLine;
8+
using Xunit;
9+
10+
namespace J4JSoftware.Binder.Tests
11+
{
12+
public class TestOne : BaseTest
13+
{
14+
[Fact]
15+
public void ATest()
16+
{
17+
var options = CompositionRoot.Default.Options;
18+
var allocator = CompositionRoot.Default.Allocator;
19+
}
20+
}
21+
}

Binder.Tests/TestProperties.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Collections.Generic;
2+
3+
#pragma warning disable 8618
4+
5+
namespace J4JSoftware.Binder.Tests
6+
{
7+
public class TestProperties
8+
{
9+
public int IntProperty { get; set; }
10+
public List<int> IntList { get; set; }
11+
public int[] IntArray { get; set; }
12+
public List<int> Unkeyed { get; set; }
13+
public bool Switch { get; set; }
14+
}
15+
}

Binder.Tests/appConfig.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"Logger": {
3+
"SourceRootPath": "C:/Programming/J4JCommandLine/Binder.Tests",
4+
"EventElements": "All",
5+
"Channels": {
6+
"Debug": {
7+
"MinimumLevel": "Verbose"
8+
}
9+
}
10+
}
11+
}

Binder/Binder.csproj

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net5.0</TargetFramework>
5+
<AssemblyName>J4JSoftware.CommandLine.Binder</AssemblyName>
6+
<RootNamespace>J4JSoftware.CommandLine</RootNamespace>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<Compile Remove="options\OptionConfiguration.cs" />
12+
<Compile Remove="options\OptionRequiredAttribute.cs" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\..\J4JLogging\J4JLogging\J4JLogging.csproj" />
17+
</ItemGroup>
18+
19+
</Project>

Binder/allocating/Allocator.cs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Runtime.CompilerServices;
5+
using System.Runtime.InteropServices.ComTypes;
6+
using System.Text;
7+
using J4JSoftware.Logging;
8+
9+
#pragma warning disable 8618
10+
11+
namespace J4JSoftware.CommandLine
12+
{
13+
public class Allocator : IAllocator
14+
{
15+
private readonly IElementTerminator _terminator;
16+
private readonly IKeyPrefixer _prefixer;
17+
private readonly IJ4JLogger _logger;
18+
19+
public Allocator(
20+
IElementTerminator terminator,
21+
IKeyPrefixer prefixer,
22+
IJ4JLogger logger
23+
)
24+
{
25+
_terminator = terminator;
26+
_prefixer = prefixer;
27+
28+
_logger = logger;
29+
_logger.SetLoggedType( GetType() );
30+
}
31+
32+
public bool AllocateCommandLine( string[] args, Options options, out List<string>? unkeyed )
33+
{
34+
unkeyed = null;
35+
36+
if( !AllocateCommandLine( string.Join( " ", args ), options, out var temp ) )
37+
return false;
38+
39+
unkeyed = temp;
40+
41+
return true;
42+
}
43+
44+
public bool AllocateCommandLine( string cmdLine, Options options, out List<string>? unkeyed )
45+
{
46+
unkeyed = null;
47+
var unkeyedInternal = new List<string>();
48+
49+
var accumulator = new StringBuilder();
50+
Option? curOption = null;
51+
var charsProcessed = 0;
52+
var lastElementWasKey = false;
53+
54+
for( var idx = 0; idx < cmdLine.Length; idx++ )
55+
{
56+
accumulator.Append( cmdLine[ idx ] );
57+
charsProcessed++;
58+
59+
var element = accumulator.ToString();
60+
61+
// analyze the sequence as it currently stands to see if it includes
62+
// a prefixed key and/or has been terminated
63+
var maxPrefix = _prefixer.GetMaxPrefixLength( element );
64+
var maxTerminator = _terminator.GetMaxTerminatorLength( element, maxPrefix > 0 );
65+
66+
// keep adding characters unless we've encountered a termination
67+
// sequence or we've reached the end of the command line
68+
if( maxTerminator <= 0 && charsProcessed < cmdLine.Length )
69+
continue;
70+
71+
// extract the true element value from the prefix and terminator
72+
element = element[ maxPrefix..^maxTerminator ];
73+
74+
// key values are identified by the presence of known prefixes
75+
if( maxPrefix > 0 )
76+
{
77+
// element is a key
78+
79+
// if the key (contained in element) isn't among the keys defined
80+
// in the options collection we have a problem
81+
if( !options.UsesCommandLineKey( element ) )
82+
{
83+
_logger.Error<string>( "Unknown key '{0}'", element );
84+
return false;
85+
}
86+
87+
curOption = options[ element ];
88+
89+
curOption!.CommandLineKeyUsed = element;
90+
91+
lastElementWasKey = true;
92+
}
93+
else
94+
{
95+
// element is parameter value
96+
if( curOption == null || !lastElementWasKey )
97+
unkeyedInternal.Add( element );
98+
else curOption.AddAllocatedValue( element );
99+
100+
lastElementWasKey = false;
101+
}
102+
103+
// clear the accumulator so we can start processing the next character sequence
104+
accumulator.Clear();
105+
}
106+
107+
unkeyed = unkeyedInternal;
108+
109+
return true;
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)