<commit_before>﻿using System;
using System.Net.Http;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using RestEase.Implementation;

namespace RestEase.HttpClientFactory
{
    /// <summary>
    /// RestEase extension methods on <see cref="IServiceCollection"/>
    /// </summary>
    public static class HttpClientFactoryExtensions
    {
        /// <summary>
        /// Register the given RestEase interface type
        /// </summary>
        /// <typeparam name="T">Type of the RestEase interface</typeparam>
        /// <param name="services">Contains to add to</param>
        /// <param name="basePath">Base path to use for requests (may be <c>null</c> if your interface only uses absolute paths, or has an absolute <c>[BasePath]</c>)</param>
        /// <param name="configurer">Optional delegate to configure the <see cref="RestClient"/> (to set serializers, etc)</param>
        /// <param name="requestModifier">Optional delegate to use to modify all requests</param>
        /// <returns>Created <see cref="IHttpClientBuilder"/> for further configuration</returns>
        public static IHttpClientBuilder AddRestEaseClient<T>(
            this IServiceCollection services,
            string? basePath,
            Action<RestClient>? configurer = null,
            RequestModifier? requestModifier = null)
            where T : class
        {
            return services.AddRestEaseClientCore(typeof(T), basePath, requestModifier, httpClient =>
            {
                var restClient = new RestClient(httpClient);
                configurer?.Invoke(restClient);
                return restClient.For<T>();
            });
        }

        /// <summary>
        /// Register the given RestEase interface type
        /// </summary>
        /// <param name="restEaseType">Type of the RestEase interface</param>
        /// <param name="services">Contains to add to</param>
        /// <param name="basePath">Base path to use for requests (may be <c>null</c> if your interface only uses absolute paths, or has an absolute <c>[BasePath]</c>)</param>
        /// <param name="configurer">Optional delegate to configure the <see cref="RestClient"/> (to set serializers, etc)</param>
        /// <param name="requestModifier">Optional delegate to use to modify all requests</param>
        /// <returns>Created <see cref="IHttpClientBuilder"/> for further configuration</returns>
        public static IHttpClientBuilder AddRestEaseClient(
            this IServiceCollection services,
            Type restEaseType,
            string? basePath,
            Action<RestClient>? configurer = null,
            RequestModifier? requestModifier = null)
        {
            if (restEaseType is null)
                throw new ArgumentNullException(nameof(restEaseType));

            return services.AddRestEaseClientCore(restEaseType, basePath, requestModifier, httpClient =>
            {
                var restClient = new RestClient(httpClient);
                configurer?.Invoke(restClient);
                return restClient.For(restEaseType);
            });
        }

        private static IHttpClientBuilder AddRestEaseClientCore(
            this IServiceCollection services,
            Type restEaseType,
            string? basePath,
            RequestModifier? requestModifier,
            Func<HttpClient, object> factory)
        {
            if (services is null)
                throw new ArgumentNullException(nameof(services));

            var builder = services.AddHttpClient(UniqueNameForType(restEaseType), httpClient =>
            {
                if (basePath != null)
                {
                    httpClient.BaseAddress = new Uri(basePath);
                }
            }).AddTypedClient(factory);

            if (requestModifier != null)
            {
                builder = builder.ConfigurePrimaryHttpMessageHandler(() => new ModifyingClientHttpHandler(requestModifier));
            }

            return builder;
        }

