diff --git a/src/Config.Tests/ConfigurationTests.cs b/src/Config.Tests/ConfigurationTests.cs index 784a9ba..5eb5e58 100644 --- a/src/Config.Tests/ConfigurationTests.cs +++ b/src/Config.Tests/ConfigurationTests.cs @@ -1,5 +1,8 @@ -using System.Globalization; +using System; +using System.Globalization; using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; using Microsoft.Extensions.Configuration; using Xunit; using Xunit.Abstractions; @@ -15,33 +18,50 @@ public ConfigurationTests(ITestOutputHelper output) Config.GlobalLocation = Path.Combine(Constants.CurrentDirectory, "Content", "global.netconfig"); Config.SystemLocation = Path.Combine(Constants.CurrentDirectory, "Content", "system.netconfig"); CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; - Directory.SetCurrentDirectory(Path.Combine(Constants.CurrentDirectory, "Content", "web")); this.output = output; } - [FlakyFact] + [Fact] public void can_load_hierarchical_values() { - var config = new ConfigurationBuilder().AddDotNetConfig().Build(); + var config = BuildConfiguration(); + Assert.Equal("on", config["core:parent"]); Assert.Equal("true", config["http:sslVerify"]); Assert.Equal("false", config["http:https://weak.example.com:sslVerify"]); Assert.Equal("yay", config["foo:bar:baz"]); } - [FlakyFact] + [Fact] public void can_save_values() { - var config = new ConfigurationBuilder().AddDotNetConfig().Build(); + var config = BuildConfiguration(); + config["foo:enabled"] = "true"; config["foo:bar:baz"] = "bye"; config["http:https://weaker.example.com:sslVerify"] = "false"; - var dotnet = Config.Build(); + var dotnet = Config.Build(Path.Combine(Constants.CurrentDirectory, "Content", "web", nameof(can_save_values))); Assert.Equal("true", dotnet.GetString("foo", "enabled")); Assert.Equal("bye", dotnet.GetString("foo", "bar", "baz")); Assert.Equal("false", dotnet.GetString("http", "https://weaker.example.com", "sslVerify")); } + + [Fact] + public void local_values_override_system_values() + { + var config = BuildConfiguration(); + + Assert.Equal("123", config["local:value"]); + } + + IConfiguration BuildConfiguration([CallerMemberName] string? methodName = null) + { + var dir = Path.Combine(Constants.CurrentDirectory, "Content", "web", methodName); + Directory.CreateDirectory(dir); + + return new ConfigurationBuilder().AddDotNetConfig(dir).Build(); + } } } \ No newline at end of file diff --git a/src/Config.Tests/Content/global.netconfig b/src/Config.Tests/Content/global.netconfig index 2adff22..458ffc7 100644 --- a/src/Config.Tests/Content/global.netconfig +++ b/src/Config.Tests/Content/global.netconfig @@ -2,3 +2,5 @@ sslVerify = false [core] global = yes +[local] + value = 456 \ No newline at end of file diff --git a/src/Config.Tests/Content/web/.netconfig b/src/Config.Tests/Content/web/.netconfig index 5f28270..54f9119 100644 --- a/src/Config.Tests/Content/web/.netconfig +++ b/src/Config.Tests/Content/web/.netconfig @@ -1 +1,2 @@ - \ No newline at end of file +[local] + value = 123 \ No newline at end of file diff --git a/src/Configuration/DotNetConfigExtensions.cs b/src/Configuration/DotNetConfigExtensions.cs index 53c5366..d140179 100644 --- a/src/Configuration/DotNetConfigExtensions.cs +++ b/src/Configuration/DotNetConfigExtensions.cs @@ -22,6 +22,25 @@ public static class DotNetConfigExtensions /// /// public static IConfigurationBuilder AddDotNetConfig(this IConfigurationBuilder builder) - => builder.Add(new DotNetConfigSource()); + => AddDotNetConfig(builder, null); + + /// + /// Adds the DotNetConfig configuration provider to the . + /// + /// The to add dotnet config support to. + /// Optional path to use when building the configuration. See . + /// If not provided, the current directory will be used. + /// The . + /// + /// Simply invoke this method on a builder to add hierarchical dotnet-config support + /// to your app, for example: + /// + /// var config = new ConfigurationBuilder().AddDotNetConfig().Build(); + /// + /// var ssl = config["http:sslVerify"]; + /// + /// + public static IConfigurationBuilder AddDotNetConfig(this IConfigurationBuilder builder, string? path = null) + => builder.Add(new DotNetConfigSource(path)); } } diff --git a/src/Configuration/DotNetConfigProvider.cs b/src/Configuration/DotNetConfigProvider.cs index d4cf22b..85f25c3 100644 --- a/src/Configuration/DotNetConfigProvider.cs +++ b/src/Configuration/DotNetConfigProvider.cs @@ -6,7 +6,9 @@ namespace DotNetConfig { class DotNetConfigProvider : ConfigurationProvider { - Config configuration = Config.Build(); + Config configuration; + + public DotNetConfigProvider(string? path = null) => configuration = Config.Build(path); public override void Load() { @@ -25,7 +27,8 @@ public override void Load() key += ConfigurationPath.KeyDelimiter + entry.Variable; - data[key] = string.IsNullOrWhiteSpace(entry.RawValue) ? "true" : entry.RawValue!.Trim('"'); + if (!data.ContainsKey(key)) + data[key] = string.IsNullOrWhiteSpace(entry.RawValue) ? "true" : entry.RawValue!.Trim('"'); } Data = data; diff --git a/src/Configuration/DotNetConfigSource.cs b/src/Configuration/DotNetConfigSource.cs index d5a9ba7..c9de4e5 100644 --- a/src/Configuration/DotNetConfigSource.cs +++ b/src/Configuration/DotNetConfigSource.cs @@ -4,6 +4,10 @@ namespace DotNetConfig { class DotNetConfigSource : IConfigurationSource { - public IConfigurationProvider Build(IConfigurationBuilder builder) => new DotNetConfigProvider(); + readonly string? path; + + public DotNetConfigSource(string? path = null) => this.path = path; + + public IConfigurationProvider Build(IConfigurationBuilder builder) => new DotNetConfigProvider(path); } }