From c8567e58dc1d4135da1f6cefa6fa66af5fcd7b19 Mon Sep 17 00:00:00 2001 From: vnugent Date: Mon, 15 Jul 2024 19:05:01 -0400 Subject: feat: Smiplify configuration helpers --- .../src/Configuration/ConfigScope.cs | 28 ++++--- .../src/ConfigurationExtensions.cs | 85 ++++++++++++++++++---- .../src/LoadingExtensions.cs | 5 +- 3 files changed, 92 insertions(+), 26 deletions(-) (limited to 'lib/VNLib.Plugins.Extensions.Loading/src') diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Configuration/ConfigScope.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Configuration/ConfigScope.cs index 7f5c09c..d8f4347 100644 --- a/lib/VNLib.Plugins.Extensions.Loading/src/Configuration/ConfigScope.cs +++ b/lib/VNLib.Plugins.Extensions.Loading/src/Configuration/ConfigScope.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Extensions.Loading @@ -29,13 +29,15 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using VNLib.Utils.Resources; + namespace VNLib.Plugins.Extensions.Loading { internal sealed class ConfigScope: IConfigScope { - private readonly Lazy> _config; + private readonly LazyInitializer> _config; private readonly JsonElement _element; @@ -48,36 +50,40 @@ namespace VNLib.Plugins.Extensions.Loading private IReadOnlyDictionary LoadTable() { - return _element.EnumerateObject().ToDictionary(static k => k.Name, static k => k.Value); + return _element.EnumerateObject() + .ToDictionary( + static k => k.Name, + static k => k.Value + ); } /// - public JsonElement this[string key] => _config.Value[key]; + public JsonElement this[string key] => _config.Instance[key]; /// - public IEnumerable Keys => _config.Value.Keys; + public IEnumerable Keys => _config.Instance.Keys; /// - public IEnumerable Values => _config.Value.Values; + public IEnumerable Values => _config.Instance.Values; /// - public int Count => _config.Value.Count; + public int Count => _config.Instance.Count; /// public string ScopeName { get; } /// - public bool ContainsKey(string key) => _config.Value.ContainsKey(key); + public bool ContainsKey(string key) => _config.Instance.ContainsKey(key); /// public T Deserialze() => _element.Deserialize()!; /// - public IEnumerator> GetEnumerator() => _config.Value.GetEnumerator(); + public IEnumerator> GetEnumerator() => _config.Instance.GetEnumerator(); /// - public bool TryGetValue(string key, [MaybeNullWhen(false)] out JsonElement value) => _config.Value.TryGetValue(key, out value); + public bool TryGetValue(string key, [MaybeNullWhen(false)] out JsonElement value) => _config.Instance.TryGetValue(key, out value); - IEnumerator IEnumerable.GetEnumerator() => _config.Value.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _config.Instance.GetEnumerator(); } } diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs index 0a1bc7f..39bdc86 100644 --- a/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs +++ b/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs @@ -31,6 +31,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using VNLib.Utils.Extensions; +using VNLib.Plugins.Extensions.Loading.Configuration; namespace VNLib.Plugins.Extensions.Loading { @@ -67,7 +68,6 @@ namespace VNLib.Plugins.Extensions.Loading public const string S3_SECRET_KEY = "s3_secret"; public const string PLUGIN_ASSET_KEY = "assets"; public const string PLUGINS_HOST_KEY = "plugins"; - public const string PLUGIN_PATHS_KEY = "paths"; /// /// Retrieves a top level configuration dictionary of elements for the specified type. @@ -200,13 +200,28 @@ namespace VNLib.Plugins.Extensions.Loading ArgumentNullException.ThrowIfNull(getter); //Get the property - if (!config.TryGetValue(property, out JsonElement el)) - { - throw new ConfigurationException($"Missing required configuration property '{property}' in config {config.ScopeName}"); - } + bool hasValue = config.TryGetValue(property, out JsonElement el); + Validate.Assert(hasValue, $"Missing required configuration property '{property}' in config {config.ScopeName}"); + + T? value = getter(el); + Validate.Assert(value is not null, $"Missing required configuration property '{property}' in config {config.ScopeName}"); - //Even if the getter returns null, throw - return getter(el) ?? throw new ConfigurationException($"Missing required configuration property '{property}' in config {config.ScopeName}"); + return value; + } + + + /// + /// Gets a required configuration property from the specified configuration scope + /// + /// + /// + /// The name of the property to get + /// The property value + /// + /// + public static T GetRequiredProperty(this IConfigScope config, string property) + { + return GetRequiredProperty(config, property, static p => p.Deserialize()!); } /// @@ -257,6 +272,28 @@ namespace VNLib.Plugins.Extensions.Loading return TryGetProperty(config, property, getter, out T? value) ? value : defaultValue; } + /// + /// Gets a configuration property from the specified configuration scope + /// and invokes your callback function on the element if found to transform the + /// output value, or returns the default value if the property is not found. + /// + /// + /// + /// The name of the configuration element to get + /// The default value to return + /// The property value returned from your getter callback, or the default value if not found + /// + [return: NotNullIfNotNull(nameof(defaultValue))] + public static T? GetValueOrDefault(this IConfigScope config, string property, T defaultValue) + { + return GetValueOrDefault( + config, + property, + static p => p.Deserialize(), + defaultValue + ); + } + /// /// Gets the configuration property name for the type /// @@ -442,7 +479,6 @@ namespace VNLib.Plugins.Extensions.Loading } } - /// /// Attempts to load the basic S3 configuration variables required /// for S3 client access @@ -483,11 +519,34 @@ namespace VNLib.Plugins.Extensions.Loading //Get global plugin config element IConfigScope config = plugin.GetConfig(PLUGINS_HOST_KEY); - //Get the plugins path or throw because it should ALWAYS be defined if this method is called - return config[PLUGIN_PATHS_KEY].EnumerateArray() - .Select(static p => p.GetString()!) - .Select(Path.GetFullPath) //Get absolute file paths - .ToArray(); + /* + * Hosts are allowed to define mutliple plugin loading paths. A + * single path is supported for compat. Multi path takes precidence + * of course so attempt to load a string array first + */ + + if (!config.TryGetValue("paths", out JsonElement searchPaths) + && !config.TryGetValue("path", out searchPaths)) + { + return []; + } + + if (searchPaths.ValueKind == JsonValueKind.Array) + { + //Get the plugins path or throw because it should ALWAYS be defined if this method is called + return searchPaths.EnumerateArray() + .Select(static p => p.GetString()!) + .Select(Path.GetFullPath) //Get absolute file paths + .ToArray(); + } + else if (searchPaths.ValueKind == JsonValueKind.String) + { + return [Path.GetFullPath(searchPaths.GetString()!)]; + } + else + { + return []; + } } } } diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs index 4ffb3a1..58478d4 100644 --- a/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs +++ b/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs @@ -123,7 +123,7 @@ namespace VNLib.Plugins.Extensions.Loading */ if (searchDirs.Length == 0) { - throw new ArgumentException("No plugin asset directory is defined for the current host configuration, this is likely a bug"); + throw new ConfigurationException("No plugin asset directory is defined for the current host configuration, this is likely a bug"); } //Get the first file that matches the search file @@ -268,9 +268,10 @@ namespace VNLib.Plugins.Extensions.Loading /// /// /// - public static void ThrowIfUnloaded(this PluginBase plugin) + public static void ThrowIfUnloaded(this PluginBase? plugin) { //See if the plugin was unlaoded + ArgumentNullException.ThrowIfNull(plugin); ObjectDisposedException.ThrowIf(plugin.UnloadToken.IsCancellationRequested, plugin); } -- cgit