diff --git a/AzureAd/.vscode/settings.json b/AzureAd/.vscode/settings.json deleted file mode 100644 index 40ac7cb..0000000 --- a/AzureAd/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "files.exclude": { - "**/.vs": false - } -} diff --git a/AzureAd/AppType.cs b/AzureAd/AppType.cs deleted file mode 100644 index 79187c8..0000000 --- a/AzureAd/AppType.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Dgmjr.AzureAd.Web; - -[Flags] -public enum AppType -{ - Web = 1, - - // Api = 2, - WebApi = 4, - RazorPages = 8, - Mvc = 16, - Console = 32, - Worker = 64, - Service = 128, - Function = 256, - AzureFunction = 512, - AzureWebJob = 1024, - - ApiBased = WebApi | AzureFunction, - - WebUiBased = Web | RazorPages | Mvc, - - WebBased = ApiBased | WebUiBased, - - All = - Web - | RazorPages - | Mvc - | WebApi - | AzureFunction - | AzureWebJob - | Console - | Worker - | Service - | Function, - - DesktopBased = Console | Worker | Service -} diff --git a/AzureAd/AutomaticAzureAdConfigurator.cs b/AzureAd/AutomaticAzureAdConfigurator.cs deleted file mode 100644 index db941fe..0000000 --- a/AzureAd/AutomaticAzureAdConfigurator.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Microsoft.Extensions.DependencyInjection; - -public class AutomaticAzureAdConfigurator - : IConfigureIHostApplicationBuilder, - IConfigureIApplicationBuilder -{ - public ConfigurationOrder Order => ConfigurationOrder.VeryEarly; - - public void Configure(WebApplicationBuilder builder) - { - builder.AddAzureAdB2CIdentity(); - } - - public void Configure(IApplicationBuilder options) - { - options.UseAzureAdB2CIdentity(); - } -} diff --git a/AzureAd/AzureAdApplicationBuilderIdentityExtensions.cs b/AzureAd/AzureAdApplicationBuilderIdentityExtensions.cs deleted file mode 100644 index 0352ed8..0000000 --- a/AzureAd/AzureAdApplicationBuilderIdentityExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Microsoft.Extensions.DependencyInjection; - -using Dgmjr.AzureAd.Web; - -public static class AzureAdApplicationBuilderIdentityExtensions -{ - public static IApplicationBuilder UseAzureAdB2CIdentity(this IApplicationBuilder app) - { - var mvcOptions = app.ApplicationServices - .GetService>() - ?.Value; - app.UseSession(); - app.UseAuthentication(); - app.UseRouting(); - app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - if (mvcOptions?.AddControllers == true) - endpoints.MapControllers(); - if (mvcOptions?.AddRazorPages == true) - endpoints.MapRazorPages(); - }); - return app; - } -} diff --git a/AzureAd/AzureAdConstants.cs b/AzureAd/AzureAdConstants.cs deleted file mode 100644 index 6a20fbc..0000000 --- a/AzureAd/AzureAdConstants.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Dgmjr.AzureAd; - -public static class Constants -{ - /// DownstreamApis - public const string DownstreamApis = nameof(DownstreamApis); - - /// MicrosoftGraphOptions - public const string MicrosoftGraphOptions = nameof(MicrosoftGraphOptions); - - /// MicrosoftGraph - public const string MicrosoftGraph = nameof(MicrosoftGraph); - - /// Scopes - public const string Scopes = nameof(Scopes); - - /// AzureAdB2C - public const string AzureAdB2C = Microsoft.Identity.Web.Constants.AzureAdB2C; - - /// AzureAd - public const string AzureAd = Microsoft.Identity.Web.Constants.AzureAd; - - /// OpenIdConnect - public const string OpenIdConnect = OpenIdConnectDefaults.AuthenticationScheme; - - /// :: - public const string DownstreamApis_MsGraph_ScopesConfigurationKey = - DownstreamApis + ":" + MicrosoftGraph + ":" + Scopes; - - /// : - public const string DownstreamApis_MsGraphConfigurationKey = - DownstreamApis + ":" + MicrosoftGraph; - - /// JwtBearer - public const string JwtBearerSchemeName = "JwtBearer"; - - /// JWT Bearer - public const string JwtBearerSchemeDisplayName = "JWT Bearer"; -} diff --git a/AzureAd/AzureAdHostApplicationBuilderIdentityExtensions.cs b/AzureAd/AzureAdHostApplicationBuilderIdentityExtensions.cs deleted file mode 100644 index 4159c12..0000000 --- a/AzureAd/AzureAdHostApplicationBuilderIdentityExtensions.cs +++ /dev/null @@ -1,149 +0,0 @@ -namespace Microsoft.Extensions.DependencyInjection; - -using System.Net.Http; - -using Dgmjr.AzureAd.Web; - -using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.AspNetCore.Components.Server; -using Microsoft.Extensions.Logging; -using Microsoft.Identity.Web.Resource; -using Microsoft.Identity.Web.UI; -using MicrosoftIdentityOptions = Dgmjr.AzureAd.Web.MicrosoftIdentityOptions; -using MsidCallsWebApiAuthBuilder = MicrosoftIdentityAppCallsWebApiAuthenticationBuilder; - -public static class AzureAdHostApplicationBuilderIdentityExtensions -{ - public static WebApplicationBuilder AddAzureAdB2CIdentity(this WebApplicationBuilder builder) - { - if ( - builder.Services.Any( - sd => sd.ServiceType == typeof(MicrosoftIdentityIssuerValidatorFactory) - ) - ) - { - return builder; // we've already added the services - } - - JwtSecurityTokenHandler.DefaultMapInboundClaims = false; - IdentityModelEventSource.ShowPII = true; - IdentityModelEventSource.Logger.LogLevel = EventLevel.Verbose; - IdentityModelEventSource.LogCompleteSecurityArtifact = true; - var initialScopes = builder.Configuration.GetValue( - DownstreamApis_MsGraph_ScopesConfigurationKey - )!; - var configurationSection = builder.Configuration.GetSection(AzureAdB2C); - var options = configurationSection.Get(); - - var authenticationBuilder = builder.Services.AddAuthentication(OpenIdConnect); - - MsidCallsWebApiAuthBuilder callsWebApiAuthenticationBuilder; - if (AppType.WebUiBased.HasFlag(options.AppType)) - { - Console.WriteLine("Registering Microsoft Identity Web UI."); - callsWebApiAuthenticationBuilder = authenticationBuilder - .AddMicrosoftIdentityWebApp(configurationSection) - .EnableTokenAcquisitionToCallDownstreamApi(opts => configurationSection.Bind(opts), options.Scope); - builder.Services.AddMvc().AddMicrosoftIdentityUI(); - } - else if (AppType.ApiBased.HasFlag(options.AppType)) - { - Console.WriteLine("Registering app with type {0}", options.AppType); - callsWebApiAuthenticationBuilder = authenticationBuilder - .AddMicrosoftIdentityWebApi(configurationSection) - .EnableTokenAcquisitionToCallDownstreamApi(opts => configurationSection.Bind(opts)); - } - else - { - throw new InvalidOperationException( - $"Invalid AppType value \"{options.AppType}\" for AzureAdB2C configuration." - ); - } - - var msGraphOptionsConfigSection = builder.Configuration.GetSection(DownstreamApis_MsGraphConfigurationKey); - var msGraphOptions = msGraphOptionsConfigSection.Get(); - - authenticationBuilder.AddJwtBearer( - JwtBearerSchemeName, - JwtBearerSchemeDisplayName, - options => configurationSection.Bind(options) - ); - - if(msGraphOptions.AppOnly) - { - callsWebApiAuthenticationBuilder - .AddMicrosoftGraphAppOnly(authProvider => new GraphServiceClient(authProvider)) - .AddDistributedTokenCaches(); - } - else - { - callsWebApiAuthenticationBuilder - .AddMicrosoftGraph(msGraphOptionsConfigSection) - .AddDistributedTokenCaches(); - } - - // callsWebApiAuthenticationBuilder.AddSessionTokenCaches(); - - foreach ( - var downstreamApiConfig in builder.Configuration - .GetSection(Dgmjr.AzureAd.Constants.DownstreamApis) - .GetChildren() - ) - { - callsWebApiAuthenticationBuilder.AddDownstreamApi( - downstreamApiConfig.Key, - options => - { - downstreamApiConfig.Bind(options); - options.Serializer = requestObject => - new StringContent( - Serialize( - requestObject, - builder.Services - .BuildServiceProvider() - .CreateScope() - .ServiceProvider.GetRequiredService< - IOptionsMonitor - >() - .CurrentValue.JsonSerializerOptions - ) - ); - } - ); - } - - builder.Services - .AddMicrosoftIdentityConsentHandler() - .AddTransient(); - builder.Services - // .ConfigureAll( - // downstreamApiOptions => - // downstreamApiOptions.Serializer = requestObject => - // new StringContent( - // Serialize( - // requestObject, - // builder.Services - // .BuildServiceProvider() - // .CreateScope() - // .ServiceProvider.GetRequiredService< - // IOptionsMonitor - // >() - // .CurrentValue.JsonSerializerOptions - // ) - // ) - // ) - .Configure( - builder.Configuration.GetSection(AzureAdB2C) - ) - .Configure( - builder.Configuration.GetSection(AzureAdB2C) - ) - .Configure( - builder.Configuration.GetSection(DownstreamApis_MsGraphConfigurationKey) - ) - .AddAuthorization() - .AddSingleton() - .AddScoped(); - return builder; - } -} diff --git a/AzureAd/Dgmjr.AzureAd.csproj b/AzureAd/Dgmjr.AzureAd.csproj deleted file mode 100644 index 3aecf08..0000000 --- a/AzureAd/Dgmjr.AzureAd.csproj +++ /dev/null @@ -1,42 +0,0 @@ - - - A set of extensions for Microsoft.AspNetCore.Authentication.OpenIdConnect - net6.0;net8.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AzureAd/Dgmjr.AzureAd.props b/AzureAd/Dgmjr.AzureAd.props deleted file mode 100644 index f246b75..0000000 --- a/AzureAd/Dgmjr.AzureAd.props +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/AzureAd/Dgmjr.AzureAd.sln b/AzureAd/Dgmjr.AzureAd.sln deleted file mode 100644 index 35acfa9..0000000 --- a/AzureAd/Dgmjr.AzureAd.sln +++ /dev/null @@ -1,42 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B283EBC2-E01F-412D-9339-FD56EF114549}" - ProjectSection(SolutionItems) = preProject - ..\Directory.Build.props = ..\Directory.Build.props - ..\Directory.Build.targets = ..\Directory.Build.targets - ..\..\..\..\global.json = ..\..\..\..\global.json - ..\..\..\..\Packages\Versions.Local.props = ..\..\..\..\Packages\Versions.Local.props - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dgmjr.AzureAd", "Dgmjr.AzureAd.csproj", "{6C87F893-EEB5-409F-A3D9-6AB329FABAE0}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Local|Any CPU = Local|Any CPU - Debug|Any CPU = Debug|Any CPU - Testing|Any CPU = Testing|Any CPU - Staging|Any CPU = Staging|Any CPU - Production|Any CPU = Production|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6C87F893-EEB5-409F-A3D9-6AB329FABAE0}.Local|Any CPU.ActiveCfg = Local|Any CPU - {6C87F893-EEB5-409F-A3D9-6AB329FABAE0}.Local|Any CPU.Build.0 = Local|Any CPU - {6C87F893-EEB5-409F-A3D9-6AB329FABAE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6C87F893-EEB5-409F-A3D9-6AB329FABAE0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6C87F893-EEB5-409F-A3D9-6AB329FABAE0}.Testing|Any CPU.ActiveCfg = Testing|Any CPU - {6C87F893-EEB5-409F-A3D9-6AB329FABAE0}.Testing|Any CPU.Build.0 = Testing|Any CPU - {6C87F893-EEB5-409F-A3D9-6AB329FABAE0}.Staging|Any CPU.ActiveCfg = Staging|Any CPU - {6C87F893-EEB5-409F-A3D9-6AB329FABAE0}.Staging|Any CPU.Build.0 = Staging|Any CPU - {6C87F893-EEB5-409F-A3D9-6AB329FABAE0}.Production|Any CPU.ActiveCfg = Local|Any CPU - {6C87F893-EEB5-409F-A3D9-6AB329FABAE0}.Production|Any CPU.Build.0 = Local|Any CPU - {6C87F893-EEB5-409F-A3D9-6AB329FABAE0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6C87F893-EEB5-409F-A3D9-6AB329FABAE0}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {D2BA4CE9-FBAD-4D65-8207-C28CCFBB4C1A} - EndGlobalSection -EndGlobal diff --git a/AzureAd/DownstreamApiOptions.cs b/AzureAd/DownstreamApiOptions.cs deleted file mode 100644 index 12221ca..0000000 --- a/AzureAd/DownstreamApiOptions.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Dgmjr.AzureAd; - -public class DownstreamApiOptions : Microsoft.Identity.Abstractions.DownstreamApiOptions -{ - public type ServiceInterface { get; set; } - public type ServiceImplementation { get; set; } -} diff --git a/AzureAd/DownstreamApiOptionsConfigurator.cs b/AzureAd/DownstreamApiOptionsConfigurator.cs deleted file mode 100644 index e0e73dc..0000000 --- a/AzureAd/DownstreamApiOptionsConfigurator.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Dgmjr.AzureAd; -using System.Net.Http; -using Application = Dgmjr.Mime.Application; - -public class DownstreamApiOptionsConfigurator(IOptionsMonitor jsonOptions) : IConfigureOptions -{ - private readonly JsonOptions _jsonOptions = jsonOptions.CurrentValue; - - public void Configure(DownstreamApiOptions options) - { - options.AcquireTokenOptions ??= new(); - options.Serializer = requestObject => - new StringContent( - Serialize( - requestObject, - _jsonOptions.JsonSerializerOptions - ), - UTF8, - ApplicationMediaTypeNames.Json - ); - options.CustomizeHttpRequestMessage += (message) => - { - message.Headers.Add(HReqH.Accept.DisplayName, Application.Json.DisplayName); - message.Headers.Add(HReqH.ContentType.DisplayName, Application.Json.DisplayName); - message.Headers.Add(HReqH.AcceptCharset.DisplayName, UTF8.HeaderName); - }; - } -} diff --git a/AzureAd/LICENSE.md b/AzureAd/LICENSE.md deleted file mode 100644 index 4f592f8..0000000 --- a/AzureAd/LICENSE.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -date: 2023-07-13T05:44:46:00-05:00Z -description: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files, yadda, yadda, yadda... -keywords: -- IP -- copyright -- license -- mit -permissions: -- commercial-use -- modifications -- distribution -- private-use -conditions: -- include-copyright -limitations: -- liability -- warranty -lastmod: 2024-01-0T00:39:00.0000+05:00Z -license: MIT -slug: mit-license -title: MIT License -type: license ---- - -# MIT License - -## Copyright © 2022-2024 [David G. Moore, Jr.](mailto:david@dgmjr.io "Send Dr. Moore") ([@dgmjr](https://github.com/dgmjr "Contact Dr. Moore on GitHub")), All Rights Reserved - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/AzureAd/LoggerExtensions.cs b/AzureAd/LoggerExtensions.cs deleted file mode 100644 index d18b7a7..0000000 --- a/AzureAd/LoggerExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Dgmjr.AzureAd; - -using Microsoft.Extensions.Logging; -using Dgmjr.AzureAd.Web; - -public static partial class LoggerExtensions -{ - [LoggerMessage( - EventId = 1, - Level = LogLevel.Trace, - Message = "Registering Microsoft Identity Web UI.", - EventName = nameof(RegisteringIdentityWebUi) - )] - public static partial void RegisteringIdentityWebUi(this ILogger logger); - - [LoggerMessage( - EventId = 2, - Level = LogLevel.Trace, - Message = "Registering app of type {AppType}", - EventName = nameof(RegisteringAppWithType) - )] - public static partial void RegisteringAppWithType(this ILogger logger, AppType appType); -} diff --git a/AzureAd/MicrosoftIdentityOptions.cs b/AzureAd/MicrosoftIdentityOptions.cs deleted file mode 100644 index 082aaca..0000000 --- a/AzureAd/MicrosoftIdentityOptions.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Dgmjr.AzureAd.Web; - -public class MicrosoftIdentityOptions : Microsoft.Identity.Web.MicrosoftIdentityOptions -{ - public AppType AppType { get; set; } = AppType.WebApi; - public string DefaultFallbackRoute { get; set; } = "/index"; - public ICollection InitialScopes => Scope; -} diff --git a/AzureAd/OpenIdConnectOptionsExtensions.cs b/AzureAd/OpenIdConnectOptionsExtensions.cs deleted file mode 100644 index e26f7b9..0000000 --- a/AzureAd/OpenIdConnectOptionsExtensions.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Microsoft.AspNetCore.Authentication.OpenIdConnect.Extensions; - -public static class OpenIdConnectOptionsExtensions -{ - public static bool IsUsingClientSecret(this OpenIdConnectOptions options) => - !IsNullOrEmpty(options.ClientSecret); - - public static bool IsUsingClientSecret(this MicrosoftIdentityOptions options) => - options.ClientCredentials.Any(cc => cc.CredentialType == CredentialType.Secret); - - public static bool IsUsingClientCertificate(this MicrosoftIdentityOptions options) => - options.ClientCredentials.Any(cc => cc.CredentialType == CredentialType.Certificate); -} diff --git a/AzureAd/icon.png b/AzureAd/icon.png deleted file mode 100644 index db07a03..0000000 Binary files a/AzureAd/icon.png and /dev/null differ diff --git a/Caching/LICENSE.md b/Caching/LICENSE.md index c80a676..4f592f8 100755 --- a/Caching/LICENSE.md +++ b/Caching/LICENSE.md @@ -1,21 +1,21 @@ --- -date: 2023-07-13T05:44:46:00+05:00Z +date: 2023-07-13T05:44:46:00-05:00Z description: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files, yadda, yadda, yadda... keywords: - - IP - - copyright - - license - - mit +- IP +- copyright +- license +- mit permissions: - - commercial-use - - modifications - - distribution - - private-use +- commercial-use +- modifications +- distribution +- private-use conditions: - - include-copyright +- include-copyright limitations: - - liability - - warranty +- liability +- warranty lastmod: 2024-01-0T00:39:00.0000+05:00Z license: MIT slug: mit-license @@ -25,7 +25,7 @@ type: license # MIT License -## Copyright © 2022-2023 [David G. Moore, Jr.](mailto:david@dgmjr.io "Send Dr. Moore an email") ([@dgmjr](https://github.com/dgmjr "Contact Dr. Moore on GitHub")), All Rights Reserved +## Copyright © 2022-2024 [David G. Moore, Jr.](mailto:david@dgmjr.io "Send Dr. Moore") ([@dgmjr](https://github.com/dgmjr "Contact Dr. Moore on GitHub")), All Rights Reserved Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/Logging/Dgmjr.Logging.Extensions.sln b/Logging/Dgmjr.Logging.Extensions.sln deleted file mode 100644 index eca24d2..0000000 --- a/Logging/Dgmjr.Logging.Extensions.sln +++ /dev/null @@ -1,27 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B283EBC2-E01F-412D-9339-FD56EF114549}" - ProjectSection(SolutionItems) = preProject - ..\Directory.Build.props = ..\Directory.Build.props - ..\Directory.Build.targets = ..\Directory.Build.targets - ..\..\..\..\global.json = ..\..\..\..\global.json - ..\..\..\..\Packages\Versions.Local.props = ..\..\..\..\Packages\Versions.Local.props - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Local|Any CPU = Local|Any CPU - Debug|Any CPU = Debug|Any CPU - Testing|Any CPU = Testing|Any CPU - Staging|Any CPU = Staging|Any CPU - Production|Any CPU = Production|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {D72166ED-2488-413C-ADF8-E7A85D23BB9F} - EndGlobalSection -EndGlobal diff --git a/Logging/LICENSE.md b/Logging/LICENSE.md index c80a676..4f592f8 100755 --- a/Logging/LICENSE.md +++ b/Logging/LICENSE.md @@ -1,21 +1,21 @@ --- -date: 2023-07-13T05:44:46:00+05:00Z +date: 2023-07-13T05:44:46:00-05:00Z description: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files, yadda, yadda, yadda... keywords: - - IP - - copyright - - license - - mit +- IP +- copyright +- license +- mit permissions: - - commercial-use - - modifications - - distribution - - private-use +- commercial-use +- modifications +- distribution +- private-use conditions: - - include-copyright +- include-copyright limitations: - - liability - - warranty +- liability +- warranty lastmod: 2024-01-0T00:39:00.0000+05:00Z license: MIT slug: mit-license @@ -25,7 +25,7 @@ type: license # MIT License -## Copyright © 2022-2023 [David G. Moore, Jr.](mailto:david@dgmjr.io "Send Dr. Moore an email") ([@dgmjr](https://github.com/dgmjr "Contact Dr. Moore on GitHub")), All Rights Reserved +## Copyright © 2022-2024 [David G. Moore, Jr.](mailto:david@dgmjr.io "Send Dr. Moore") ([@dgmjr](https://github.com/dgmjr "Contact Dr. Moore on GitHub")), All Rights Reserved Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/Logging/LoggerWebApplicationBuilderExtensions.cs b/Logging/LoggerWebApplicationBuilderExtensions.cs index 073c99d..454bd7b 100644 --- a/Logging/LoggerWebApplicationBuilderExtensions.cs +++ b/Logging/LoggerWebApplicationBuilderExtensions.cs @@ -33,7 +33,6 @@ public static partial class LoggingWebApplicationBuilderExtensions private const string ApplicationInsightsConnectionString = $"{ApplicationInsights}:{ConnectionString}"; -#if NET5_0_OR_GREATER public static WebApplicationBuilder AddLoggingAndApplicationInsightsTelemetry( this WebApplicationBuilder builder ) @@ -59,7 +58,9 @@ this WebApplicationBuilder builder #endif .AddOpenTelemetry() .AddSerilog() - .AddAzureWebAppDiagnostics(); + .AddAzureWebAppDiagnostics() + // .AddEventLog() + .AddEventSourceLogger(); builder.Services.AddTransient(sp => { @@ -69,7 +70,6 @@ this WebApplicationBuilder builder return builder; } -#endif public static IServiceCollection AddLoggingAndApplicationInsightsTelemetry( this IServiceCollection services, diff --git a/Redis/LICENSE.md b/Redis/LICENSE.md index c80a676..4f592f8 100755 --- a/Redis/LICENSE.md +++ b/Redis/LICENSE.md @@ -1,21 +1,21 @@ --- -date: 2023-07-13T05:44:46:00+05:00Z +date: 2023-07-13T05:44:46:00-05:00Z description: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files, yadda, yadda, yadda... keywords: - - IP - - copyright - - license - - mit +- IP +- copyright +- license +- mit permissions: - - commercial-use - - modifications - - distribution - - private-use +- commercial-use +- modifications +- distribution +- private-use conditions: - - include-copyright +- include-copyright limitations: - - liability - - warranty +- liability +- warranty lastmod: 2024-01-0T00:39:00.0000+05:00Z license: MIT slug: mit-license @@ -25,7 +25,7 @@ type: license # MIT License -## Copyright © 2022-2023 [David G. Moore, Jr.](mailto:david@dgmjr.io "Send Dr. Moore an email") ([@dgmjr](https://github.com/dgmjr "Contact Dr. Moore on GitHub")), All Rights Reserved +## Copyright © 2022-2024 [David G. Moore, Jr.](mailto:david@dgmjr.io "Send Dr. Moore") ([@dgmjr](https://github.com/dgmjr "Contact Dr. Moore on GitHub")), All Rights Reserved Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/System/Dgmjr.System.Extensions.csproj b/System/Dgmjr.System.Extensions.csproj index bad65c2..45df060 100644 --- a/System/Dgmjr.System.Extensions.csproj +++ b/System/Dgmjr.System.Extensions.csproj @@ -35,6 +35,15 @@ + + + + + + + + + diff --git a/System/Dgmjr.System.Extensions.props b/System/Dgmjr.System.Extensions.props index aef3c20..a623d75 100644 --- a/System/Dgmjr.System.Extensions.props +++ b/System/Dgmjr.System.Extensions.props @@ -51,15 +51,6 @@ - - - - - - - - - diff --git a/System/LICENSE.md b/System/LICENSE.md index 4f60874..4f592f8 100755 --- a/System/LICENSE.md +++ b/System/LICENSE.md @@ -1,22 +1,22 @@ --- -date: 2023-07-13T05:44:46:00.048Z +date: 2023-07-13T05:44:46:00-05:00Z description: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files, yadda, yadda, yadda... keywords: - - IP - - copyright - - license - - mit +- IP +- copyright +- license +- mit permissions: - - commercial-use - - modifications - - distribution - - private-use +- commercial-use +- modifications +- distribution +- private-use conditions: - - include-copyright +- include-copyright limitations: - - liability - - warranty -lastmod: 2023-08-29T17:13:51:00.216Z +- liability +- warranty +lastmod: 2024-01-0T00:39:00.0000+05:00Z license: MIT slug: mit-license title: MIT License @@ -25,7 +25,7 @@ type: license # MIT License -## Copyright © 2022-2023 [David G. Moore, Jr.](mailto:david@dgmjr.io "Send David an email") ([@dgmjr](https://github.com/dgmjr "Contact david on GitHub")), All Rights Reserved +## Copyright © 2022-2024 [David G. Moore, Jr.](mailto:david@dgmjr.io "Send Dr. Moore") ([@dgmjr](https://github.com/dgmjr "Contact Dr. Moore on GitHub")), All Rights Reserved Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/System/System.Collections.Generic/CaseInsensitiveKeyDictionary{TValue}.cs b/System/System.Collections.Generic/CaseInsensitiveKeyDictionary{TValue}.cs index 8522cc7..989a2c9 100644 --- a/System/System.Collections.Generic/CaseInsensitiveKeyDictionary{TValue}.cs +++ b/System/System.Collections.Generic/CaseInsensitiveKeyDictionary{TValue}.cs @@ -9,15 +9,27 @@ * Copyright © 2022 - 2023 David G. Moore, Jr., All Rights Reserved * License: MIT (https://opensource.org/licenses/MIT) */ +// Documentation for CaseInsensitiveKeyDictionary -using System.Linq; +using System.Linq; // Using directive for LINQ functionality -namespace System.Collections.Generic; +namespace System.Collections.Generic; // Namespace declaration for the custom class -public class CaseInsensitiveKeyDictionary( - IEnumerable> original -) : Dictionary(original.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), StringComparer.OrdinalIgnoreCase) +public class CaseInsensitiveKeyDictionary : Dictionary { - public CaseInsensitiveKeyDictionary() - : this(Empty>()) { } + /// + /// Initializes a new instance of the CaseInsensitiveKeyDictionary class that is empty. + /// + public CaseInsensitiveKeyDictionary() : this(Enumerable.Empty>()) { } + + /// + /// Initializes a new instance of the CaseInsensitiveKeyDictionary class with the specified original key-value pairs. + /// + /// The original key-value pairs to initialize the dictionary with. + public CaseInsensitiveKeyDictionary(IEnumerable> original) + : base(original.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), StringComparer.OrdinalIgnoreCase) + { + // Calls the base class constructor (Dictionary) to populate the dictionary + // Key comparison is done in a case-insensitive manner using StringComparer.OrdinalIgnoreCase + } } diff --git a/System/System.Collections.Generic/DefaultableDictionary{TKey,TValue}.cs b/System/System.Collections.Generic/DefaultableDictionary{TKey,TValue}.cs index cc6451f..02f1f0b 100644 --- a/System/System.Collections.Generic/DefaultableDictionary{TKey,TValue}.cs +++ b/System/System.Collections.Generic/DefaultableDictionary{TKey,TValue}.cs @@ -55,20 +55,32 @@ public class DefaultableDictionary : IDictionary /// This value is set in the constructor and cannot be changed after the /// object is created. /// - public TValue DefaultValue { get; init; } + public TValue DefaultValue => DefaultValueFactory(default); + + /// + /// The default factory that will be used tp create an instance of a(s) to return when a key is not + /// found in the dictionary. + /// + /// The factory that will be used tp create an instance of a(s) to return when a key is + /// not found in the dictionary. + /// + /// This value is set in the constructor and cannot be changed after the + /// object is created. + /// + public Func DefaultValueFactory { get; init; } private readonly Dictionary _dictionary = new(); public DefaultableDictionary() : this(default(TValue)) { } public DefaultableDictionary(IDictionary original) - : this(default, original, EqualityComparer.Default) { } + : this(default(TValue), original, EqualityComparer.Default) { } public DefaultableDictionary( IDictionary original, IEqualityComparer keyComparer ) - : this(default, original, keyComparer) { } + : this(default(TValue), original, keyComparer) { } public DefaultableDictionary(TValue defaultValue) : this(defaultValue, EqualityComparer.Default) { } @@ -80,10 +92,16 @@ public DefaultableDictionary( TValue defaultValue, IDictionary original, IEqualityComparer keyComparer + ) : this(_ => defaultValue, original, keyComparer) { } + + public DefaultableDictionary( + Func defaultValueFactory, + IDictionary original = null, + IEqualityComparer keyComparer = null ) { - this.DefaultValue = defaultValue; - this._dictionary = new Dictionary(original, keyComparer); + DefaultValueFactory = defaultValueFactory; + _dictionary = new(original ?? new Dictionary(), keyComparer ?? EqualityComparer.Default); } public virtual ICollection Keys => ((IDictionary)this._dictionary).Keys; @@ -103,7 +121,7 @@ public virtual TValue this[TKey key] } public virtual void Add(TKey key, TValue value) => - ((IDictionary)this._dictionary).Add(key, value); + ((IDictionary)this._dictionary)[key] = value; public virtual bool ContainsKey(TKey key) => ((IDictionary)this._dictionary).ContainsKey(key); diff --git a/System/System.IO/StreamExtensions.cs b/System/System.IO/StreamExtensions.cs index 3a6b3ba..950029b 100644 --- a/System/System.IO/StreamExtensions.cs +++ b/System/System.IO/StreamExtensions.cs @@ -38,9 +38,9 @@ public static Task ReadToEndAsync(this Stream s) => /// all s from the public static byte[] ReadAllBytes(this Stream stream) { - using var memoryStream = new MemoryStream(); - stream.CopyTo(memoryStream); - return memoryStream.ToArray(); + var buff = new byte[stream.Length]; + stream.Read(buff, 0, buff.Length); + return buff; } /// @@ -48,6 +48,10 @@ public static byte[] ReadAllBytes(this Stream stream) /// /// The stream to read from /// all s from the - public static async Task ReadAllBytesAsync(this Stream stream) => - await Task.Run(() => stream.ReadAllBytes()); + public static async Task ReadAllBytesAsync(this Stream stream) + { + var buff = new byte[stream.Length]; + await stream.ReadAsync(buff, 0, buff.Length); + return buff; + } } diff --git a/System/System.Linq.Expressions/ExpressionExtensions.cs b/System/System.Linq.Expressions/ExpressionExtensions.cs index 1a8a6a4..bf74c4d 100644 --- a/System/System.Linq.Expressions/ExpressionExtensions.cs +++ b/System/System.Linq.Expressions/ExpressionExtensions.cs @@ -40,10 +40,10 @@ public static MethodInfo AsMethod(this LambdaExpression methodCaller) } /// - /// Returns the that describes the property that + /// Returns the that describes the fiels that /// is being returned in an expression in the form: /// - /// x => x.SomeProperty + /// x => x.SomeField /// /// public static FieldInfo AsField(this LambdaExpression fieldAccessor) diff --git a/System/System.Linq/System.Linq.cs b/System/System.Linq/System.Linq.cs index c3caf47..7c8fdb5 100644 --- a/System/System.Linq/System.Linq.cs +++ b/System/System.Linq/System.Linq.cs @@ -43,8 +43,11 @@ public static bool IsNullOrEmpty(this IEnumerable e) => /// . public static void ForEach(this IEnumerable e, Action @foreach) { - foreach (var item in e) + _ = e.Aggregate(0, (i, item) => + { @foreach(item); + return i + 1; + }); } /// @@ -54,10 +57,10 @@ public static void ForEach(this IEnumerable e, Action @foreach) /// elements to. /// The elements to add to the /// . - /// The type of elements in the - /// . /// The type of the /// . + /// The type of elements in the + /// . /// The with the added elements. /// public static TCollection AddRange( @@ -74,10 +77,7 @@ IEnumerable thingsToAdd } else { - foreach (var item in thingsToAdd) - { - collection.Add(item); - } + thingsToAdd.ForEach(item => collection.Add(item)); } } return collection; @@ -86,8 +86,8 @@ IEnumerable thingsToAdd /// Removes the specified elements from the . /// The collection from which to remove the elements /// The elements to remove from the - /// The type of elements in the . /// The type of the . + /// The type of elements in the . /// The with the removed elements. public static TCollection RemoveRange( this TCollection collection, @@ -103,10 +103,7 @@ IEnumerable removeRange } else { - foreach (var item in removeRange) - { - collection.Remove(item); - } + removeRange.ForEach(item => collection.Remove(item)); } } return collection; @@ -118,8 +115,8 @@ IEnumerable removeRange /// /// The collection from which to remove the elements. /// The predicate to match the elements to remove. - /// The type of elements in the . /// The type of the . + /// The type of elements in the . /// The with the removed elements. public static TCollection Without( this TCollection collection, @@ -137,8 +134,8 @@ Func predicate /// /// The collection from which to select the elements. /// The predicate to match the elements to not select. - /// The type of elements in the . /// The type of the . + /// The type of elements in the . /// The without the matchings elements. public static TCollection Except( this TCollection collection, @@ -157,9 +154,9 @@ Func predicate /// /// An IEnumerable of type T representing the collection of values to which value will be appended. /// A new value to append to the - /// T is a generic type parameter that can be replaced with any type at runtime. It is + /// T is a generic type parameter that can be replaced with any type at runtime. It is /// used to define the type of elements in the input IEnumerable and the type of the new value to be - /// appended to it. + /// appended to it. public static IEnumerable Append(IEnumerable values, T newValue) { foreach (var value in values) @@ -176,9 +173,9 @@ public static IEnumerable Append(IEnumerable values, T newValue) /// An IEnumerable of type T representing the collection of values to which a new /// value will be prepended. /// A new value to prepend to the - /// T is a generic type parameter that can be replaced with any type at runtime. It + /// T is a generic type parameter that can be replaced with any type at runtime. It /// represents the type of elements in the input sequence and the type of the new value being added to - /// the beginning of the sequence. + /// the beginning of the sequence. public static IEnumerable Prepend(IEnumerable values, T newValue) { yield return newValue; @@ -188,24 +185,36 @@ public static IEnumerable Prepend(IEnumerable values, T newValue) } } + /// + /// Returns the greatest power of two that is less than or equal to the maximum value in the collection of integers. + /// + /// The collection of integers. + /// The greatest power of two. public static int GreatestPowerOfTwo(this IEnumerable values) { var n = values.Max(); var k = 1; while (k < n) { - k = k << 1; + k <<= 1; } return k; } + /// + /// Returns the greatest power of two that is less than or equal to the maximum value obtained by applying a selector function to each element in the collection. + /// + /// The type of elements in the collection. + /// The collection of elements. + /// A function to extract an integer value from each element. + /// The greatest power of two. public static int GreatestPowerOfTwo(this IEnumerable values, Func selector) { var n = values.Max(selector); var k = 1; while (k < n) { - k = k << 1; + k <<= 1; } return k; } diff --git a/System/System.Text.Json.Extensions/JsonExtensions.cs b/System/System.Text.Json.Extensions/JsonExtensions.cs index 2a0dfaa..2bc023d 100644 --- a/System/System.Text.Json.Extensions/JsonExtensions.cs +++ b/System/System.Text.Json.Extensions/JsonExtensions.cs @@ -9,22 +9,22 @@ public static class JsonExtensions /// /// Parses binary data as JSON. /// - public static JsonElement Parse(byte[] json, JsonDocumentOptions options = default) + public static JElem Parse(byte[] json, JDocOpts options = default) { - using var document = JsonDocument.Parse(json, options); + using var document = JDoc.Parse(json, options); return document.RootElement.Clone(); } /// /// Parses string as JSON. /// - public static JsonElement Parse(string json, JsonDocumentOptions options = default) => + public static JElem Parse(string json, JDocOpts options = default) => Parse(UTF8.GetBytes(json), options); /// /// Tries to parse binary data as JSON or returns null if the input is malformed. /// - public static JsonElement? TryParse(byte[] json, JsonDocumentOptions options = default) + public static JElem? TryParse(byte[] json, JDocOpts options = default) { try { @@ -39,6 +39,6 @@ public static JsonElement Parse(string json, JsonDocumentOptions options = defau /// /// Tries to parse string as JSON or returns null if the input is malformed. /// - public static JsonElement? TryParse(string json, JsonDocumentOptions options = default) => + public static JElem? TryParse(string json, JDocOpts options = default) => TryParse(UTF8.GetBytes(json), options); } diff --git a/System/System.Text.Json/JsonSerializerOptionsBuilder.cs b/System/System.Text.Json/JsonSerializerOptionsBuilder.cs index 4aaa738..616906a 100644 --- a/System/System.Text.Json/JsonSerializerOptionsBuilder.cs +++ b/System/System.Text.Json/JsonSerializerOptionsBuilder.cs @@ -4,7 +4,7 @@ namespace System.Text.Json; using System.Text.Encodings.Web; using System.Text.Json.Serialization.Metadata; -public class JsonSerializerOptionsBuilder : IJsonSerializerOptions +public partial class JsonSerializerOptionsBuilder : IJsonSerializerOptions { public IJsonTypeInfoResolver? TypeInfoResolver { get; set; } public IList TypeInfoResolverChain { get; } = diff --git a/System/System.Xml.Linq/XElementExtensions.cs b/System/System.Xml.Linq/XElementExtensions.cs index 07b7042..402f32c 100644 --- a/System/System.Xml.Linq/XElementExtensions.cs +++ b/System/System.Xml.Linq/XElementExtensions.cs @@ -1,25 +1,4 @@ -// // // #if !NETSTANDARD1_0 -// #if NETSTANDARD2_0 || NETSTANDARD2_1// && !N`ET6_0_OR_GREATER -// extern alias XDoc; -// extern alias XPathDoc; -// using XPathDoc::System.Xml.XPath; -// using XA = XDoc::System.Xml.Linq.XAttribute; -// using XC = XDoc::System.Xml.Linq.XComment; -// using XD = XDoc::System.Xml.Linq.XDocument; -// using XE = XDoc::System.Xml.Linq.XElement; -// using XN = XDoc::System.Xml.Linq.XName; -// using XNS = XDoc::System.Xml.Linq.XNamespace; -// using XO = XDoc::System.Xml.Linq.XNode; -// #else -// using XA = System.Xml.Linq.XAttribute; -// using XC = System.Xml.Linq.XComment; -// using XD = System.Xml.Linq.XDocument; -// using XE = System.Xml.Linq.XElement; -// using XN = System.Xml.Linq.XName; -// using XNS = System.Xml.Linq.XNamespace; -// using XO = System.Xml.Linq.XNode; -// #endif -using System; +using System; namespace System.Xml.Linq; @@ -35,13 +14,19 @@ public static class XElementExtensions /// /// The element. /// The attribute name. - /// A string? . + /// A ? . public static string? GetAttributeValue(this XE element, string attributeName) { var attribute = element.Attribute(attributeName); return attribute?.Value; } + /// + /// Extension method to convert a string to an XElement object. + /// + /// The input XML string to be converted to XElement. + /// A boolean flag indicating whether to throw an exception on invalid XML. True by default. + /// An XElement object representing the XML string, or a default XElement with name "InvalidXml" if conversion fails and throwOnInvalidXml is false. public static XE ToXElement(this string xml, bool throwOnInvalidXml = true) { try @@ -79,7 +64,7 @@ public static XE[] SelectXpath(this XE element, string xpath) /// /// The element. /// The xpath. - /// A string of the value at xpath . + /// A string of the value at . public static string? SelectXpathValue(this XE element, string xpath) { #if !NETSTANDARD2_0_OR_GREATER diff --git a/System/System/EnumExtensions.cs b/System/System/EnumExtensions.cs new file mode 100644 index 0000000..576820c --- /dev/null +++ b/System/System/EnumExtensions.cs @@ -0,0 +1,40 @@ +namespace System; + +public static class EnumExtensions +{ + public static int ToInt32(this T value) + where T : Enum => + ConvertTo(value, out var result) + ? result + : throw new InvalidCastException("Could not convert to int32"); + + public static long ToInt64(this T value) + where T : Enum + => value.ConvertTo(out var result) + ? result + : throw new InvalidCastException("Could not convert to int64"); + + public static bool ConvertTo(this T value, out U result) + where T : Enum + { + try + { + var uValue = Convert.ChangeType(value, typeof(U)); + if (uValue is U u) + { + result = u; + return true; + } + else + { + result = default; + return false; + } + } + catch + { + result = default; + return false; + } + } +} diff --git a/System/System/EnvironmentExtensions.cs b/System/System/EnvironmentExtensions.cs index 62774fe..2e6d515 100644 --- a/System/System/EnvironmentExtensions.cs +++ b/System/System/EnvironmentExtensions.cs @@ -6,13 +6,13 @@ public static string GetEnvironmentVariable( this EnvironmentVariableTarget target, string name, string? defaultValue = null - ) => Environment.GetEnvironmentVariable(name, target) ?? defaultValue ?? ""; + ) => env.GetEnvironmentVariable(name, target) ?? defaultValue ?? ""; public static string GetEnvironmentVariable( this EnvironmentVariableTarget target, string name, Func defaultConstructor - ) => Environment.GetEnvironmentVariable(name, target) ?? defaultConstructor(); + ) => env.GetEnvironmentVariable(name, target) ?? defaultConstructor(); public static string GetEnvironmentName(this EnvironmentVariableTarget target) => target.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development"); diff --git a/System/System/FlagsExtensions.cs b/System/System/FlagsExtensions.cs index b63eae2..752861b 100644 --- a/System/System/FlagsExtensions.cs +++ b/System/System/FlagsExtensions.cs @@ -43,7 +43,7 @@ static class FlagExtensions /// /// public static bool HasFlag(this T enumValue, T flag) - where T : struct + where T : Enum { if (!IsEnum()) { @@ -68,6 +68,7 @@ public static bool HasFlag(this T enumValue, T flag) /// /// public static bool HasFlags(this T value, T flag1, T flag2, params T[] moreFlags) + where T : Enum { if (!ConvertToInt(value, out var intValue)) { @@ -141,7 +142,7 @@ public static bool HasFlags(this int value, IEnumerable flags) /// /// public static T WithFlag(this T enumValue, T flag) - where T : struct + where T : Enum { if (!IsEnum()) { @@ -178,7 +179,7 @@ public static int WithFlag(this int value, int flag) /// /// public static T WithoutFlag(this T enumValue, T flag) - where T : struct + where T : Enum { if (!IsEnum()) { @@ -204,6 +205,7 @@ public static int WithoutFlag(this int value, int flag) } private static bool ConvertToInt(T value, out int result) + where T : Enum { try { @@ -241,6 +243,7 @@ public static bool HasFlag(this int value, int flag) } private static bool IsEnum() + where T : Enum { return typeof(T).IsEnum; } diff --git a/System/System/GuidOrString.cs b/System/System/GuidOrString.cs new file mode 100644 index 0000000..a5deddd --- /dev/null +++ b/System/System/GuidOrString.cs @@ -0,0 +1,7 @@ +namespace System; + +public class GuidOrString : TOrString +{ + public GuidOrString(string str) : base(str) { } + public GuidOrString(guid guid) : base(guid) { } +} diff --git a/System/System/IComplex.cs b/System/System/IComplex.cs index 0a704c4..5b0ded9 100644 --- a/System/System/IComplex.cs +++ b/System/System/IComplex.cs @@ -19,8 +19,8 @@ public partial interface IComplex : IComparable double Magnitude { get; } double Phase { get; } double Real { get; } - bool Equals(global::System.Numerics.Complex value); - string ToString(global::System.IFormatProvider provider); + bool Equals(Complex value); + string ToString(IFormatProvider provider); string ToString(string format); - string ToString(string format, global::System.IFormatProvider provider); + string ToString(string format, IFormatProvider provider); } diff --git a/System/System/JsonIntegerToTimeSpanConverter.cs b/System/System/JsonIntegerToTimeSpanConverter.cs index a4b58d2..a58a887 100644 --- a/System/System/JsonIntegerToTimeSpanConverter.cs +++ b/System/System/JsonIntegerToTimeSpanConverter.cs @@ -7,16 +7,8 @@ namespace System; public class JsonIntegerToTimeSpanConverter : JsonConverter { public override duration Read(ref Utf8JsonReader reader, type typeToConvert, Jso options) - { - if (reader.TokenType != JTokenType.Number) - { - throw new JsonException(); - } - return duration.FromSeconds(reader.GetInt64()); - } + => reader.TokenType != JTokenType.Number ? throw new JsonException() : duration.FromSeconds(reader.GetInt64()); public override void Write(Utf8JsonWriter writer, duration value, Jso options) - { - writer.WriteNumberValue(value.TotalSeconds); - } + => writer.WriteNumberValue(value.TotalSeconds); } diff --git a/System/System/ObjectOrString.cs b/System/System/ObjectOrString.cs new file mode 100644 index 0000000..7b6b2de --- /dev/null +++ b/System/System/ObjectOrString.cs @@ -0,0 +1,36 @@ +namespace System; + +using OneOf; + +public abstract class TOrString where TSelf : TOrString +{ + protected TOrString(string str) + { + Value = str; + } + + protected TOrString(T t) + { + Value = t; + } + + private object Value { get; } + + private T? AsT => (T?)Value; + private string? String => Value as string; + + public bool IsT => AsT is not null; + public bool IsString => String != null; + + + public static implicit operator TOrString(T t) => (Activator.CreateInstance(typeof(TSelf), t) as TSelf)!; + + public static implicit operator TOrString(string str) => (Activator.CreateInstance(typeof(TSelf), str) as TSelf)!; + + public static implicit operator T(TOrString tOrString) => + tOrString.IsT ? tOrString.AsT! : (Activator.CreateInstance(typeof(TSelf), tOrString) as TSelf)!; + + public static explicit operator string(TOrString tOrString) => tOrString.ToString(); + + public override string ToString() => IsString ? String! : AsT!.ToString(); +} diff --git a/System/System/StringExtensions.cs b/System/System/StringExtensions.cs index 0815250..450207a 100644 --- a/System/System/StringExtensions.cs +++ b/System/System/StringExtensions.cs @@ -12,6 +12,8 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Specialized; +using System.Text.Encodings.Web; namespace System; @@ -100,4 +102,262 @@ public static bool Contains( /// The concatenated string public static string Join(string separator, IEnumerable values) => string.Join(separator, values.OfType()); + + /// + /// Checks if the given string is null, empty, or consists only of whitespace characters. + /// + /// The string to check. + /// true if the string is null, empty, or consists only of whitespace characters; otherwise, false. + [DebuggerStepThrough] + public static bool IsMissing(this string value) + { + return IsNullOrWhiteSpace(value); + } + + /// + /// Checks if the given string is null, empty, consists only of whitespace characters, or exceeds a specified maximum length. + /// + /// The string to check. + /// The maximum length allowed. + /// true if the string is null, empty, consists only of whitespace characters, or exceeds the specified maximum length; otherwise, false. + [DebuggerStepThrough] + public static bool IsMissingOrTooLong(this string value, int maxLength) + { + return IsNullOrWhiteSpace(value) || value.Length > maxLength; + } + + /// + /// Checks if the given string is not null, not empty, and contains at least one non-whitespace character. + /// + /// The string to check. + /// true if the string is not null, not empty, and contains at least one non-whitespace character; otherwise, false. + [DebuggerStepThrough] + public static bool IsPresent(this string value) + { + return !IsNullOrWhiteSpace(value); + } + + /// + /// Ensures that the URL has a leading forward slash (/) character. If the URL already starts with a forward slash, it is returned unchanged. + /// + /// The URL to modify. + /// The URL with a leading forward slash (/) character. + [DebuggerStepThrough] + public static string? EnsureLeadingSlash(this string url) + { + return url?.StartsWith("/") == false ? "/" + url : url; + } + + /// + /// Ensures that the URL has a trailing forward slash (/) character. If the URL already ends with a forward slash, it is returned unchanged. + /// + /// The URL to modify. + /// The URL with a trailing forward slash (/) character. + [DebuggerStepThrough] + public static string? EnsureTrailingSlash(this string url) + { + return url?.EndsWith("/") == false ? url + "/" : url; + } + + /// + /// Removes the leading forward slash (/) character from the URL, if it exists. + /// + /// The URL to modify. + /// The URL with the leading forward slash (/) character removed, or the original URL if it doesn't start with a forward slash. + [DebuggerStepThrough] + public static string? RemoveLeadingSlash(this string url) + { + if (url?.StartsWith("/") == true) + { + url = url.Substring(1); + } + + return url; + } + + /// + /// Removes the trailing forward slash (/) character from the URL, if it exists. + /// + /// The URL to modify. + /// The URL with the trailing forward slash (/) character removed, or the original URL if it doesn't end with a forward slash. + [DebuggerStepThrough] + public static string? RemoveTrailingSlash(this string url) + { + if (url?.EndsWith("/") == true) + { + url = url.Substring(0, url.Length - 1); + } + + return url; + } + + /// + /// Cleans the URL path by replacing a null or empty URL with a forward slash (/) character, + /// and removing the trailing forward slash (/) character, if it exists and the URL is not equal to "/". + /// + /// The URL to clean. + /// The cleaned URL. + [DebuggerStepThrough] + public static string CleanUrlPath(this string url) + { + if (IsNullOrWhiteSpace(url)) + url = "/"; + + if (url != "/" && url.EndsWith("/")) + { + url = url.Substring(0, url.Length - 1); + } + + return url; + } + + /// + /// Checks if the given URL is a local URL. + /// + /// The URL to check. + /// True if the URL is local; otherwise, false. + [DebuggerStepThrough] + public static bool IsLocalUrl(this string url) + { + if (IsNullOrEmpty(url)) + { + return false; + } + + // Allows "/" or "/foo" but not "//" or "/\". + if (url[0] == '/') + { + // url is exactly "/" + if (url.Length == 1) + { + return true; + } + + // url doesn't start with "//" or "/\" + return url[1] != '/' && url[1] != '\\'; + } + + // Allows "~/" or "~/foo" but not "~//" or "~/\". + if (url[0] == '~' && url.Length > 1 && url[1] == '/') + { + // url is exactly "~/" + if (url.Length == 2) + { + return true; + } + + // url doesn't start with "~//" or "~/\" + return url[2] != '/' && url[2] != '\\'; + } + + return false; + } + + /// + /// Adds a query string to the given URL. + /// + /// The URL to add the query string to. + /// The query string to add. + /// The modified URL with the added query string. + [DebuggerStepThrough] + public static string AddQueryString(this string url, string query) + { + if (!url.Contains('?')) + { + url += "?"; + } + else if (!url.EndsWith("&")) + { + url += "&"; + } + + return url + query; + } + + /// + /// Adds a query string parameter to the given URL. + /// + /// The URL to add the query string parameter to. + /// The name of the query string parameter. + /// The value of the query string parameter. + /// The modified URL with the added query string parameter. + [DebuggerStepThrough] + public static string AddQueryString(this string url, string name, string value) + { + return url.AddQueryString(name + "=" + UrlEncoder.Default.Encode(value)); + } + + /// + /// Adds a hash fragment to the given URL. + /// + /// The URL to add the hash fragment to. + /// The hash fragment to add. + /// The modified URL with the added hash fragment. + [DebuggerStepThrough] + public static string AddHashFragment(this string url, string query) + { + if (!url.Contains('#')) + { + url += "#"; + } + + return url + query; + } + + private const string Http = "http"; + private const string Https = "https"; + + /// + /// Retrieves the origin of the given URL. + /// + /// The URL to retrieve the origin from. + /// The origin part of the URL (scheme and authority), or null if the URL is invalid or not using HTTP or HTTPS scheme. + public static string? GetOrigin(this string url) + { + if (url != null) + { + Uri uri; + try + { + uri = new Uri(url); + } + catch (Exception) + { + return null; + } + + if (uri.Scheme == Http || uri.Scheme == Https) + { + return $"{uri.Scheme}://{uri.Authority}"; + } + } + + return null; + } + + /// + /// Obfuscates the given string value by replacing all characters except the last four characters with asterisks. + /// + /// The string value to obfuscate. + /// The obfuscated string value. + public static string Obfuscate(this string value) + { + var last4Chars = "****"; + if (value.IsPresent() && value.Length > 4) + { + last4Chars = value.Substring(0, value.Length - 4); + } + + return "****" + last4Chars; + } + + /// + /// Checks if the given string value is a valid URI. + /// + /// The string value to check. + /// True if the string value is a valid URI; otherwise, false. + public static bool IsUri(this string value) + { + return Uri.TryCreate(value, Absolute, out _); + } } diff --git a/System/System/UriOrString.cs b/System/System/UriOrString.cs index b5dedd8..5791e7f 100644 --- a/System/System/UriOrString.cs +++ b/System/System/UriOrString.cs @@ -2,7 +2,7 @@ namespace System; using OneOf; -public readonly record struct UriOrString +public class UriOrString { public UriOrString(string str) {