using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using Swan.Formatters; using Swan.Reflection; namespace Swan.Configuration { /// /// Represents a provider to save and load settings using a plain JSON file. /// /// /// The following example shows how to save and load settings. /// /// using Swan.Configuration; /// /// public class Example /// { /// public static void Main() /// { /// // get user from settings /// var user = SettingsProvider<Settings>.Instance.Global.User; /// /// // modify the port /// SettingsProvider<Settings>.Instance.Global.Port = 20; /// /// // if we want these settings to persist /// SettingsProvider<Settings>.Instance.PersistGlobalSettings(); /// } /// /// public class Settings /// { /// public int Port { get; set; } = 9696; /// /// public string User { get; set; } = "User"; /// } /// } /// /// /// The type of settings model. public sealed class SettingsProvider : SingletonBase> { private readonly object _syncRoot = new object(); private T _global; /// /// Gets or sets the configuration file path. By default the entry assembly directory is used /// and the filename is 'appsettings.json'. /// /// /// The configuration file path. /// public string ConfigurationFilePath { get; set; } = Path.Combine(SwanRuntime.EntryAssemblyDirectory, "appsettings.json"); /// /// Gets the global settings object. /// /// /// The global settings object. /// public T Global { get { lock (_syncRoot) { if (Equals(_global, default(T))) ReloadGlobalSettings(); return _global; } } } /// /// Reloads the global settings. /// public void ReloadGlobalSettings() { if (File.Exists(ConfigurationFilePath) == false || File.ReadAllText(ConfigurationFilePath).Length == 0) { ResetGlobalSettings(); return; } lock (_syncRoot) _global = Json.Deserialize(File.ReadAllText(ConfigurationFilePath)); } /// /// Persists the global settings. /// public void PersistGlobalSettings() => File.WriteAllText(ConfigurationFilePath, Json.Serialize(Global, true)); /// /// Updates settings from list. /// /// The list. /// /// A list of settings of type ref="ExtendedPropertyInfo". /// /// propertyList. public List RefreshFromList(List> propertyList) { if (propertyList == null) throw new ArgumentNullException(nameof(propertyList)); var changedSettings = new List(); var globalProps = PropertyTypeCache.DefaultCache.Value.RetrieveAllProperties(); foreach (var property in propertyList) { var propertyInfo = globalProps.FirstOrDefault(x => x.Name == property.Property); if (propertyInfo == null) continue; var originalValue = propertyInfo.GetValue(Global); var isChanged = propertyInfo.PropertyType.IsArray ? property.Value is IEnumerable enumerable && propertyInfo.TrySetArray(enumerable.Cast(), Global) : SetValue(property.Value, originalValue, propertyInfo); if (!isChanged) continue; changedSettings.Add(property.Property); PersistGlobalSettings(); } return changedSettings; } /// /// Gets the list. /// /// A List of ExtendedPropertyInfo of the type T. public List>? GetList() { var jsonData = Json.Deserialize(Json.Serialize(Global)) as Dictionary; return jsonData?.Keys .Select(p => new ExtendedPropertyInfo(p) { Value = jsonData[p] }) .ToList(); } /// /// Resets the global settings. /// public void ResetGlobalSettings() { lock (_syncRoot) _global = Activator.CreateInstance(); PersistGlobalSettings(); } private bool SetValue(object property, object originalValue, PropertyInfo propertyInfo) { switch (property) { case null when originalValue == null: break; case null: propertyInfo.SetValue(Global, null); return true; default: if (propertyInfo.PropertyType.TryParseBasicType(property, out var propertyValue) && !propertyValue.Equals(originalValue)) { propertyInfo.SetValue(Global, propertyValue); return true; } break; } return false; } } }