diff options
Diffstat (limited to 'lib/VNLib.Plugins.Extensions.Loading')
3 files changed, 92 insertions, 26 deletions
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<IReadOnlyDictionary<string, JsonElement>> _config; + private readonly LazyInitializer<IReadOnlyDictionary<string, JsonElement>> _config; private readonly JsonElement _element; @@ -48,36 +50,40 @@ namespace VNLib.Plugins.Extensions.Loading private IReadOnlyDictionary<string, JsonElement> 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 + ); } ///<inheritdoc/> - public JsonElement this[string key] => _config.Value[key]; + public JsonElement this[string key] => _config.Instance[key]; ///<inheritdoc/> - public IEnumerable<string> Keys => _config.Value.Keys; + public IEnumerable<string> Keys => _config.Instance.Keys; ///<inheritdoc/> - public IEnumerable<JsonElement> Values => _config.Value.Values; + public IEnumerable<JsonElement> Values => _config.Instance.Values; ///<inheritdoc/> - public int Count => _config.Value.Count; + public int Count => _config.Instance.Count; ///<inheritdoc/> public string ScopeName { get; } ///<inheritdoc/> - public bool ContainsKey(string key) => _config.Value.ContainsKey(key); + public bool ContainsKey(string key) => _config.Instance.ContainsKey(key); ///<inheritdoc/> public T Deserialze<T>() => _element.Deserialize<T>()!; ///<inheritdoc/> - public IEnumerator<KeyValuePair<string, JsonElement>> GetEnumerator() => _config.Value.GetEnumerator(); + public IEnumerator<KeyValuePair<string, JsonElement>> GetEnumerator() => _config.Instance.GetEnumerator(); ///<inheritdoc/> - 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"; /// <summary> /// 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; + } + + + /// <summary> + /// Gets a required configuration property from the specified configuration scope + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="config"></param> + /// <param name="property">The name of the property to get</param> + /// <returns>The property value</returns> + /// <exception cref="ArgumentNullException"></exception> + /// <exception cref="ConfigurationException"></exception> + public static T GetRequiredProperty<T>(this IConfigScope config, string property) + { + return GetRequiredProperty(config, property, static p => p.Deserialize<T>()!); } /// <summary> @@ -258,6 +273,28 @@ namespace VNLib.Plugins.Extensions.Loading } /// <summary> + /// 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. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="config"></param> + /// <param name="property">The name of the configuration element to get</param> + /// <param name="defaultValue">The default value to return</param> + /// <returns>The property value returned from your getter callback, or the default value if not found</returns> + /// <exception cref="ArgumentNullException"></exception> + [return: NotNullIfNotNull(nameof(defaultValue))] + public static T? GetValueOrDefault<T>(this IConfigScope config, string property, T defaultValue) + { + return GetValueOrDefault( + config, + property, + static p => p.Deserialize<T>(), + defaultValue + ); + } + + /// <summary> /// Gets the configuration property name for the type /// </summary> /// <param name="type">The type to get the configuration name for</param> @@ -442,7 +479,6 @@ namespace VNLib.Plugins.Extensions.Loading } } - /// <summary> /// 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 /// </summary> /// <param name="plugin"></param> /// <exception cref="ObjectDisposedException"></exception> - 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); } |