diff --git a/.circleci/config.yml b/.circleci/config.yml index d191187..42dcb25 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,17 +14,9 @@ jobs: command: dotnet build - run: - name: Core Test + name: Tests command: - dotnet test tests/dotenv.net.Tests/dotenv.net.Tests.csproj /p:CollectCoverage=true /p:CoverletOutput="./../../results/" - - run: - name: Autofac Extension Test - command: - dotnet test tests/dotenv.net.DependencyInjection.Autofac.Tests/dotenv.net.DependencyInjection.Autofac.Tests.csproj /p:CollectCoverage=true /p:CoverletOutput="./../../results/" /p:MergeWith="./../../results/coverage.json" - - run: - name: MS DI Test - command: - dotnet test tests/dotenv.net.DependencyInjection.Microsoft.Tests/dotenv.net.DependencyInjection.Microsoft.Tests.csproj /p:CollectCoverage=true /p:CoverletOutput="./../../results/" /p:MergeWith="./../../results/coverage.json" /p:CoverletOutputFormat="opencover" + dotnet test tests/dotenv.net.Tests/dotenv.net.Tests.csproj /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput="./../../results/" - run: name: Install Coveralls command: diff --git a/dotenv.net.sln b/dotenv.net.sln index af89a1d..f38f28e 100644 --- a/dotenv.net.sln +++ b/dotenv.net.sln @@ -7,14 +7,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotenv.net", "src\dotenv.ne EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotenv.net.Tests", "tests\dotenv.net.Tests\dotenv.net.Tests.csproj", "{A03D81EE-4F67-4521-932A-0B9B5BE50F72}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotenv.net.DependencyInjection.Microsoft", "src\dotenv.net.DependencyInjection.Microsoft\dotenv.net.DependencyInjection.Microsoft.csproj", "{5E1AD966-386D-4BB4-85FC-872E8EE2F211}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotenv.net.DependencyInjection.Autofac", "src\dotenv.net.DependencyInjection.Autofac\dotenv.net.DependencyInjection.Autofac.csproj", "{DE6358F8-CA1A-428F-8DE9-12A0A17DD0CF}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotenv.net.DependencyInjection.Autofac.Tests", "tests\dotenv.net.DependencyInjection.Autofac.Tests\dotenv.net.DependencyInjection.Autofac.Tests.csproj", "{5CD8426D-725D-4079-9EC5-46A285A7988B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotenv.net.DependencyInjection.Microsoft.Tests", "tests\dotenv.net.DependencyInjection.Microsoft.Tests\dotenv.net.DependencyInjection.Microsoft.Tests.csproj", "{9D856541-B72B-428D-8FE7-483FC1943C7F}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "tests", "{1CEC83AC-9E34-4320-AECE-F0F52FE70538}" EndProject Global @@ -31,29 +23,11 @@ Global {A03D81EE-4F67-4521-932A-0B9B5BE50F72}.Debug|Any CPU.Build.0 = Debug|Any CPU {A03D81EE-4F67-4521-932A-0B9B5BE50F72}.Release|Any CPU.ActiveCfg = Release|Any CPU {A03D81EE-4F67-4521-932A-0B9B5BE50F72}.Release|Any CPU.Build.0 = Release|Any CPU - {5E1AD966-386D-4BB4-85FC-872E8EE2F211}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5E1AD966-386D-4BB4-85FC-872E8EE2F211}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5E1AD966-386D-4BB4-85FC-872E8EE2F211}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5E1AD966-386D-4BB4-85FC-872E8EE2F211}.Release|Any CPU.Build.0 = Release|Any CPU - {DE6358F8-CA1A-428F-8DE9-12A0A17DD0CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DE6358F8-CA1A-428F-8DE9-12A0A17DD0CF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DE6358F8-CA1A-428F-8DE9-12A0A17DD0CF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DE6358F8-CA1A-428F-8DE9-12A0A17DD0CF}.Release|Any CPU.Build.0 = Release|Any CPU - {5CD8426D-725D-4079-9EC5-46A285A7988B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5CD8426D-725D-4079-9EC5-46A285A7988B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5CD8426D-725D-4079-9EC5-46A285A7988B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5CD8426D-725D-4079-9EC5-46A285A7988B}.Release|Any CPU.Build.0 = Release|Any CPU - {9D856541-B72B-428D-8FE7-483FC1943C7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9D856541-B72B-428D-8FE7-483FC1943C7F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9D856541-B72B-428D-8FE7-483FC1943C7F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9D856541-B72B-428D-8FE7-483FC1943C7F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {5CD8426D-725D-4079-9EC5-46A285A7988B} = {1CEC83AC-9E34-4320-AECE-F0F52FE70538} - {9D856541-B72B-428D-8FE7-483FC1943C7F} = {1CEC83AC-9E34-4320-AECE-F0F52FE70538} {A03D81EE-4F67-4521-932A-0B9B5BE50F72} = {1CEC83AC-9E34-4320-AECE-F0F52FE70538} EndGlobalSection EndGlobal diff --git a/src/dotenv.net.DependencyInjection.Autofac/AutofacExtensions.cs b/src/dotenv.net.DependencyInjection.Autofac/AutofacExtensions.cs deleted file mode 100644 index c7bece9..0000000 --- a/src/dotenv.net.DependencyInjection.Autofac/AutofacExtensions.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Text; -using Autofac; -using dotenv.net.DependencyInjection.Infrastructure; -using dotenv.net.Interfaces; -using dotenv.net.Utilities; - -namespace dotenv.net.DependencyInjection.Autofac -{ - public static class AutofacExtensions - { - /// - /// Add the environment variables using autofac - /// - /// The container builder instance - /// The env builder action - /// The container builder instance - /// If the container builder or action is null - public static ContainerBuilder AddEnv(this ContainerBuilder containerBuilder, - Action action) - { - if (containerBuilder == null) - { - throw new ArgumentNullException(nameof(containerBuilder)); - } - - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - var dotEnvOptionsBuilder = new DotEnvOptionsBuilder(); - action(dotEnvOptionsBuilder); - - var dotEnvOptions = dotEnvOptionsBuilder.Build(); - DotEnv.Config(dotEnvOptions); - - return containerBuilder; - } - - /// - /// Add the environment variables using autofac - /// - /// The container builder instance - /// The container builder instance - /// If the container builder is null - public static ContainerBuilder AddEnv(this ContainerBuilder containerBuilder) - { - if (containerBuilder == null) - { - throw new ArgumentNullException(nameof(containerBuilder)); - } - - AddEnv(containerBuilder, ac => - { - ac.AddEnvFile(".env") - .AddEncoding(Encoding.UTF8) - .AddThrowOnError(true) - .AddTrimOptions(true); - }); - - return containerBuilder; - } - - /// - /// Add autofac IoC for the env reader - /// - /// The container builder instance - /// The container builder instance - /// If the container builder is null - public static ContainerBuilder AddEnvReader(this ContainerBuilder containerBuilder) - { - if (containerBuilder == null) - { - throw new ArgumentNullException(nameof(containerBuilder)); - } - - containerBuilder.RegisterType().As(); - return containerBuilder; - } - } -} \ No newline at end of file diff --git a/src/dotenv.net.DependencyInjection.Autofac/README.md b/src/dotenv.net.DependencyInjection.Autofac/README.md deleted file mode 100644 index 72c9e2d..0000000 --- a/src/dotenv.net.DependencyInjection.Autofac/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# dotenv.net - -[![NuGet Badge](https://buildstats.info/nuget/dotenv.net.DependencyInjection.Autofac)](https://www.nuget.org/packages/dotenv.net.DependencyInjection.Autofac) - -## Usage - -### Conventional - -First install the library as a dependency in your application from nuget - -``` -Install-Package dotenv.net.DependencyInjection.Autofac -``` - -or - -``` -dotnet add package dotenv.net.DependencyInjection.Autofac -``` - -or for paket - -``` -paket add dotenv.net.DependencyInjection.Autofac -``` - -## Reading the Environment Variables - -If using with ASP.NET Core or any other system that uses `ContainerBuilder` for its dependency injection, in the `Startup.cs` file - -``` csharp -public void ConfigureContainer(ContainerBuilder containerBuilder) -{ - ... - - // configure dotenv - containerBuilder.AddEnv(builder => { - builder - .AddEnvFile("/custom/path/to/your/env/vars") - .AddThrowOnError(false) - .AddEncoding(Encoding.ASCII); - }); -} -``` - -## Injecting the EnvReader - -If using with ASP.NET Core or any other system that uses `ContainerBuilder` for its dependency injection, in the `Startup.cs` file - -``` csharp -public void ConfigureContainer(ContainerBuilder containerBuilder) -{ - ... - - // inject the env reader - containerBuilder.AddEnvReader(); -} -``` - -In a controller or service you can then inject the reader - -```csharp -... - -public class StuffController: ControllerBase(IEnvReader envReader) -{ - // you can use the reader now to get specific variables by key -} -``` \ No newline at end of file diff --git a/src/dotenv.net.DependencyInjection.Autofac/dotenv.net.DependencyInjection.Autofac.csproj b/src/dotenv.net.DependencyInjection.Autofac/dotenv.net.DependencyInjection.Autofac.csproj deleted file mode 100644 index dd7f3c6..0000000 --- a/src/dotenv.net.DependencyInjection.Autofac/dotenv.net.DependencyInjection.Autofac.csproj +++ /dev/null @@ -1,46 +0,0 @@ - - - - 3.0.0 - dotenv.net.DependencyInjection.Autofac - Winner-Timothy Bolorunduro - Adds DI support for values read from the environment for Autofac - https://github.com/bolorundurowb/dotenv.net - https://github.com/bolorundurowb/dotenv.net/blob/master/LICENSE - https://raw.githubusercontent.com/motdotla/dotenv/master/dotenv.png - https://github.com/bolorundurowb/dotenv.net - git - autofac, dotenv, net core - - initial release - 3.0.0 - true - Copyright 2021 - net5.0;net5.0-windows;netstandard1.6;netstandard2.0;netstandard2.1 - 3.0.0 - en-NG - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/dotenv.net.DependencyInjection.Microsoft/README.md b/src/dotenv.net.DependencyInjection.Microsoft/README.md deleted file mode 100644 index 479c390..0000000 --- a/src/dotenv.net.DependencyInjection.Microsoft/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# dotenv.net - -[![NuGet Badge](https://buildstats.info/nuget/dotenv.net.DependencyInjection.Microsoft)](https://www.nuget.org/packages/dotenv.net.DependencyInjection.Microsoft) - -## Usage - -### Conventional - -First install the library as a dependency in your application from nuget - -``` -Install-Package dotenv.net.DependencyInjection.Microsoft -``` - -or - -``` -dotnet add package dotenv.net.DependencyInjection.Microsoft -``` - -or for paket - -``` -paket add dotenv.net.DependencyInjection.Microsoft -``` - -## Reading the Environment Variables - -If using with ASP.NET Core or any other system that uses `IServiceCollection` for its dependency injection, in the `Startup.cs` file - -``` csharp -public void ConfigureServices(IServiceCollection services) -{ - ... - - // configure dotenv - services.AddEnv(builder => { - builder - .AddEnvFile("/custom/path/to/your/env/vars") - .AddThrowOnError(false) - .AddEncoding(Encoding.ASCII); - }); -} -``` - -## Injecting the EnvReader - -If using with ASP.NET Core or any other system that uses `IServiceCollection` for its dependency injection, in the `Startup.cs` file - -``` csharp -public void ConfigureServices(IServiceCollection services) -{ - ... - - // inject the env reader - services.AddEnvReader(); -} -``` - -In a controller or service you can then inject the reader - -```csharp -... - -public class StuffController: ControllerBase(IEnvReader envReader) -{ - // you can use the reader now to get specific variables by key -} -``` \ No newline at end of file diff --git a/src/dotenv.net.DependencyInjection.Microsoft/ServiceCollectionExtensions.cs b/src/dotenv.net.DependencyInjection.Microsoft/ServiceCollectionExtensions.cs deleted file mode 100644 index b36d941..0000000 --- a/src/dotenv.net.DependencyInjection.Microsoft/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Text; -using dotenv.net.DependencyInjection.Infrastructure; -using dotenv.net.Interfaces; -using dotenv.net.Utilities; -using Microsoft.Extensions.DependencyInjection; - -namespace dotenv.net.DependencyInjection.Microsoft -{ - /// - /// Service collection extensions - /// - public static class ServiceCollectionExtensions - { - /// - /// Add the environment vars using a service - /// - /// The service collection - /// The service collection - public static IServiceCollection AddEnv(this IServiceCollection services) - { - AddEnv(services, builder => - { - builder - .AddEnvFile(".env") - .AddEncoding(Encoding.UTF8) - .AddThrowOnError(true) - .AddTrimOptions(true); - }); - return services; - } - - /// - /// Add the environment vars using a service - /// - /// Service collection - /// The dot env options builder action - /// The service collection - /// If the service passed in is null - public static IServiceCollection AddEnv(this IServiceCollection services, - Action setupAction) - { - if (services == null) - { - throw new ArgumentNullException(nameof(services)); - } - - if (setupAction == null) - { - throw new ArgumentNullException(nameof(setupAction)); - } - - var dotEnvOptionsBuilder = new DotEnvOptionsBuilder(); - setupAction(dotEnvOptionsBuilder); - - var dotEnvOptions = dotEnvOptionsBuilder.Build(); - DotEnv.Config(dotEnvOptions); - - return services; - } - - /// - /// Use the env reader class ad the provider for reading environment variables - /// - /// Service collection - /// The service collection - /// If the service passed in is null - public static IServiceCollection AddEnvReader(this IServiceCollection services) - { - if (services == null) - { - throw new ArgumentNullException(nameof(services)); - } - - services.AddSingleton(); - return services; - } - } -} \ No newline at end of file diff --git a/src/dotenv.net.DependencyInjection.Microsoft/dotenv.net.DependencyInjection.Microsoft.csproj b/src/dotenv.net.DependencyInjection.Microsoft/dotenv.net.DependencyInjection.Microsoft.csproj deleted file mode 100644 index 30d70bc..0000000 --- a/src/dotenv.net.DependencyInjection.Microsoft/dotenv.net.DependencyInjection.Microsoft.csproj +++ /dev/null @@ -1,46 +0,0 @@ - - - - true - 3.0.0 - dotenv.net.DependencyInjection.Microsoft - Winner-Timothy Bolorunduro - Adds DI support for values read from the environment for Microsoft Service Collection - Copyright 2021 - https://github.com/bolorundurowb/dotenv.net - https://github.com/bolorundurowb/dotenv.net/blob/master/LICENSE - https://raw.githubusercontent.com/motdotla/dotenv/master/dotenv.png - https://github.com/bolorundurowb/dotenv.net - git - service collection, dotenv - - bump support - net5.0;net5.0-windows;netstandard1.6;netstandard2.0;netstandard2.1 - 3.0.0 - 3.0.0 - en-NG - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/dotenv.net/DependencyInjection/Infrastructure/DotEnvOptions.cs b/src/dotenv.net/DependencyInjection/Infrastructure/DotEnvOptions.cs deleted file mode 100644 index 6dc88bb..0000000 --- a/src/dotenv.net/DependencyInjection/Infrastructure/DotEnvOptions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Text; - -namespace dotenv.net.DependencyInjection.Infrastructure -{ - public class DotEnvOptions - { - /// - /// A value to state whether to throw an exception if the env file doesn't exist. The default is true. - /// - public bool ThrowOnError { get; set; } - - /// - /// The path to the env file. The default is ".env". - /// - public string EnvFile { get; set; } - - /// - /// The Encoding that the env file was created with. - /// - public Encoding Encoding { get; set; } - - /// - /// A value to state whether or not to trim whitespace from the values retrieved. - /// - public bool TrimValues { get; set; } - } -} \ No newline at end of file diff --git a/src/dotenv.net/DependencyInjection/Infrastructure/DotEnvOptionsBuilder.cs b/src/dotenv.net/DependencyInjection/Infrastructure/DotEnvOptionsBuilder.cs deleted file mode 100644 index e7865c5..0000000 --- a/src/dotenv.net/DependencyInjection/Infrastructure/DotEnvOptionsBuilder.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Text; - -namespace dotenv.net.DependencyInjection.Infrastructure -{ - public class DotEnvOptionsBuilder - { - private readonly DotEnvOptions _dotEnvOptions = new DotEnvOptions(); - - /// - /// Sets the environment file to be read - /// - /// The file path - /// The current options builder - public DotEnvOptionsBuilder AddEnvFile(string file) - { - _dotEnvOptions.EnvFile = file; - return this; - } - - /// - /// Sets the option to throw an eception if an error should occur - /// - /// The option to use - /// The current options builder - public DotEnvOptionsBuilder AddThrowOnError(bool throwOnError) - { - _dotEnvOptions.ThrowOnError = throwOnError; - return this; - } - - /// - /// Set the encoding to read the env file in. - /// - /// The encoding to use - /// The current options builder - public DotEnvOptionsBuilder AddEncoding(Encoding encoding) - { - _dotEnvOptions.Encoding = encoding; - return this; - } - - /// - /// Set the option to trim whitespace from values. - /// - /// The encoding to use - /// The current options builder - public DotEnvOptionsBuilder AddTrimOptions(bool trimValues) - { - _dotEnvOptions.TrimValues = trimValues; - return this; - } - - /// - /// - /// - /// The constructed - public DotEnvOptions Build() - { - return _dotEnvOptions; - } - } -} \ No newline at end of file diff --git a/src/dotenv.net/DotEnv.cs b/src/dotenv.net/DotEnv.cs index e48a375..ee17ef9 100644 --- a/src/dotenv.net/DotEnv.cs +++ b/src/dotenv.net/DotEnv.cs @@ -1,53 +1,27 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using dotenv.net.DependencyInjection.Infrastructure; +using dotenv.net.Utilities; namespace dotenv.net { - public class DotEnv + public static class DotEnv { - private const string DefaultEnvFileName = ".env"; - - private static void ConfigRunner(bool throwOnError, string filePath, Encoding encoding, bool trimValues) - { - var rawEnvRows = Reader.Read(filePath, throwOnError, encoding); - - if (rawEnvRows == ReadOnlySpan.Empty) - { - return; - } - - var processedEnvRows = Parser.Parse(rawEnvRows, trimValues); - foreach (var processedEnvRow in processedEnvRows) - { - Environment.SetEnvironmentVariable(processedEnvRow.Key, processedEnvRow.Value); - } - } - /// - /// Configure the environment variables from a .env file + /// Initialize the fluent configuration API /// - /// A value stating whether the application should throw an exception on unexpected data - /// An optional env file path, if not provided it defaults to the one in the same folder as the output exe or dll - /// The encoding with which the env file was created, It defaults to the platforms default - /// This determines whether not whitespace is trimmed from the values. It defaults to true - /// Thrown if the env file doesn't exist - public static void Config(bool throwOnError = true, string filePath = DefaultEnvFileName, - Encoding encoding = null, bool trimValues = true) + public static DotEnvOptions Config() { - ConfigRunner(throwOnError, filePath, encoding, trimValues); + return new DotEnvOptions(); } /// /// Configure the environment variables from a .env file /// /// Options on how to load the env file + [Obsolete] public static void Config(DotEnvOptions options) { - ConfigRunner(options.ThrowOnError, options.EnvFile, options.Encoding, options.TrimValues); + Helpers.ReadAndWrite(options); } /// @@ -55,51 +29,30 @@ public static void Config(DotEnvOptions options) /// /// The number of top-level directories to search; the default is 4 top-level directories. /// States whether or not the operation succeeded + [Obsolete] public static bool AutoConfig(int levelsToSearch = 4) { - var currentDirectory = new DirectoryInfo(AppContext.BaseDirectory); - - for (; - currentDirectory != null && levelsToSearch > 0; - levelsToSearch--, currentDirectory = currentDirectory.Parent) - { - foreach (var file in currentDirectory.GetFiles(DefaultEnvFileName, SearchOption.TopDirectoryOnly)) - { - Config(false, file.FullName); - return true; - } - } - - return false; + Helpers.ReadAndWrite(new DotEnvOptions(probeDirectoryDepth: levelsToSearch)); + return true; } /// - /// Load the values in the provided env file into the environment variables + /// Read and return the values in the provided env files /// - /// The path to the .env file to be read - /// The encoding that the env file was saved in - /// Determines if an exception should be thrown or swallowed - public static void Load(string envFilePath = DefaultEnvFileName, Encoding encoding = null, - bool ignoreExceptions = true) + /// The options required to configure the env loader + /// The key value pairs read from the env files + public static IDictionary Read(DotEnvOptions options) { - ConfigRunner(ignoreExceptions, envFilePath, encoding, true); + return Helpers.ReadAndReturn(options); } /// /// Load the values in the provided env files into the environment variables /// - /// The paths to the .env files to be read - /// The encoding that the env file was saved in - /// Determines if an exception should be thrown or swallowed - public static void Load(IEnumerable envFilePaths = null, Encoding encoding = null, - bool ignoreExceptions = true) + /// The options required to configure the env loader + public static void Load(DotEnvOptions options) { - envFilePaths ??= Enumerable.Empty(); - - foreach (var envFilePath in envFilePaths) - { - ConfigRunner(ignoreExceptions, envFilePath, encoding, true); - } + Helpers.ReadAndWrite(options); } } } \ No newline at end of file diff --git a/src/dotenv.net/DotEnvOptions.cs b/src/dotenv.net/DotEnvOptions.cs new file mode 100644 index 0000000..049194f --- /dev/null +++ b/src/dotenv.net/DotEnvOptions.cs @@ -0,0 +1,192 @@ +using System.Collections.Generic; +using System.Text; + +namespace dotenv.net +{ + public class DotEnvOptions + { + public const string DefaultEnvFileName = ".env"; + private const int DefaultProbeDepth = 4; + + /// + /// A value to state whether to throw or swallow exceptions. The default is true. + /// + public bool IgnoreExceptions { get; private set; } + + /// + /// The paths to the env files. The default is [.env] + /// + public IEnumerable EnvFilePaths { get; private set; } + + /// + /// The Encoding that the env file was created with. The default is UTF-8. + /// + public Encoding Encoding { get; private set; } + + /// + /// A value to state whether or not to trim whitespace from the values retrieved. The default is true. + /// + public bool TrimValues { get; private set; } + + /// + /// A value to state whether or not to override the env variable if it has been set. the default is true. + /// + public bool OverwriteExistingVars { get; private set; } + + /// + /// A value to state whether we traverse up the directory structure. The default is false. + /// + public bool ProbeForEnv { get; private set; } + + /// + /// A value to state how far up the directory structure we should search for env files. + /// + public int ProbeDirectoryDepth { get; private set; } + + /// + /// Default constructor for the dot env options + /// + /// A boolean + /// + /// + /// + /// + /// + /// + public DotEnvOptions(bool ignoreExceptions = true, IEnumerable envFilePaths = null, + Encoding encoding = null, bool trimValues = false, bool overwriteExistingVars = true, + bool probeForEnv = false, int probeDirectoryDepth = DefaultProbeDepth) + { + IgnoreExceptions = ignoreExceptions; + EnvFilePaths = envFilePaths ?? new[] {DefaultEnvFileName}; + Encoding = encoding ?? Encoding.UTF8; + TrimValues = trimValues; + OverwriteExistingVars = overwriteExistingVars; + ProbeForEnv = probeForEnv; + ProbeDirectoryDepth = probeDirectoryDepth; + } + + /// + /// Ignore exceptions thrown + /// + /// configured dot env options + public DotEnvOptions WithExceptions() + { + IgnoreExceptions = false; + return this; + } + + /// + /// Throw exceptions when triggered + /// + /// configured dot env options + public DotEnvOptions WithoutExceptions() + { + IgnoreExceptions = true; + return this; + } + + /// + /// Search up the directory for a .env file. By default searches up 4 directories. + /// + /// configured dot env options + public DotEnvOptions WithProbeForEnv(int probeDepth = DefaultProbeDepth) + { + ProbeForEnv = true; + ProbeDirectoryDepth = probeDepth; + return this; + } + + /// + /// Rely on the provided env files. By default is false. + /// + /// configured dot env options + public DotEnvOptions WithoutProbeForEnv() + { + ProbeForEnv = false; + ProbeDirectoryDepth = DefaultProbeDepth; + return this; + } + + /// + /// Overwrite an environment variable even if it has been set + /// + /// configured dot env options + public DotEnvOptions WithOverwriteExistingVars() + { + OverwriteExistingVars = true; + return this; + } + + /// + /// Only write an environment variable if it hasnt been et + /// + /// configured dot env options + public DotEnvOptions WithoutOverwriteExistingVars() + { + OverwriteExistingVars = false; + return this; + } + + /// + /// Trim whitespace from the values read + /// + /// configured dot env options + public DotEnvOptions WithTrimValues() + { + TrimValues = true; + return this; + } + + /// + /// Leave read values as is + /// + /// configured dot env options + public DotEnvOptions WithoutTrimValues() + { + TrimValues = false; + return this; + } + + /// + /// Change the encoding for reading the env files + /// + /// configured dot env options + public DotEnvOptions WithEncoding(Encoding encoding) + { + Encoding = encoding ?? Encoding.UTF8; + return this; + } + + /// + /// Revert to the default encoding for reading the env files. The default encoding is UTF-8 + /// + /// configured dot env options + public DotEnvOptions WithDefaultEncoding() + { + Encoding = Encoding.UTF8; + return this; + } + + /// + /// Set the env files to be read, if none is provided, we revert to the default '.env' + /// + /// configured dot env options + public DotEnvOptions WithEnvFiles(params string[] envFilePaths) + { + EnvFilePaths = envFilePaths?.Length == 0 ? new []{DefaultEnvFileName} : envFilePaths; + return this; + } + + /// + /// Return the values in the env files without writing to the environment + /// + /// configured dot env options + public IDictionary Read() => DotEnv.Read(this); + + /// + /// Read the env files and write to the syetm environment variables + /// + public void Load() => DotEnv.Load(this); + } +} \ No newline at end of file diff --git a/src/dotenv.net/Interfaces/IEnvReader.cs b/src/dotenv.net/Interfaces/IEnvReader.cs deleted file mode 100644 index 856e81b..0000000 --- a/src/dotenv.net/Interfaces/IEnvReader.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace dotenv.net.Interfaces -{ - public interface IEnvReader - { - string GetStringValue(string key); - - int GetIntValue(string key); - - double GetDoubleValue(string key); - - decimal GetDecimalValue(string key); - - bool GetBooleanValue(string key); - - bool TryGetStringValue(string key, out string value); - - bool TryGetIntValue(string key, out int value); - - bool TryGetDoubleValue(string key, out double value); - - bool TryGetDecimalValue(string key, out decimal value); - - bool TryGetBooleanValue(string key, out bool value); - } -} \ No newline at end of file diff --git a/src/dotenv.net/Reader.cs b/src/dotenv.net/Reader.cs index 116a790..726b8fa 100644 --- a/src/dotenv.net/Reader.cs +++ b/src/dotenv.net/Reader.cs @@ -6,29 +6,37 @@ namespace dotenv.net { internal static class Reader { - internal static ReadOnlySpan Read(string filePath, bool throwOnError, Encoding encoding) + internal static ReadOnlySpan Read(string envFilePath, bool ignoreExceptions, Encoding encoding) { - if (string.IsNullOrWhiteSpace(filePath)) + try { - throw new ArgumentException("The file path cannot be null, empty or whitespace.", nameof(filePath)); - } + if (string.IsNullOrWhiteSpace(envFilePath)) + { + throw new ArgumentException("The file path cannot be null, empty or whitespace.", nameof(envFilePath)); + } - // if configured to throw errors then throw otherwise return - if (!File.Exists(filePath)) - { - if (throwOnError) + // if configured to throw errors then throw otherwise return + if (!File.Exists(envFilePath)) { - throw new FileNotFoundException($"An environment file with path \"{filePath}\" does not exist."); + throw new FileNotFoundException( + $"An environment file with path \"{envFilePath}\" does not exist."); } - return ReadOnlySpan.Empty; - } + // default to UTF8 if encoding is not provided + encoding ??= Encoding.UTF8; - // default to UTF8 if encoding is not provided - encoding ??= Encoding.UTF8; + // read all lines from the env file + return new ReadOnlySpan(File.ReadAllLines(envFilePath, encoding)); + } + catch (Exception) + { + if (ignoreExceptions) + { + return ReadOnlySpan.Empty; + } - // read all lines from the env file - return new ReadOnlySpan(File.ReadAllLines(filePath, encoding)); + throw; + } } } } \ No newline at end of file diff --git a/src/dotenv.net/Utilities/EnvReader.cs b/src/dotenv.net/Utilities/EnvReader.cs index abb846b..a9cad99 100644 --- a/src/dotenv.net/Utilities/EnvReader.cs +++ b/src/dotenv.net/Utilities/EnvReader.cs @@ -1,9 +1,11 @@ using System; -using dotenv.net.Interfaces; namespace dotenv.net.Utilities { - public class EnvReader : IEnvReader + /// + /// Holds reader helper methods + /// + public static class EnvReader { /// /// Retrieve a string value from the current environment @@ -11,7 +13,7 @@ public class EnvReader : IEnvReader /// The key to retrieve the value via /// A string representing the value /// When the value could not be found - public string GetStringValue(string key) + public static string GetStringValue(string key) { if (TryGetStringValue(key, out var value)) { @@ -27,7 +29,7 @@ public string GetStringValue(string key) /// The key to retrieve the value via /// An integer representing the value /// When the value could not be found or is not an integer - public int GetIntValue(string key) + public static int GetIntValue(string key) { if (TryGetIntValue(key, out var value)) { @@ -43,7 +45,7 @@ public int GetIntValue(string key) /// The key to retrieve the value via /// A double representing the value /// When the value could not be found or is not a valid double - public double GetDoubleValue(string key) + public static double GetDoubleValue(string key) { if (TryGetDoubleValue(key, out var value)) { @@ -59,7 +61,7 @@ public double GetDoubleValue(string key) /// The key to retrieve the value via /// A decimal representing the value /// When the value could not be found or is not a valid decimal - public decimal GetDecimalValue(string key) + public static decimal GetDecimalValue(string key) { if (TryGetDecimalValue(key, out var value)) { @@ -73,9 +75,9 @@ public decimal GetDecimalValue(string key) /// Retrieve a boolean value from the current environment /// /// The key to retrieve the value via - /// A boolran representing the value + /// A boolean representing the value /// When the value could not be found or is not a valid bool - public bool GetBooleanValue(string key) + public static bool GetBooleanValue(string key) { if (TryGetBooleanValue(key, out var value)) { @@ -91,7 +93,7 @@ public bool GetBooleanValue(string key) /// The key to retrieve the value via /// The string value retrieved or null /// A value representing the retrieval success status - public bool TryGetStringValue(string key, out string value) + public static bool TryGetStringValue(string key, out string value) { var retrievedValue = Environment.GetEnvironmentVariable(key); @@ -111,7 +113,7 @@ public bool TryGetStringValue(string key, out string value) /// The key to retrieve the value via /// The int value retrieved or null /// A value representing the retrieval success status - public bool TryGetIntValue(string key, out int value) + public static bool TryGetIntValue(string key, out int value) { var retrievedValue = Environment.GetEnvironmentVariable(key); @@ -130,7 +132,7 @@ public bool TryGetIntValue(string key, out int value) /// The key to retrieve the value via /// The double value retrieved or null /// A value representing the retrieval success status - public bool TryGetDoubleValue(string key, out double value) + public static bool TryGetDoubleValue(string key, out double value) { var retrievedValue = Environment.GetEnvironmentVariable(key); @@ -149,7 +151,7 @@ public bool TryGetDoubleValue(string key, out double value) /// The key to retrieve the value via /// The decimal value retrieved or null /// A value representing the retrieval success status - public bool TryGetDecimalValue(string key, out decimal value) + public static bool TryGetDecimalValue(string key, out decimal value) { var retrievedValue = Environment.GetEnvironmentVariable(key); @@ -168,7 +170,7 @@ public bool TryGetDecimalValue(string key, out decimal value) /// The key to retrieve the value via /// The boolean value retrieved or null /// A value representing the retrieval success status - public bool TryGetBooleanValue(string key, out bool value) + public static bool TryGetBooleanValue(string key, out bool value) { var retrievedValue = Environment.GetEnvironmentVariable(key); @@ -180,5 +182,16 @@ public bool TryGetBooleanValue(string key, out bool value) value = false; return false; } + + /// + /// Determine if an environment key has a set value or not + /// + /// The key to retrieve the value via + /// A value determining if a value is set or not + public static bool HasValue(string key) + { + var retrievedValue = Environment.GetEnvironmentVariable(key); + return !string.IsNullOrEmpty(retrievedValue); + } } } \ No newline at end of file diff --git a/src/dotenv.net/Utilities/Helpers.cs b/src/dotenv.net/Utilities/Helpers.cs new file mode 100644 index 0000000..820d7b2 --- /dev/null +++ b/src/dotenv.net/Utilities/Helpers.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace dotenv.net.Utilities +{ + internal static class Helpers + { + private static ReadOnlySpan> ReadAndParse(string envFilePath, + bool ignoreExceptions, Encoding encoding, bool trimValues) + { + var rawEnvRows = Reader.Read(envFilePath, ignoreExceptions, encoding); + + if (rawEnvRows == ReadOnlySpan.Empty) + { + return ReadOnlySpan>.Empty; + } + + return Parser.Parse(rawEnvRows, trimValues); + } + + internal static IDictionary ReadAndReturn(DotEnvOptions options) + { + var response = new Dictionary(); + var envFilePaths = options.ProbeForEnv + ? new[] {GetProbedEnvPath(options.ProbeDirectoryDepth)} + : options.EnvFilePaths; + + foreach (var envFilePath in envFilePaths) + { + var envRows = ReadAndParse(envFilePath, options.IgnoreExceptions, options.Encoding, + options.TrimValues); + foreach (var envRow in envRows) + { + if (response.ContainsKey(envRow.Key)) + { + response[envRow.Key] = envRow.Value; + } + else + { + response.Add(envRow.Key, envRow.Value); + } + } + } + + return response; + } + + internal static void ReadAndWrite(DotEnvOptions options) + { + var envVars = ReadAndReturn(options); + + foreach (var envVar in envVars) + { + if (options.OverwriteExistingVars) + { + Environment.SetEnvironmentVariable(envVar.Key, envVar.Value); + } + else if (!EnvReader.HasValue(envVar.Key)) + { + Environment.SetEnvironmentVariable(envVar.Key, envVar.Value); + } + } + } + + private static string GetProbedEnvPath(int levelsToSearch) + { + var currentDirectory = new DirectoryInfo(AppContext.BaseDirectory); + + for (; + currentDirectory != null && levelsToSearch > 0; + levelsToSearch--, currentDirectory = currentDirectory.Parent) + { + foreach (var file in currentDirectory.GetFiles(DotEnvOptions.DefaultEnvFileName, + SearchOption.TopDirectoryOnly)) + { + return file.FullName; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/tests/dotenv.net.DependencyInjection.Autofac.Tests/.env b/tests/dotenv.net.DependencyInjection.Autofac.Tests/.env deleted file mode 100644 index 4307815..0000000 --- a/tests/dotenv.net.DependencyInjection.Autofac.Tests/.env +++ /dev/null @@ -1,3 +0,0 @@ -hello=world -# comment -strongestavenger = \ No newline at end of file diff --git a/tests/dotenv.net.DependencyInjection.Autofac.Tests/AutofacExtensionTests.cs b/tests/dotenv.net.DependencyInjection.Autofac.Tests/AutofacExtensionTests.cs deleted file mode 100644 index b4f9be7..0000000 --- a/tests/dotenv.net.DependencyInjection.Autofac.Tests/AutofacExtensionTests.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using Autofac; -using dotenv.net.Interfaces; -using dotenv.net.Utilities; -using FluentAssertions; -using Xunit; - -namespace dotenv.net.DependencyInjection.Autofac.Tests -{ - public class AutofacExtensionTests - { - [Fact] - public void ShouldThrowWhenContainerBuilderIsNull() - { - Action action = () => AutofacExtensions.AddEnv(null, builder => { }); - action.Should() - .ThrowExactly(); - } - - [Fact] - public void ShouldThrowWhenSetupActionIsNull() - { - var containerBuilder = new ContainerBuilder(); - Action action = () => containerBuilder.AddEnv(null); - action.Should() - .ThrowExactly(); - } - - [Fact] - public void AddsEnvironmentVariablesIfADefaultEnvFileExists() - { - var builder = new ContainerBuilder(); - Action action = () => builder.AddEnv(); - - action.Should() - .NotThrow(); - Environment.GetEnvironmentVariable("hello").Should().Be("world"); - } - - [Fact] - public void ShouldThrowWhenContainerBuilderIsNotProvided() - { - Action action = () => AutofacExtensions.AddEnvReader(null); - action.Should() - .ThrowExactly(); - } - - [Fact] - public void ShouldInjectEnvReaderWhenContainerBuilderServicesIsProvided() - { - var containerBuilder = new ContainerBuilder(); - containerBuilder.AddEnvReader(); - var provider = containerBuilder.Build(); - - using (var scope = provider.BeginLifetimeScope()) - { - object service = null; - Action action = () => service = scope.Resolve(); - - action.Should() - .NotThrow(); - service.Should() - .NotBeNull(); - service.Should() - .BeOfType(); - } - } - } -} \ No newline at end of file diff --git a/tests/dotenv.net.DependencyInjection.Autofac.Tests/dotenv.net.DependencyInjection.Autofac.Tests.csproj b/tests/dotenv.net.DependencyInjection.Autofac.Tests/dotenv.net.DependencyInjection.Autofac.Tests.csproj deleted file mode 100644 index eff3e32..0000000 --- a/tests/dotenv.net.DependencyInjection.Autofac.Tests/dotenv.net.DependencyInjection.Autofac.Tests.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - Exe - net5.0 - - - - runtime; build; native; contentfiles; analyzers - all - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - PreserveNewest - - - diff --git a/tests/dotenv.net.DependencyInjection.Microsoft.Tests/.env b/tests/dotenv.net.DependencyInjection.Microsoft.Tests/.env deleted file mode 100644 index 4307815..0000000 --- a/tests/dotenv.net.DependencyInjection.Microsoft.Tests/.env +++ /dev/null @@ -1,3 +0,0 @@ -hello=world -# comment -strongestavenger = \ No newline at end of file diff --git a/tests/dotenv.net.DependencyInjection.Microsoft.Tests/ServiceCollectionExtensionsTests.cs b/tests/dotenv.net.DependencyInjection.Microsoft.Tests/ServiceCollectionExtensionsTests.cs deleted file mode 100644 index 966995a..0000000 --- a/tests/dotenv.net.DependencyInjection.Microsoft.Tests/ServiceCollectionExtensionsTests.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Text; -using dotenv.net.Interfaces; -using dotenv.net.Utilities; -using FluentAssertions; -using Microsoft.Extensions.DependencyInjection; -using Xunit; - -namespace dotenv.net.DependencyInjection.Microsoft.Tests -{ - public class ServiceCollectionExtensionsTests - { - [Fact] - public void ShouldThrowWhenServicesAreNull() - { - Action action = () => ServiceCollectionExtensions.AddEnv(null, builder => { }); - action.Should() - .ThrowExactly(); - } - - [Fact] - public void ShouldThrowWhenSetupActionIsNull() - { - var services = new ServiceCollection(); - Action action = () => services.AddEnv(null); - action.Should() - .ThrowExactly(); - } - - [Fact] - public void ShouldWorkWhenSetupActionIsValid() - { - var services = new ServiceCollection(); - Action action = () => services.AddEnv(options => - { - options.AddEnvFile(".env"); - options.AddEncoding(Encoding.UTF8); - options.AddThrowOnError(false); - }); - action.Should() - .NotThrow(); - } - - [Fact] - public void ShouldWorkWithAutomatedOptions() - { - var services = new ServiceCollection(); - Action action = () => services.AddEnv(); - action.Should() - .NotThrow(); - } - - [Fact] - public void ShouldThrowWhenServicesAreNotProvided() - { - Action action = () => ServiceCollectionExtensions.AddEnvReader(null); - action.Should() - .ThrowExactly(); - } - - [Fact] - public void ShouldInjectEnvReaderWhenServicesAreProvided() - { - var services = new ServiceCollection(); - services.AddEnvReader(); - var provider = services.BuildServiceProvider(); - - object service = null; - Action action = () => service = (EnvReader) provider.GetService(typeof(IEnvReader)); - - action.Should() - .NotThrow(); - service.Should() - .NotBeNull(); - service.Should() - .BeOfType(); - } - } -} \ No newline at end of file diff --git a/tests/dotenv.net.DependencyInjection.Microsoft.Tests/dotenv.net.DependencyInjection.Microsoft.Tests.csproj b/tests/dotenv.net.DependencyInjection.Microsoft.Tests/dotenv.net.DependencyInjection.Microsoft.Tests.csproj deleted file mode 100644 index 4c4b386..0000000 --- a/tests/dotenv.net.DependencyInjection.Microsoft.Tests/dotenv.net.DependencyInjection.Microsoft.Tests.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - Exe - net5.0 - - - - runtime; build; native; contentfiles; analyzers - all - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - PreserveNewest - - - diff --git a/tests/dotenv.net.Tests/.env b/tests/dotenv.net.Tests/.env index 4307815..367b05c 100644 --- a/tests/dotenv.net.Tests/.env +++ b/tests/dotenv.net.Tests/.env @@ -1,3 +1,4 @@ hello=world # comment -strongestavenger = \ No newline at end of file +strongestavenger = +uniquekey=kjdjkd \ No newline at end of file diff --git a/tests/dotenv.net.Tests/DependencyInjection/Infrastructure/DotEnvOptionsBuilder.Tests.cs b/tests/dotenv.net.Tests/DependencyInjection/Infrastructure/DotEnvOptionsBuilder.Tests.cs deleted file mode 100644 index 9e73b58..0000000 --- a/tests/dotenv.net.Tests/DependencyInjection/Infrastructure/DotEnvOptionsBuilder.Tests.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Text; -using dotenv.net.DependencyInjection.Infrastructure; -using FluentAssertions; -using Xunit; - -namespace dotenv.net.Tests.DependencyInjection.Infrastructure -{ - public class DotEnvOptionsBuilderTests - { - [Fact] - public void ShouldGenerateSpecified() - { - var fileName = "file.env"; - var dotEnvOptionsBuilder = new DotEnvOptionsBuilder(); - - var dotEnvOptions = dotEnvOptionsBuilder - .AddEncoding(Encoding.UTF32) - .AddEnvFile(fileName) - .AddThrowOnError(false) - .AddTrimOptions(true) - .Build(); - - dotEnvOptions.Encoding.Should().Be(Encoding.UTF32); - dotEnvOptions.EnvFile.Should().Be(fileName); - dotEnvOptions.TrimValues.Should().BeTrue(); - dotEnvOptions.ThrowOnError.Should().BeFalse(); - } - } -} \ No newline at end of file diff --git a/tests/dotenv.net.Tests/DotEnv.Tests.cs b/tests/dotenv.net.Tests/DotEnv.Tests.cs index ea22003..dc1e1b2 100644 --- a/tests/dotenv.net.Tests/DotEnv.Tests.cs +++ b/tests/dotenv.net.Tests/DotEnv.Tests.cs @@ -1,7 +1,7 @@ using System; using System.IO; using System.Text; -using dotenv.net.DependencyInjection.Infrastructure; +using dotenv.net.Utilities; using FluentAssertions; using Xunit; @@ -16,101 +16,90 @@ public class DotEnvTests private const string QuotationsEnvFileName = "quotations.env"; [Fact] - public void ShouldThrowExceptionWhenFileNameEmptyOrNull() + public void Config_ShouldInitializeEnvOptions_WithDefaultOptions() { - Action action = () => DotEnv.Config(true, null); - action.Should().ThrowExactly() - .WithMessage( - "The file path cannot be null, empty or whitespace. (Parameter 'filePath')"); - } - - [Fact] - public void ThrowsExceptionWithNonExistentEnvFileWhenThrowErrorIsTrue() - { - Action action = () => DotEnv.Config(true, NonExistentEnvFileName); - action.Should().ThrowExactly() - .WithMessage($"An environment file with path \"{NonExistentEnvFileName}\" does not exist."); - } + var config = DotEnv.Config(); - [Fact] - public void DoesNotThrowExceptionWithNonExistentEnvFileWhenThrowErrorIsFalse() - { - var dotEnvOptions = new DotEnvOptions - { - Encoding = Encoding.UTF8, - ThrowOnError = false, - EnvFile = NonExistentEnvFileName, - TrimValues = true - }; - Action action = () => DotEnv.Config(dotEnvOptions); - action.Should().NotThrow(); + config.Encoding + .Should() + .Be(Encoding.UTF8); } [Fact] - public void AddsEnvironmentVariablesIfADefaultEnvFileExists() + public void Config_ShouldNotLoadEnv_WithDefaultOptions_AsThereIsNoEnvFile() { - Action action = () => DotEnv.Config(); - action.Should().NotThrow(); + var action = new Action(() => DotEnv.Config(new DotEnvOptions(ignoreExceptions: false))); - Environment.GetEnvironmentVariable("hello").Should().Be("world"); + action.Should() + .ThrowExactly(); } [Fact] - public void AddsEnvironmentVariablesAndSetsValueAsNullIfNoneExists() + public void Config_ShouldLoadEnv_WithProbeEnvOptions() { - Action action = () => DotEnv.Config(); - action.Should().NotThrow(); + DotEnv.Config(new DotEnvOptions(probeForEnv: true)); - Environment.GetEnvironmentVariable("strongestavenger").Should().Be(null); + EnvReader.GetStringValue("hello") + .Should() + .Be("world"); } [Fact] - public void AllowsEnvFilePathToBeSpecified() + public void AutoConfig_ShouldLocateAndLoadEnv() { - Action action = () => DotEnv.Config(true, ValuesAndCommentsEnvFileName); - action.Should().NotThrow(); + var success = DotEnv.AutoConfig(); - Environment.GetEnvironmentVariable("me").Should().Be("winner"); + success.Should().BeTrue(); + EnvReader.GetStringValue("uniquekey") + .Should() + .Be("kjdjkd"); } [Fact] - public void ShouldReturnUntrimmedValuesWhenTrimIsFalse() + public void Read_Should_ReturnTheReadValues() { - DotEnv.Config(true, WhitespacesEnvFileName, Encoding.UTF8, false); - - Environment.GetEnvironmentVariable("DB_CONNECTION").Should().Be("mysql "); - Environment.GetEnvironmentVariable("DB_HOST").Should().Be("127.0.0.1"); - Environment.GetEnvironmentVariable("DB_PORT").Should().Be(" 3306"); - Environment.GetEnvironmentVariable("DB_DATABASE").Should().Be("laravel"); + var values = + DotEnv.Read(new DotEnvOptions(trimValues: true, envFilePaths: new[] {WhitespacesEnvFileName})); + + values.Count + .Should() + .BeGreaterThan(0); + values["DB_CONNECTION"] + .Should() + .Be("mysql"); + values["DB_PORT"] + .Should() + .Be("3306"); + values["DB_HOST"] + .Should() + .Be("127.0.0.1"); + values["DB_DATABASE"] + .Should() + .Be("laravel"); + values["IS_PRESENT"] + .Should() + .Be("true"); } [Fact] - public void ShouldReturnTrimmedValuesWhenTrimIsTrue() + public void Read_Should_ThrowAnException_WithEmptyFileNameAndConfig() { - DotEnv.Config(true, WhitespacesCopyEnvFileName, Encoding.UTF8, true); + var action = new Action(() => + DotEnv.Read(new DotEnvOptions(ignoreExceptions: false, envFilePaths: new[] {string.Empty}))); - Environment.GetEnvironmentVariable("B_CONNECTION").Should().Be("mysql"); - Environment.GetEnvironmentVariable("B_HOST").Should().Be("127.0.0.1"); - Environment.GetEnvironmentVariable("B_PORT").Should().Be("3306"); - Environment.GetEnvironmentVariable("B_DATABASE").Should().Be("laravel"); + action.Should() + .ThrowExactly(); } [Fact] - public void ShouldReturnValidValuesWhenValuesAreQuoted() + public void Read_Should_IgnoreFieldsThatHaveExistingValues_WithConfig() { - DotEnv.Config(true, QuotationsEnvFileName, Encoding.UTF8); - - Environment.GetEnvironmentVariable("SINGLE").Should().Be("single"); - Environment.GetEnvironmentVariable("DOUBLE").Should().Be("double"); - } + Environment.SetEnvironmentVariable("me", "whoIam"); + DotEnv.Load(new DotEnvOptions(overwriteExistingVars: false, envFilePaths: new[] {ValuesAndCommentsEnvFileName})); - [Fact] - public void ShouldReturnValidValuesWithAutoConfig() - { - var success = DotEnv.AutoConfig(); - - success.Should().BeTrue(); - Environment.GetEnvironmentVariable("hello").Should().Be("world"); + EnvReader.GetStringValue("me") + .Should() + .Be("whoIam"); } } } \ No newline at end of file diff --git a/tests/dotenv.net.Tests/DotEnvOptions.Tests.cs b/tests/dotenv.net.Tests/DotEnvOptions.Tests.cs new file mode 100644 index 0000000..1c17d48 --- /dev/null +++ b/tests/dotenv.net.Tests/DotEnvOptions.Tests.cs @@ -0,0 +1,413 @@ +using System; +using System.Text; +using FluentAssertions; +using Xunit; + +namespace dotenv.net.Tests +{ + public class DotEnvOptionsTests + { + [Fact] + public void Constructor_ShouldInitialize_WithDefaults() + { + var options = new DotEnvOptions(); + + options.IgnoreExceptions + .Should() + .BeTrue(); + options.Encoding + .Should() + .Be(Encoding.UTF8); + options.TrimValues + .Should() + .BeFalse(); + options.OverwriteExistingVars + .Should() + .BeTrue(); + options.ProbeForEnv + .Should() + .BeFalse(); + options.ProbeDirectoryDepth + .Should() + .Be(4); + options.EnvFilePaths + .Should() + .Contain(".env"); + } + + [Fact] + public void Constructor_ShouldInitialize_WithSpecifiedValues() + { + var filePaths = new[] {"test.env"}; + var options = new DotEnvOptions(encoding: Encoding.UTF32, trimValues: false, probeForEnv: true, + probeDirectoryDepth: 5, overwriteExistingVars: false, ignoreExceptions: false, envFilePaths: filePaths); + + options.IgnoreExceptions + .Should() + .BeFalse(); + options.Encoding + .Should() + .Be(Encoding.UTF32); + options.TrimValues + .Should() + .BeFalse(); + options.OverwriteExistingVars + .Should() + .BeFalse(); + options.ProbeForEnv + .Should() + .BeTrue(); + options.ProbeDirectoryDepth + .Should() + .Be(5); + options.EnvFilePaths + .Should() + .Contain("test.env"); + } + + [Fact] + public void ShouldGenerateOptions_WithExceptions() + { + var options = new DotEnvOptions() + .WithExceptions(); + + options.IgnoreExceptions + .Should() + .BeFalse(); + options.Encoding + .Should() + .Be(Encoding.UTF8); + options.TrimValues + .Should() + .BeFalse(); + options.OverwriteExistingVars + .Should() + .BeTrue(); + options.ProbeForEnv + .Should() + .BeFalse(); + options.ProbeDirectoryDepth + .Should() + .Be(4); + options.EnvFilePaths + .Should() + .Contain(".env"); + } + + [Fact] + public void ShouldGenerateOptions_WithoutExceptions() + { + var options = new DotEnvOptions() + .WithoutExceptions(); + + options.IgnoreExceptions + .Should() + .BeTrue(); + options.Encoding + .Should() + .Be(Encoding.UTF8); + options.TrimValues + .Should() + .BeFalse(); + options.OverwriteExistingVars + .Should() + .BeTrue(); + options.ProbeForEnv + .Should() + .BeFalse(); + options.ProbeDirectoryDepth + .Should() + .Be(4); + options.EnvFilePaths + .Should() + .Contain(".env"); + } + + [Fact] + public void ShouldGenerateOptions_WithProbeForEnv() + { + var options = new DotEnvOptions() + .WithProbeForEnv(7); + + options.IgnoreExceptions + .Should() + .BeTrue(); + options.Encoding + .Should() + .Be(Encoding.UTF8); + options.TrimValues + .Should() + .BeFalse(); + options.OverwriteExistingVars + .Should() + .BeTrue(); + options.ProbeForEnv + .Should() + .BeTrue(); + options.ProbeDirectoryDepth + .Should() + .Be(7); + options.EnvFilePaths + .Should() + .Contain(".env"); + } + + [Fact] + public void ShouldGenerateOptions_WithoutProbeForEnv() + { + var options = new DotEnvOptions() + .WithoutProbeForEnv(); + + options.IgnoreExceptions + .Should() + .BeTrue(); + options.Encoding + .Should() + .Be(Encoding.UTF8); + options.TrimValues + .Should() + .BeFalse(); + options.OverwriteExistingVars + .Should() + .BeTrue(); + options.ProbeForEnv + .Should() + .BeFalse(); + options.ProbeDirectoryDepth + .Should() + .Be(4); + options.EnvFilePaths + .Should() + .Contain(".env"); + } + + [Fact] + public void ShouldGenerateOptions_WithOverwriteExistingVars() + { + var options = new DotEnvOptions() + .WithOverwriteExistingVars(); + + options.IgnoreExceptions + .Should() + .BeTrue(); + options.Encoding + .Should() + .Be(Encoding.UTF8); + options.TrimValues + .Should() + .BeFalse(); + options.OverwriteExistingVars + .Should() + .BeTrue(); + options.ProbeForEnv + .Should() + .BeFalse(); + options.ProbeDirectoryDepth + .Should() + .Be(4); + options.EnvFilePaths + .Should() + .Contain(".env"); + } + + [Fact] + public void ShouldGenerateOptions_WithoutOverwriteExistingVars() + { + var options = new DotEnvOptions() + .WithoutOverwriteExistingVars(); + + options.IgnoreExceptions + .Should() + .BeTrue(); + options.Encoding + .Should() + .Be(Encoding.UTF8); + options.TrimValues + .Should() + .BeFalse(); + options.OverwriteExistingVars + .Should() + .BeFalse(); + options.ProbeForEnv + .Should() + .BeFalse(); + options.ProbeDirectoryDepth + .Should() + .Be(4); + options.EnvFilePaths + .Should() + .Contain(".env"); + } + + [Fact] + public void ShouldGenerateOptions_WithTrimValues() + { + var options = new DotEnvOptions() + .WithTrimValues(); + + options.IgnoreExceptions + .Should() + .BeTrue(); + options.Encoding + .Should() + .Be(Encoding.UTF8); + options.TrimValues + .Should() + .BeTrue(); + options.OverwriteExistingVars + .Should() + .BeTrue(); + options.ProbeForEnv + .Should() + .BeFalse(); + options.ProbeDirectoryDepth + .Should() + .Be(4); + options.EnvFilePaths + .Should() + .Contain(".env"); + } + + [Fact] + public void ShouldGenerateOptions_WithoutTrimValues() + { + var options = new DotEnvOptions() + .WithoutTrimValues(); + + options.IgnoreExceptions + .Should() + .BeTrue(); + options.Encoding + .Should() + .Be(Encoding.UTF8); + options.TrimValues + .Should() + .BeFalse(); + options.OverwriteExistingVars + .Should() + .BeTrue(); + options.ProbeForEnv + .Should() + .BeFalse(); + options.ProbeDirectoryDepth + .Should() + .Be(4); + options.EnvFilePaths + .Should() + .Contain(".env"); + } + + [Fact] + public void ShouldGenerateOptions_WithEncoding() + { + var options = new DotEnvOptions() + .WithEncoding(Encoding.Latin1); + + options.IgnoreExceptions + .Should() + .BeTrue(); + options.Encoding + .Should() + .Be(Encoding.Latin1); + options.TrimValues + .Should() + .BeFalse(); + options.OverwriteExistingVars + .Should() + .BeTrue(); + options.ProbeForEnv + .Should() + .BeFalse(); + options.ProbeDirectoryDepth + .Should() + .Be(4); + options.EnvFilePaths + .Should() + .Contain(".env"); + } + + [Fact] + public void ShouldGenerateOptions_WithDefaultEncoding() + { + var options = new DotEnvOptions() + .WithDefaultEncoding(); + + options.IgnoreExceptions + .Should() + .BeTrue(); + options.Encoding + .Should() + .Be(Encoding.UTF8); + options.TrimValues + .Should() + .BeFalse(); + options.OverwriteExistingVars + .Should() + .BeTrue(); + options.ProbeForEnv + .Should() + .BeFalse(); + options.ProbeDirectoryDepth + .Should() + .Be(4); + options.EnvFilePaths + .Should() + .Contain(".env"); + } + + [Fact] + public void ShouldGenerateOptions_WithEnvFiles() + { + var envFiles = new[] {"test.env", "other.env"}; + var options = new DotEnvOptions() + .WithEnvFiles(envFiles); + + options.IgnoreExceptions + .Should() + .BeTrue(); + options.Encoding + .Should() + .Be(Encoding.UTF8); + options.TrimValues + .Should() + .BeFalse(); + options.OverwriteExistingVars + .Should() + .BeTrue(); + options.ProbeForEnv + .Should() + .BeFalse(); + options.ProbeDirectoryDepth + .Should() + .Be(4); + options.EnvFilePaths + .Should() + .BeEquivalentTo(envFiles); + } + + [Fact] + public void ShouldGenerateOptions_Read() + { + var envFiles = new[] {"quotations.env"}; + var values = new DotEnvOptions() + .WithEnvFiles(envFiles) + .Read(); + + values.Count + .Should() + .BeGreaterThan(0); + } + + [Fact] + public void ShouldGenerateOptions_Load() + { + var envFiles = new[] {"quotations.env"}; + var action = new Action(() => new DotEnvOptions() + .WithEnvFiles(envFiles) + .Load()); + + action.Should() + .NotThrow(); + } + } +} \ No newline at end of file diff --git a/tests/dotenv.net.Tests/TestFixtures/VariousValueTypesFixture.cs b/tests/dotenv.net.Tests/TestFixtures/VariousValueTypesFixture.cs new file mode 100644 index 0000000..42ee364 --- /dev/null +++ b/tests/dotenv.net.Tests/TestFixtures/VariousValueTypesFixture.cs @@ -0,0 +1,19 @@ +using System; + +namespace dotenv.net.Tests.TestFixtures +{ + public class VariousValueTypesFixture : IDisposable + { + public VariousValueTypesFixture() + { + DotEnv.Config() + .WithEnvFiles("various-value-types.env") + .Load(); + } + + public void Dispose() + { + // do nothing + } + } +} \ No newline at end of file diff --git a/tests/dotenv.net.Tests/Utilities/EnvReader.Tests.cs b/tests/dotenv.net.Tests/Utilities/EnvReader.Tests.cs index 6cdd780..6f811aa 100644 --- a/tests/dotenv.net.Tests/Utilities/EnvReader.Tests.cs +++ b/tests/dotenv.net.Tests/Utilities/EnvReader.Tests.cs @@ -1,49 +1,49 @@ using System; -using System.Text; +using dotenv.net.Tests.TestFixtures; using dotenv.net.Utilities; using FluentAssertions; using Xunit; namespace dotenv.net.Tests.Utilities { - public class EnvReaderTests + public class EnvReaderTests : IClassFixture { - private const string ValueTypesEnvFileName = "various-value-types.env"; - [Fact] public void ShouldReadStringValues() { - DotEnv.Config(true, ValueTypesEnvFileName, Encoding.UTF8); - var envReader = new EnvReader(); - - envReader.GetStringValue("CONNECTION") + EnvReader.GetStringValue("CONNECTION") .Should() .Be("mysql"); - envReader.TryGetStringValue("NON_EXISTENT_KEY", out _) + EnvReader.TryGetStringValue("CONNECTION", out _) + .Should() + .BeTrue(); + + EnvReader.TryGetStringValue("NON_EXISTENT_KEY", out _) .Should() .BeFalse(); - Action action = () => envReader.GetStringValue("NON_EXISTENT_KEY"); + Action action = () => EnvReader.GetStringValue("NON_EXISTENT_KEY"); action.Should() .Throw(); } [Fact] - public void ShouldReadBooleanValues() + public void ShouldReadIntValues() { - DotEnv.Config(true, ValueTypesEnvFileName, Encoding.UTF8); - var envReader = new EnvReader(); + EnvReader.GetIntValue("PORT") + .Should() + .Be(3306); - envReader.GetBooleanValue("IS_PRESENT") + EnvReader.TryGetIntValue("PORT", out _) .Should() .BeTrue(); - envReader.TryGetBooleanValue("NON_EXISTENT_KEY", out _) + EnvReader.TryGetIntValue("NON_EXISTENT_KEY", out _) .Should() .BeFalse(); - Action action = () => envReader.GetBooleanValue("NON_EXISTENT_KEY"); + Action action = () => EnvReader.GetIntValue("NON_EXISTENT_KEY"); action.Should() .Throw(); } @@ -51,18 +51,19 @@ public void ShouldReadBooleanValues() [Fact] public void ShouldReadDoubleValues() { - DotEnv.Config(true, ValueTypesEnvFileName, Encoding.UTF8); - var envReader = new EnvReader(); - - envReader.GetDoubleValue("DOUBLE") + EnvReader.GetDoubleValue("DOUBLE") .Should() .Be(2762821981981.37627828722); - envReader.TryGetDoubleValue("NON_EXISTENT_KEY", out _) + EnvReader.TryGetDoubleValue("DOUBLE", out _) + .Should() + .BeTrue(); + + EnvReader.TryGetDoubleValue("NON_EXISTENT_KEY", out _) .Should() .BeFalse(); - Action action = () => envReader.GetDoubleValue("NON_EXISTENT_KEY"); + Action action = () => EnvReader.GetDoubleValue("NON_EXISTENT_KEY"); action.Should() .Throw(); } @@ -70,39 +71,53 @@ public void ShouldReadDoubleValues() [Fact] public void ShouldReadDecimalValues() { - DotEnv.Config(true, ValueTypesEnvFileName, Encoding.UTF8); - var envReader = new EnvReader(); - - envReader.GetDecimalValue("DECIMAL") + EnvReader.GetDecimalValue("DECIMAL") .Should() .Be(34.56m); - envReader.TryGetDecimalValue("NON_EXISTENT_KEY", out _) + EnvReader.TryGetDecimalValue("DECIMAL", out _) + .Should() + .BeTrue(); + + EnvReader.TryGetDecimalValue("NON_EXISTENT_KEY", out _) .Should() .BeFalse(); - Action action = () => envReader.GetDecimalValue("NON_EXISTENT_KEY"); + Action action = () => EnvReader.GetDecimalValue("NON_EXISTENT_KEY"); action.Should() .Throw(); } [Fact] - public void ShouldReadIntValues() + public void ShouldReadBooleanValues() { - DotEnv.Config(true, ValueTypesEnvFileName, Encoding.UTF8); - var envReader = new EnvReader(); + EnvReader.GetBooleanValue("IS_PRESENT") + .Should() + .BeTrue(); - envReader.GetIntValue("PORT") + EnvReader.TryGetBooleanValue("IS_PRESENT", out _) .Should() - .Be(3306); + .BeTrue(); - envReader.TryGetIntValue("NON_EXISTENT_KEY", out _) + EnvReader.TryGetBooleanValue("NON_EXISTENT_KEY", out _) .Should() .BeFalse(); - Action action = () => envReader.GetIntValue("NON_EXISTENT_KEY"); + Action action = () => EnvReader.GetBooleanValue("NON_EXISTENT_KEY"); action.Should() .Throw(); } + + [Fact] + public void ShouldTellIfAKeyHasAValue() + { + EnvReader.HasValue("IS_PRESENT") + .Should() + .BeTrue(); + + EnvReader.HasValue("NON_EXISTENT_KEY") + .Should() + .BeFalse(); + } } } \ No newline at end of file diff --git a/tests/dotenv.net.Tests/dotenv.net.Tests.csproj b/tests/dotenv.net.Tests/dotenv.net.Tests.csproj index c28598e..c12f82d 100644 --- a/tests/dotenv.net.Tests/dotenv.net.Tests.csproj +++ b/tests/dotenv.net.Tests/dotenv.net.Tests.csproj @@ -2,15 +2,15 @@ net5.0 false - 1.0.6 + 3.0.0 - + runtime; build; native; contentfiles; analyzers all - + all @@ -18,9 +18,6 @@ - - Always - PreserveNewest diff --git a/tests/dotenv.net.Tests/values-and-comments.env b/tests/dotenv.net.Tests/values-and-comments.env index d0e6df8..c3c99c4 100644 --- a/tests/dotenv.net.Tests/values-and-comments.env +++ b/tests/dotenv.net.Tests/values-and-comments.env @@ -1,3 +1,7 @@ # this is a comment me=winner -akeywithoutavalue \ No newline at end of file +akeywithoutavalue + + = avaluewithoutakey + +me=recurringvalue \ No newline at end of file diff --git a/tests/dotenv.net.Tests/values-with-whitespaces.env b/tests/dotenv.net.Tests/values-with-whitespaces.env index 4d50a9a..59e4987 100644 --- a/tests/dotenv.net.Tests/values-with-whitespaces.env +++ b/tests/dotenv.net.Tests/values-with-whitespaces.env @@ -8,4 +8,4 @@ DB_PORT= 3306 # This is a comment DB_DATABASE=laravel -IS_PRESENT= true +IS_PRESENT= true \ No newline at end of file