// 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()}" +
$")";
}
}