diff --git a/Modules/GenHTTP.Modules.I18n/Parsers/LanguageParser.cs b/Modules/GenHTTP.Modules.I18n/Parsers/LanguageParser.cs index ba6fcc0b..7a620110 100644 --- a/Modules/GenHTTP.Modules.I18n/Parsers/LanguageParser.cs +++ b/Modules/GenHTTP.Modules.I18n/Parsers/LanguageParser.cs @@ -44,7 +44,6 @@ public static CultureInfo[] ParseFromLanguage(string? language) try { - var actualCount = 0; var start = 0; int commaIndex; @@ -95,7 +94,6 @@ public static CultureInfo[] ParseFromLanguage(string? language) rentedArray[actualCount++] = (languagePart.ToString(), qValue); } } - } while (commaIndex >= 0); if (actualCount == 0) diff --git a/Modules/GenHTTP.Modules.I18n/Provider/LocalizationConcern.cs b/Modules/GenHTTP.Modules.I18n/Provider/LocalizationConcern.cs index 555d6c24..3bdebb59 100644 --- a/Modules/GenHTTP.Modules.I18n/Provider/LocalizationConcern.cs +++ b/Modules/GenHTTP.Modules.I18n/Provider/LocalizationConcern.cs @@ -11,9 +11,9 @@ public sealed class LocalizationConcern : IConcern public IHandler Content { get; } private readonly CultureInfo _defaultCulture; - private readonly CultureSelectorDelegate _cultureSelector; - private readonly CultureFilterDelegate _cultureFilter; - private readonly CultureSetterDelegate _cultureSetter; + private readonly CultureSelectorCombinedAsyncDelegate _cultureSelector; + private readonly CultureFilterAsyncDelegate _cultureFilter; + private readonly CultureSetterAsyncDelegate _cultureSetter; #endregion @@ -22,9 +22,9 @@ public sealed class LocalizationConcern : IConcern public LocalizationConcern( IHandler content, CultureInfo defaultCulture, - CultureSelectorDelegate cultureSelector, - CultureFilterDelegate cultureFilter, - CultureSetterDelegate cultureSetter + CultureSelectorCombinedAsyncDelegate cultureSelector, + CultureFilterAsyncDelegate cultureFilter, + CultureSetterAsyncDelegate cultureSetter ) { Content = content; @@ -40,13 +40,23 @@ CultureSetterDelegate cultureSetter #region Functionality + private async ValueTask ResolveCultureInfoAsync(IRequest request) + { + await foreach (var candidate in _cultureSelector(request)) + { + if (await _cultureFilter(request, candidate)) + { + return candidate; + } + } + return null; + } + public async ValueTask HandleAsync(IRequest request) { - var culture = (_cultureSelector(request) ?? []) - .FirstOrDefault(c => _cultureFilter(request, c)) - ?? _defaultCulture; + var culture = await ResolveCultureInfoAsync(request) ?? _defaultCulture; - _cultureSetter(request, culture); + await _cultureSetter(request, culture); return await Content.HandleAsync(request); } diff --git a/Modules/GenHTTP.Modules.I18n/Provider/LocalizationConcernBuilder.cs b/Modules/GenHTTP.Modules.I18n/Provider/LocalizationConcernBuilder.cs index 89bb5d3d..06dd3db0 100644 --- a/Modules/GenHTTP.Modules.I18n/Provider/LocalizationConcernBuilder.cs +++ b/Modules/GenHTTP.Modules.I18n/Provider/LocalizationConcernBuilder.cs @@ -14,9 +14,9 @@ public sealed class LocalizationConcernBuilder : IConcernBuilder private CultureInfo _defaultCulture = CultureInfo.CurrentCulture; - private readonly List _cultureSelectors = []; - private CultureFilterDelegate _cultureFilter = (_, _) => true; - private readonly List _cultureSetters = []; + private readonly List _cultureSelectors = []; + private CultureFilterAsyncDelegate _cultureFilter = (_, _) => ValueTask.FromResult(true); + private readonly List _cultureSetters = []; #endregion @@ -46,13 +46,19 @@ public LocalizationConcernBuilder FromHeader(string headerName = "Accept-Languag }); public LocalizationConcernBuilder FromLanguage(Func languageSelector) - => FromRequest(request => + => FromLanguage(request => ValueTask.FromResult(languageSelector(request))); + + public LocalizationConcernBuilder FromLanguage(Func> languageSelector) + => FromRequest(async request => { - var language = languageSelector(request); + var language = await languageSelector(request); return CultureInfoParser.ParseFromLanguage(language); }); - public LocalizationConcernBuilder FromRequest(CultureSelectorDelegate cultureSelector) + public LocalizationConcernBuilder FromRequest(CultureSelectorDelegate cultureSelector) + => FromRequest(request => ValueTask.FromResult(cultureSelector(request))); + + public LocalizationConcernBuilder FromRequest(CultureSelectorAsyncDelegate cultureSelector) { _cultureSelectors.Add(cultureSelector); return this; @@ -71,7 +77,13 @@ public LocalizationConcernBuilder Supports(params CultureInfo[] supportedCulture public LocalizationConcernBuilder Supports(Predicate culturePredicate) => Supports((_, culture) => culturePredicate(culture)); + public LocalizationConcernBuilder Supports(Func> culturePredicate) + => Supports((_, culture) => culturePredicate(culture)); + public LocalizationConcernBuilder Supports(CultureFilterDelegate cultureFilter) + => Supports((request, culture) => ValueTask.FromResult(cultureFilter(request, culture))); + + public LocalizationConcernBuilder Supports(CultureFilterAsyncDelegate cultureFilter) { _cultureFilter = cultureFilter; return this; @@ -100,10 +112,20 @@ public LocalizationConcernBuilder Setter(bool currentCulture = false, bool curre return this; } + public LocalizationConcernBuilder Setter(Func cultureSetter) + => Setter((_, culture) => cultureSetter(culture)); + public LocalizationConcernBuilder Setter(Action cultureSetter) => Setter((_, culture) => cultureSetter(culture)); public LocalizationConcernBuilder Setter(CultureSetterDelegate cultureSetter) + => Setter((request, culture) => + { + cultureSetter(request, culture); + return ValueTask.CompletedTask; + }); + + public LocalizationConcernBuilder Setter(CultureSetterAsyncDelegate cultureSetter) { _cultureSetters.Add(cultureSetter); return this; @@ -146,14 +168,24 @@ public IConcern Build(IHandler content) #region Composite functions - private IEnumerable CultureSelector(IRequest request) - => _cultureSelectors.SelectMany(selector => selector(request)); + private async IAsyncEnumerable CultureSelector(IRequest request) + { + foreach (var selector in _cultureSelectors) + { + var cultures = await selector(request); + foreach (var culture in cultures) + { + yield return culture; + } + } + } + - private void CultureSetter(IRequest request, CultureInfo cultureInfo) + private async ValueTask CultureSetter(IRequest request, CultureInfo cultureInfo) { foreach (var setter in _cultureSetters) { - setter(request, cultureInfo); + await setter(request, cultureInfo); } } diff --git a/Modules/GenHTTP.Modules.I18n/Provider/Types.cs b/Modules/GenHTTP.Modules.I18n/Provider/Types.cs index f2b065c6..24f3229b 100644 --- a/Modules/GenHTTP.Modules.I18n/Provider/Types.cs +++ b/Modules/GenHTTP.Modules.I18n/Provider/Types.cs @@ -3,6 +3,35 @@ namespace GenHTTP.Modules.I18n.Provider; +/// +/// Delegate to extract the cultures for a given request. +/// +/// The request to extract cultures for. +/// An enumerable of CultureInfo objects representing the extracted cultures. +public delegate IAsyncEnumerable CultureSelectorCombinedAsyncDelegate(IRequest request); + +/// +/// Delegate to extract the cultures for a given request. +/// +/// The request to extract cultures for. +/// An enumerable of CultureInfo objects representing the extracted cultures. +public delegate ValueTask> CultureSelectorAsyncDelegate(IRequest request); + +/// +/// Delegate to set the culture for a given request. +/// +/// The request to set the culture for. +/// The CultureInfo object representing the culture to be set. +public delegate ValueTask CultureSetterAsyncDelegate(IRequest request, CultureInfo cultureInfo); + +/// +/// Delegate to filter the cultures for a given request. +/// +/// The request to filter cultures for. +/// The CultureInfo object representing the culture to be filtered. +/// True if the culture is valid for the request, otherwise false. +public delegate ValueTask CultureFilterAsyncDelegate(IRequest request, CultureInfo cultureInfo); + /// /// Delegate to extract the cultures for a given request. ///