        private static string UniqueNameForType(Type type)
        {
            var sb = new StringBuilder();
            Impl(type);
            return sb.ToString();

            void Impl(Type typeInfo)
            {
                if (typeInfo.IsGenericType)
                {
                    string fullName = type.GetGenericTypeDefinition().FullName;
                    sb.Append(fullName.Substring(0, fullName.LastIndexOf('`')));
                    sb.Append("<");<commit_msg>Add HttpClientFactory overloads which take a Uri<commit_after>﻿using System;
using System.Net.Http;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using RestEase.Implementation;

namespace RestEase.HttpClientFactory
{
    /// <summary>
    /// RestEase extension methods on <see cref="IServiceCollection"/>
    /// </summary>
    public static class HttpClientFactoryExtensions
    {
        /// <summary>
        /// Register the given RestEase interface type
        /// </summary>
        /// <typeparam name="T">Type of the RestEase interface</typeparam>
        /// <param name="services">Contains to add to</param>
        /// <param name="basePath">Base path to use for requests (may be <c>null</c> if your interface only uses absolute paths, or has an absolute <c>[BasePath]</c>)</param>
        /// <param name="configurer">Optional delegate to configure the <see cref="RestClient"/> (to set serializers, etc)</param>
        /// <param name="requestModifier">Optional delegate to use to modify all requests</param>
        /// <returns>Created <see cref="IHttpClientBuilder"/> for further configuration</returns>
        public static IHttpClientBuilder AddRestEaseClient<T>(
            this IServiceCollection services,
            string? basePath,
            Action<RestClient>? configurer = null,
            RequestModifier? requestModifier = null)
            where T : class
        {
            return services.AddRestEaseClientCore(typeof(T), ToUri(basePath), requestModifier, httpClient =>
            {
                var restClient = new RestClient(httpClient);
                configurer?.Invoke(restClient);
                return restClient.For<T>();
            });
        }

        /// <summary>
        /// Register the given RestEase interface type
        /// </summary>
        /// <typeparam name="T">Type of the RestEase interface</typeparam>
        /// <param name="services">Contains to add to</param>
        /// <param name="baseUri">Base URI to use for requests (may be <c>null</c> if your interface only uses absolute paths, or has an absolute <c>[BasePath]</c>)</param>
        /// <param name="configurer">Optional delegate to configure the <see cref="RestClient"/> (to set serializers, etc)</param>
        /// <param name="requestModifier">Optional delegate to use to modify all requests</param>
        /// <returns>Created <see cref="IHttpClientBuilder"/> for further configuration</returns>
        public static IHttpClientBuilder AddRestEaseClient<T>(
            this IServiceCollection services,
            Uri? baseUri,
            Action<RestClient>? configurer = null,
            RequestModifier? requestModifier = null)
            where T : class
        {
            return services.AddRestEaseClientCore(typeof(T), baseUri, requestModifier, httpClient =>
            {
                var restClient = new RestClient(httpClient);
                configurer?.Invoke(restClient);
                return restClient.For<T>();
            });
        }

        /// <summary>
        /// Register the given RestEase interface type
        /// </summary>
        /// <param name="restEaseType">Type of the RestEase interface</param>
        /// <param name="services">Contains to add to</param>
        /// <param name="basePath">Base path to use for requests (may be <c>null</c> if your interface only uses absolute paths, or has an absolute <c>[BasePath]</c>)</param>
        /// <param name="configurer">Optional delegate to configure the <see cref="RestClient"/> (to set serializers, etc)</param>
        /// <param name="requestModifier">Optional delegate to use to modify all requests</param>
        /// <returns>Created <see cref="IHttpClientBuilder"/> for further configuration</returns>
        public static IHttpClientBuilder AddRestEaseClient(
            this IServiceCollection services,
            Type restEaseType,
            string? basePath,
            Action<RestClient>? configurer = null,
            RequestModifier? requestModifier = null)
        {
            if (restEaseType is null)
                throw new ArgumentNullException(nameof(restEaseType));

            return services.AddRestEaseClientCore(restEaseType, ToUri(basePath), requestModifier, httpClient =>
            {
                var restClient = new RestClient(httpClient);
                configurer?.Invoke(restClient);
                return restClient.For(restEaseType);
            });
        }

        /// <summary>
        /// Register the given RestEase interface type
        /// </summary>
        /// <param name="restEaseType">Type of the RestEase interface</param>
        /// <param name="services">Contains to add to</param>
        /// <param name="baseUri">Base URI to use for requests (may be <c>null</c> if your interface only uses absolute paths, or has an absolute <c>[BasePath]</c>)</param>
        /// <param name="configurer">Optional delegate to configure the <see cref="RestClient"/> (to set serializers, etc)</param>
        /// <param name="requestModifier">Optional delegate to use to modify all requests</param>
        /// <returns>Created <see cref="IHttpClientBuilder"/> for further configuration</returns>
        public static IHttpClientBuilder AddRestEaseClient(
            this IServiceCollection services,
            Type restEaseType,
            Uri? baseUri,
            Action<RestClient>? configurer = null,
            RequestModifier? requestModifier = null)
        {
            if (restEaseType is null)
                throw new ArgumentNullException(nameof(restEaseType));

            return services.AddRestEaseClientCore(restEaseType, baseUri, requestModifier, httpClient =>
            {
                var restClient = new RestClient(httpClient);
                configurer?.Invoke(restClient);
                return restClient.For(restEaseType);
            });
        }

        private static IHttpClientBuilder AddRestEaseClientCore(
            this IServiceCollection services,
            Type restEaseType,
            Uri? baseUri,
            RequestModifier? requestModifier,
            Func<HttpClient, object> factory)
        {
            if (services is null)
                throw new ArgumentNullException(nameof(services));

            var builder = services.AddHttpClient(UniqueNameForType(restEaseType), httpClient =>
            {
                if (baseUri != null)
                {
                    httpClient.BaseAddress = baseUri;
                }
            }).AddTypedClient(factory);

            if (requestModifier != null)
            {
                builder = builder.ConfigurePrimaryHttpMessageHandler(() => new ModifyingClientHttpHandler(requestModifier));
            }

            return builder;
        }

        private static Uri? ToUri(string? uri) =>
            uri == null ? null : new Uri(uri);

        private static string UniqueNameForType(Type type)
        {
            var sb = new StringBuilder();
            Impl(type);
            return sb.ToString();

            void Impl(Type typeInfo)
            {
                if (typeInfo.IsGenericType)
                {
                    string fullName = type.GetGenericTypeDefinition().FullName;
                    sb.Append(fullName.Substring(0, fullName.LastIndexOf('`')));
                    sb.Append("<");
{'fraction_non_alphanumeric': 0.10350921484473617, 'fraction_numerical': 0.00016830766641420518, 'mean_word_length': 2.156440903054449, 'pattern_counts': {'":': 0, '<': 145, '<?xml version=': 0, '>': 154, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '530781', 'n_tokens_mistral': 2906, 'n_tokens_neox': 2863, 'n_words': 800}