//
// ServiceCollectionExtensions.cs
//
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Net;
using Microsoft.Extensions.DependencyInjection;
using NosSmooth.Comms.Core.NamedPipes;
using NosSmooth.Comms.Core.Responders;
using NosSmooth.Comms.Core.Tcp;
using NosSmooth.Comms.Data;
using NosSmooth.Comms.Data.Responders;
using NosSmooth.Core.Client;
namespace NosSmooth.Comms.Core.Extensions;
///
/// Extension methods for .
///
public static class ServiceCollectionExtensions
{
///
/// Adds server handling ( and ).
///
///
/// The specific server has to be added separately as .
///
/// The service collection.
/// The same service collection.
public static IServiceCollection AddServerHandling(this IServiceCollection serviceCollection)
=> serviceCollection
.AddNosSmoothResolverOptions()
.AddSingleton(p => new MessageHandler(p, true))
.AddSingleton()
.AddInjecting();
///
/// Adds handling for a single client.
///
///
/// The specific client has to be added separately as .
///
/// The service collection.
/// The same service collection.
public static IServiceCollection AddSingleClientHandling(this IServiceCollection serviceCollection)
=> serviceCollection
.AddInjecting()
.AddNosSmoothResolverOptions()
.AddMessageResponder()
.AddSingleton()
.AddSingleton(p => p.GetRequiredService())
.AddSingleton(p => new MessageHandler(p, false))
.AddScoped()
.AddScoped();
///
/// Add handling for multiple clients.
///
///
/// The clients should not be inside of the provider.
/// Initialize clients outside of the provider and use the
/// provider for injecting connection handler and nostale client.
/// Nostale client will be created automatically if connection is injected successfully.
/// Connection will be injected when calling message handler with the specific connection.
///
/// Connection may be injected by setting properties in a scope.
///
/// The service collection.
/// The same service collection.
public static IServiceCollection AddMultiClientHandling(this IServiceCollection serviceCollection)
=> serviceCollection
.AddNosSmoothResolverOptions()
.AddSingleton()
.AddSingleton(p => new MessageHandler(p, false))
.AddInjecting()
.AddScoped
(p => p.GetRequiredService().Resolve(p.GetRequiredService()));
///
/// Add with default NosSmooth options.
///
/// The service collection.
/// The same service collection.
public static IServiceCollection AddNosSmoothResolverOptions(this IServiceCollection serviceCollection)
=> serviceCollection
.Configure
(o => o.Options = o.Options.WithResolver(NosSmoothResolver.Instance));
///
/// Adds a message responder.
///
/// The service collection.
/// The type of the responder.
/// The same service collection.
public static IServiceCollection AddMessageResponder(this IServiceCollection serviceCollection)
{
return serviceCollection.AddMessageResponder(typeof(TResponder));
}
///
/// Adds a message responder.
///
/// The service collection.
/// The type of the responder.
/// The same service collection.
public static IServiceCollection AddMessageResponder(this IServiceCollection serviceCollection, Type responderType)
{
if (serviceCollection.Any(x => x.ImplementationType == responderType))
{ // already added... assuming every packet responder was added even though that may not be the case.
return serviceCollection;
}
if (!responderType.GetInterfaces().Any
(
i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMessageResponder<>)
))
{
throw new ArgumentException
(
$"{nameof(responderType)} should implement IMessageResponder.",
nameof(responderType)
);
}
var responderTypeInterfaces = responderType.GetInterfaces();
var responderInterfaces = responderTypeInterfaces.Where
(
r => r.IsGenericType && r.GetGenericTypeDefinition() == typeof(IMessageResponder<>)
);
foreach (var responderInterface in responderInterfaces)
{
serviceCollection.AddScoped(responderInterface, responderType);
}
return serviceCollection;
}
///
/// Adds a named pipe client as a .
///
/// The service collection.
/// A function for resolving the name of the pipe using service provider.
/// The same service collection.
public static IServiceCollection AddNamedPipeClient
(this IServiceCollection serviceCollection, Func pipeNameResolver)
=> serviceCollection
.AddSingleton(p => new NamedPipeClient(pipeNameResolver(p)))
.AddSingleton(p => p.GetRequiredService());
///
/// Adds a named pipe server as a .
///
/// The service collection.
/// A function for resolving the name of the pipe using service provider.
/// The same service collection.
public static IServiceCollection AddNamedPipeServer
(this IServiceCollection serviceCollection, Func pipeNameResolver)
=> serviceCollection
.AddSingleton(p => new NamedPipeServer(pipeNameResolver(p)))
.AddSingleton(p => p.GetRequiredService());
///
/// Adds tcp server as .
///
/// The service collection.
/// A resovler used for resolving the ip and port to bind to.
/// The same service collection.
public static IServiceCollection AddTcpServer
(
this IServiceCollection serviceCollection,
Func bindResolver
)
{
return serviceCollection
.AddSingleton(p => p.GetRequiredService())
.AddSingleton
(
p =>
{
var resolved = bindResolver(p);
return new TcpServer(resolved.BindAddress, resolved.Port);
}
);
}
///
/// Adds tcp client as .
///
/// The service collection.
/// A resovler used for resolving the hostname and port to connect to.
/// The same service collection.
public static IServiceCollection AddTcpClient
(
this IServiceCollection serviceCollection,
Func connectionResolver
)
{
return serviceCollection
.AddSingleton(p => p.GetRequiredService())
.AddSingleton
(
p =>
{
var resolved = connectionResolver(p);
return new TcpClient(resolved.Hostname, resolved.Port);
}
);
}
private static IServiceCollection AddInjecting(this IServiceCollection serviceCollection)
=> serviceCollection
.AddScoped()
.AddScoped
(
p =>
{
var handler = p.GetRequiredService().ConnectionHandler;
if (handler is null)
{
throw new InvalidOperationException("Connection handler was requested, but is not injected.");
}
return handler;
}
)
.AddScoped
(
p =>
{
var connection = p.GetRequiredService().Connection;
if (connection is null)
{
throw new InvalidOperationException("Connection was requested, but is not injected.");
}
return connection;
}
);
}