// Copyright © WireMock.Net using System.Collections.Generic; using System.Linq; using AnyOfTypes; using Stef.Validation; using WireMock.Extensions; using WireMock.Models; using WireMock.Util; namespace WireMock.Matchers; /// /// FormUrl Encoded fields Matcher /// /// /// public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher { private readonly AnyOf[] _patterns; /// public MatchBehaviour MatchBehaviour { get; } private readonly List<(WildcardMatcher Key, WildcardMatcher? Value)> _pairs = []; /// /// Initializes a new instance of the class. /// /// The pattern. /// Ignore the case from the pattern. /// The to use. (default = "Or") public FormUrlEncodedMatcher( AnyOf pattern, bool ignoreCase = false, MatchOperator matchOperator = MatchOperator.Or) : this(MatchBehaviour.AcceptOnMatch, [pattern], ignoreCase, matchOperator) { } /// /// Initializes a new instance of the class. /// /// The match behaviour. /// The pattern. /// Ignore the case from the pattern. /// The to use. (default = "Or") public FormUrlEncodedMatcher( MatchBehaviour matchBehaviour, AnyOf pattern, bool ignoreCase = false, MatchOperator matchOperator = MatchOperator.Or) : this(matchBehaviour, [pattern], ignoreCase, matchOperator) { } /// /// Initializes a new instance of the class. /// /// The patterns. /// Ignore the case from the pattern. /// The to use. (default = "Or") public FormUrlEncodedMatcher( AnyOf[] patterns, bool ignoreCase = false, MatchOperator matchOperator = MatchOperator.Or) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase, matchOperator) { } /// /// Initializes a new instance of the class. /// /// The match behaviour. /// The patterns. /// Ignore the case from the pattern. /// The to use. (default = "Or") public FormUrlEncodedMatcher( MatchBehaviour matchBehaviour, AnyOf[] patterns, bool ignoreCase = false, MatchOperator matchOperator = MatchOperator.Or) { _patterns = Guard.NotNull(patterns); IgnoreCase = ignoreCase; MatchBehaviour = matchBehaviour; MatchOperator = matchOperator; foreach (var pattern in _patterns) { if (QueryStringParser.TryParse(pattern, IgnoreCase, out var nameValueCollection)) { foreach (var nameValue in nameValueCollection) { var keyMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Key], ignoreCase, MatchOperator); var valueMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Value], ignoreCase, MatchOperator); _pairs.Add((keyMatcher, valueMatcher)); } } } } /// public MatchResult IsMatch(string? input) { // Input is null or empty and if no patterns defined, return Perfect match. if (string.IsNullOrEmpty(input) && _patterns.Length == 0) { return MatchResult.From(Name, MatchScores.Perfect); } if (!QueryStringParser.TryParse(input, IgnoreCase, out var inputNameValueCollection)) { return MatchResult.From(Name, MatchScores.Mismatch); } var matches = GetMatches(inputNameValueCollection); var score = MatchScores.ToScore(matches, MatchOperator); return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score)); } private bool[] GetMatches(IDictionary inputNameValueCollection) { var matches = new List(); if (_pairs.Count > inputNameValueCollection.Count) { matches.AddRange(Enumerable.Repeat(false, _pairs.Count - inputNameValueCollection.Count)); } foreach (var inputKeyValuePair in inputNameValueCollection) { var match = false; foreach (var pair in _pairs) { var keyMatchResult = pair.Key.IsMatch(inputKeyValuePair.Key).IsPerfect(); if (keyMatchResult) { match = pair.Value?.IsMatch(inputKeyValuePair.Value).IsPerfect() ?? false; if (match) { break; } } } matches.Add(match); } return matches.ToArray(); } /// public virtual AnyOf[] GetPatterns() { return _patterns; } /// public virtual string Name => nameof(FormUrlEncodedMatcher); /// public bool IgnoreCase { get; } /// public MatchOperator MatchOperator { get; } /// public string GetCSharpCodeArguments() { return $"new {Name}" + $"(" + $"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " + $"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " + $"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " + $"{MatchOperator.GetFullyQualifiedEnumValue()}" + $")"; } }