diff --git a/Fig.Core/Fig.Core.csproj b/Fig.Core/Fig.Core.csproj index bada2eb..15bc26f 100644 --- a/Fig.Core/Fig.Core.csproj +++ b/Fig.Core/Fig.Core.csproj @@ -15,6 +15,7 @@ Robert Friberg et al MIT Fig + 8 diff --git a/Fig.Core/Settings.cs b/Fig.Core/Settings.cs index d3479b7..bff1d2e 100644 --- a/Fig.Core/Settings.cs +++ b/Fig.Core/Settings.cs @@ -17,29 +17,21 @@ public sealed class Settings /// private readonly IStringConverter _converter; - /// - /// Path to the node in the tree to bind to for example: - /// Given keys A.B.C and A.B.D, a binding path of A.B - /// will bind the values of A.B.C and A.B.D to properties C and D of this type - /// - private readonly string _bindingPath; - public override String ToString() { if (SettingsDictionary is null) return base.ToString(); return SettingsDictionary.AsString(); } - private Settings(string bindingPath = null, IStringConverter converter = null) + private Settings(IStringConverter converter = null) { _converter = converter ?? new InvariantStringConverter(); - _bindingPath = bindingPath ?? ""; } internal Settings(LayeredSettingsDictionary settingsDictionary, - string bindingPath = null, IStringConverter converter = null) - : this(bindingPath, converter) + IStringConverter converter = null) + : this(converter) { SettingsDictionary = settingsDictionary; } @@ -76,10 +68,15 @@ public void Bind(T target, bool validate = true, string path = null) BindProperties(target, validate, path); } + internal string Combine(string bindingPath, string typeName, string propertyName) + { + if (bindingPath is null) return $"{typeName}.{propertyName}"; + if (bindingPath.Trim() == "") return propertyName; + else return $"{bindingPath}.{propertyName}"; + } + private void BindProperties(object target, bool validate, string bindingPath = null) - { - bindingPath = bindingPath ?? _bindingPath; - + { foreach (var property in target.GetType().GetProperties()) { try @@ -89,12 +86,12 @@ private void BindProperties(object target, bool validate, string bindingPath = n if (propertyIsReadonly || property.PropertyType.IsAbstract || property.PropertyType.IsInterface) continue; - - var name = String.IsNullOrEmpty(bindingPath?.Trim()) ? property.Name : $"{bindingPath}.{property.Name}"; + + var name = Combine(bindingPath, property.PropertyType.Name, property.Name); var defaultValue = property.GetValue(target); var required = validate && defaultValue is null; var result = GetPropertyValue(property, name, required); - property.SetValue(target, result); + if (result != null) property.SetValue(target, result); } catch (TargetInvocationException ex) { @@ -165,11 +162,8 @@ public bool TryGet(string key, out T result) /// /// This is where the actual retrieval and type conversion happens /// - private object Get(Type propertyType, string propertyName) + private object Get(Type propertyType, string key) { - var key = propertyName; - if (_bindingPath.Length > 0) key = _bindingPath + "." + key; - if (!SettingsDictionary.TryGetValue(key, out string value)) { throw new KeyNotFoundException(key); diff --git a/Fig.Tests/BindToNullablePropertiesTests.cs b/Fig.Tests/BindToNullablePropertiesTests.cs index d21bff5..8b652f0 100644 --- a/Fig.Tests/BindToNullablePropertiesTests.cs +++ b/Fig.Tests/BindToNullablePropertiesTests.cs @@ -1,5 +1,4 @@ using System; -using System.Globalization; using NUnit.Framework; namespace Fig.Test; diff --git a/Fig.Tests/BindingBehavior.cs b/Fig.Tests/BindingBehavior.cs new file mode 100644 index 0000000..379f380 --- /dev/null +++ b/Fig.Tests/BindingBehavior.cs @@ -0,0 +1,44 @@ +using NUnit.Framework; + +namespace Fig.Test; + +public class BindingBehavior +{ + [Test] + public void EmptyPathBindsToRoot() + { + var settings = new SettingsBuilder() + .UseSettingsDictionary(new SettingsDictionary() + { + {"Hello", "Hi"}, + {"Number", "16"}, + {"OtherNumber", "16"} + }).Build(); + + var settingsWithDefaults = settings.Bind(path:""); + Assert.AreEqual(settingsWithDefaults.Hello, "Hi"); + Assert.AreEqual(settingsWithDefaults.Number, 16); + Assert.AreEqual(settingsWithDefaults.OtherNumber, 16); + + } + + [Test] + public void CanBindToNullableProperties() + { + //empty dictionary + var settings = new SettingsBuilder().Build(); + var settingsWithDefaults = settings.Bind(); + + Assert.AreEqual(settingsWithDefaults.Hello, "Hello"); + Assert.AreEqual(settingsWithDefaults.Number, 42); + Assert.AreEqual(settingsWithDefaults.OtherNumber, 42); + } + + private class SettingsWithDefaults + { + public int Number { get; set; } = 42; + public string Hello { get; set; } = nameof(Hello); + + public int? OtherNumber { get; set; } = 42; + } +} \ No newline at end of file