mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-01 06:33:11 +02:00
Create WireMock.Net.MimePart project (#1300)
* Create WireMock.Net.MimePart project * . * REFACTOR * ILRepack * -- * ... * x * x * . * fix * public class MimePartMatcher * shared * min * . * <!--<DelaySign>true</DelaySign>--> * Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
82
src/WireMock.Net.Shared/Matchers/ExactObjectMatcher.cs
Normal file
82
src/WireMock.Net.Shared/Matchers/ExactObjectMatcher.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// ExactObjectMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IObjectMatcher" />
|
||||
public class ExactObjectMatcher : IObjectMatcher
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object Value { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExactObjectMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public ExactObjectMatcher(object value) : this(MatchBehaviour.AcceptOnMatch, value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExactObjectMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public ExactObjectMatcher(MatchBehaviour matchBehaviour, object value)
|
||||
{
|
||||
Value = Guard.NotNull(value);
|
||||
MatchBehaviour = matchBehaviour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExactObjectMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public ExactObjectMatcher(byte[] value) : this(MatchBehaviour.AcceptOnMatch, value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExactObjectMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public ExactObjectMatcher(MatchBehaviour matchBehaviour, byte[] value)
|
||||
{
|
||||
Value = Guard.NotNull(value);
|
||||
MatchBehaviour = matchBehaviour;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(object? input)
|
||||
{
|
||||
bool equals;
|
||||
if (Value is byte[] valueAsBytes && input is byte[] inputAsBytes)
|
||||
{
|
||||
equals = valueAsBytes.SequenceEqual(inputAsBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
equals = Equals(Value, input);
|
||||
}
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(equals));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(ExactObjectMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return "NotImplemented";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using Stef.Validation;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers.Helpers;
|
||||
|
||||
internal static class BodyDataMatchScoreCalculator
|
||||
{
|
||||
internal static MatchResult CalculateMatchScore(IBodyData? requestMessage, IMatcher matcher)
|
||||
{
|
||||
Guard.NotNull(matcher);
|
||||
|
||||
if (requestMessage == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
if (matcher is NotNullOrEmptyMatcher notNullOrEmptyMatcher)
|
||||
{
|
||||
switch (requestMessage.DetectedBodyType)
|
||||
{
|
||||
case BodyType.Json:
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyAsString);
|
||||
|
||||
case BodyType.Bytes:
|
||||
return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyAsBytes);
|
||||
|
||||
default:
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
if (matcher is ExactObjectMatcher exactObjectMatcher)
|
||||
{
|
||||
// If the body is a byte array, try to match.
|
||||
var detectedBodyType = requestMessage.DetectedBodyType;
|
||||
if (detectedBodyType is BodyType.Bytes or BodyType.String or BodyType.FormUrlEncoded)
|
||||
{
|
||||
return exactObjectMatcher.IsMatch(requestMessage.BodyAsBytes);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the matcher is a IObjectMatcher
|
||||
if (matcher is IObjectMatcher objectMatcher)
|
||||
{
|
||||
// If the body is a JSON object, try to match.
|
||||
if (requestMessage.DetectedBodyType == BodyType.Json)
|
||||
{
|
||||
return objectMatcher.IsMatch(requestMessage.BodyAsJson);
|
||||
}
|
||||
|
||||
// If the body is a byte array, try to match.
|
||||
if (requestMessage.DetectedBodyType == BodyType.Bytes)
|
||||
{
|
||||
return objectMatcher.IsMatch(requestMessage.BodyAsBytes);
|
||||
}
|
||||
}
|
||||
|
||||
// In case the matcher is a IStringMatcher and If body is a Json or a String, use the BodyAsString to match on.
|
||||
if (matcher is IStringMatcher stringMatcher && requestMessage.DetectedBodyType is BodyType.Json or BodyType.String or BodyType.FormUrlEncoded)
|
||||
{
|
||||
return stringMatcher.IsMatch(requestMessage.BodyAsString);
|
||||
}
|
||||
|
||||
// In case the matcher is a IProtoBufMatcher, use the BodyAsBytes to match on.
|
||||
if (matcher is IProtoBufMatcher protoBufMatcher)
|
||||
{
|
||||
return protoBufMatcher.IsMatchAsync(requestMessage.BodyAsBytes).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
20
src/WireMock.Net.Shared/Matchers/IBytesMatcher.cs
Normal file
20
src/WireMock.Net.Shared/Matchers/IBytesMatcher.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IBytesMatcher
|
||||
/// </summary>
|
||||
public interface IBytesMatcher : IMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the specified input is match.
|
||||
/// </summary>
|
||||
/// <param name="input">The input byte array.</param>
|
||||
/// <param name="cancellationToken">The CancellationToken [optional].</param>
|
||||
/// <returns>MatchResult</returns>
|
||||
Task<MatchResult> IsMatchAsync(byte[]? input, CancellationToken cancellationToken = default);
|
||||
}
|
||||
12
src/WireMock.Net.Shared/Matchers/ICSharpCodeMatcher.cs
Normal file
12
src/WireMock.Net.Shared/Matchers/ICSharpCodeMatcher.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// CSharpCode / CS-Script Matcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IObjectMatcher"/>
|
||||
/// <inheritdoc cref="IStringMatcher"/>
|
||||
public interface ICSharpCodeMatcher : IObjectMatcher, IStringMatcher
|
||||
{
|
||||
}
|
||||
20
src/WireMock.Net.Shared/Matchers/IDecodeBytesMatcher.cs
Normal file
20
src/WireMock.Net.Shared/Matchers/IDecodeBytesMatcher.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IDecodeBytesMatcher
|
||||
/// </summary>
|
||||
public interface IDecodeBytesMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Decode byte array to an object.
|
||||
/// </summary>
|
||||
/// <param name="input">The byte array</param>
|
||||
/// <param name="cancellationToken">The CancellationToken [optional].</param>
|
||||
/// <returns>object</returns>
|
||||
Task<object?> DecodeAsync(byte[]? input, CancellationToken cancellationToken = default);
|
||||
}
|
||||
15
src/WireMock.Net.Shared/Matchers/IIgnoreCaseMatcher.cs
Normal file
15
src/WireMock.Net.Shared/Matchers/IIgnoreCaseMatcher.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IIgnoreCaseMatcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IMatcher"/>
|
||||
public interface IIgnoreCaseMatcher : IMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Ignore the case from the pattern.
|
||||
/// </summary>
|
||||
bool IgnoreCase { get; }
|
||||
}
|
||||
25
src/WireMock.Net.Shared/Matchers/IMatcher.cs
Normal file
25
src/WireMock.Net.Shared/Matchers/IMatcher.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IMatcher
|
||||
/// </summary>
|
||||
public interface IMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the match behaviour.
|
||||
/// </summary>
|
||||
MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the C# code arguments.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
string GetCSharpCodeArguments();
|
||||
}
|
||||
37
src/WireMock.Net.Shared/Matchers/IMimePartMatcher.cs
Normal file
37
src/WireMock.Net.Shared/Matchers/IMimePartMatcher.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// MimePartMatcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IMatcher"/>
|
||||
public interface IMimePartMatcher : IMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// ContentType Matcher (image/png; name=image.png.)
|
||||
/// </summary>
|
||||
IStringMatcher? ContentTypeMatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ContentDisposition Matcher (attachment; filename=image.png)
|
||||
/// </summary>
|
||||
IStringMatcher? ContentDispositionMatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ContentTransferEncoding Matcher (base64)
|
||||
/// </summary>
|
||||
IStringMatcher? ContentTransferEncodingMatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Content Matcher
|
||||
/// </summary>
|
||||
IMatcher? ContentMatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified MimePart is match.
|
||||
/// </summary>
|
||||
/// <param name="value">The MimePart.</param>
|
||||
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
|
||||
public MatchResult IsMatch(object value);
|
||||
}
|
||||
22
src/WireMock.Net.Shared/Matchers/IObjectMatcher.cs
Normal file
22
src/WireMock.Net.Shared/Matchers/IObjectMatcher.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IObjectMatcher
|
||||
/// </summary>
|
||||
public interface IObjectMatcher : IMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the value (can be a string or an object).
|
||||
/// </summary>
|
||||
/// <returns>Value</returns>
|
||||
object Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified input is match.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <returns>MatchResult</returns>
|
||||
MatchResult IsMatch(object? input);
|
||||
}
|
||||
10
src/WireMock.Net.Shared/Matchers/IProtoBufMatcher.cs
Normal file
10
src/WireMock.Net.Shared/Matchers/IProtoBufMatcher.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IProtoBufMatcher
|
||||
/// </summary>
|
||||
public interface IProtoBufMatcher : IDecodeBytesMatcher, IBytesMatcher
|
||||
{
|
||||
}
|
||||
31
src/WireMock.Net.Shared/Matchers/IStringMatcher.cs
Normal file
31
src/WireMock.Net.Shared/Matchers/IStringMatcher.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using AnyOfTypes;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IStringMatcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IMatcher"/>
|
||||
public interface IStringMatcher : IMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the specified input is match.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <returns>MatchResult</returns>
|
||||
MatchResult IsMatch(string? input);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the patterns.
|
||||
/// </summary>
|
||||
/// <returns>Patterns</returns>
|
||||
AnyOf<string, StringPattern>[] GetPatterns();
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Matchers.MatchOperator"/>.
|
||||
/// </summary>
|
||||
MatchOperator MatchOperator { get; }
|
||||
}
|
||||
19
src/WireMock.Net.Shared/Matchers/MatchBehaviour.cs
Normal file
19
src/WireMock.Net.Shared/Matchers/MatchBehaviour.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// MatchBehaviour (Accept or Reject)
|
||||
/// </summary>
|
||||
public enum MatchBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Accept on match (default)
|
||||
/// </summary>
|
||||
AcceptOnMatch,
|
||||
|
||||
/// <summary>
|
||||
/// Reject on match
|
||||
/// </summary>
|
||||
RejectOnMatch
|
||||
}
|
||||
41
src/WireMock.Net.Shared/Matchers/MatchBehaviourHelper.cs
Normal file
41
src/WireMock.Net.Shared/Matchers/MatchBehaviourHelper.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// MatchBehaviourHelper
|
||||
/// </summary>
|
||||
internal static class MatchBehaviourHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the specified match behaviour and match value to a new match value.
|
||||
///
|
||||
/// if AcceptOnMatch --> return match (default)
|
||||
/// if RejectOnMatch and match = 0.0 --> return 1.0
|
||||
/// if RejectOnMatch and match = 0.? --> return 0.0
|
||||
/// if RejectOnMatch and match = 1.0 --> return 0.0
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="match">The match.</param>
|
||||
/// <returns>match value</returns>
|
||||
internal static double Convert(MatchBehaviour matchBehaviour, double match)
|
||||
{
|
||||
if (matchBehaviour == MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return match;
|
||||
}
|
||||
|
||||
return match <= MatchScores.Tolerance ? MatchScores.Perfect : MatchScores.Mismatch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified match behaviour and match result to a new match result value.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="result">The match result.</param>
|
||||
/// <returns>match result</returns>
|
||||
internal static MatchResult Convert(MatchBehaviour matchBehaviour, MatchResult result)
|
||||
{
|
||||
return matchBehaviour == MatchBehaviour.AcceptOnMatch ? result : new MatchResult(Convert(matchBehaviour, result.Score), result.Exception);
|
||||
}
|
||||
}
|
||||
24
src/WireMock.Net.Shared/Matchers/MatchOperator.cs
Normal file
24
src/WireMock.Net.Shared/Matchers/MatchOperator.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// The Operator to use when multiple patterns are defined.
|
||||
/// </summary>
|
||||
public enum MatchOperator
|
||||
{
|
||||
/// <summary>
|
||||
/// Only one pattern needs to match. [Default]
|
||||
/// </summary>
|
||||
Or,
|
||||
|
||||
/// <summary>
|
||||
/// All patterns should match.
|
||||
/// </summary>
|
||||
And,
|
||||
|
||||
/// <summary>
|
||||
/// The average value from all patterns.
|
||||
/// </summary>
|
||||
Average
|
||||
}
|
||||
91
src/WireMock.Net.Shared/Matchers/MatchResult.cs
Normal file
91
src/WireMock.Net.Shared/Matchers/MatchResult.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// The MatchResult which contains the score (value between 0.0 - 1.0 of the similarity) and an optional error message.
|
||||
/// </summary>
|
||||
public struct MatchResult
|
||||
{
|
||||
/// <summary>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </summary>
|
||||
public double Score { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The exception message) in case the matching fails.
|
||||
/// [Optional]
|
||||
/// </summary>
|
||||
public Exception? Exception { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a MatchResult
|
||||
/// </summary>
|
||||
/// <param name="score">A value between 0.0 - 1.0 of the similarity.</param>
|
||||
/// <param name="exception">The exception in case the matching fails. [Optional]</param>
|
||||
public MatchResult(double score, Exception? exception = null)
|
||||
{
|
||||
Score = score;
|
||||
Exception = exception;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a MatchResult
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception in case the matching fails.</param>
|
||||
public MatchResult(Exception exception)
|
||||
{
|
||||
Exception = Guard.NotNull(exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts a double to a MatchResult.
|
||||
/// </summary>
|
||||
/// <param name="score">The score</param>
|
||||
public static implicit operator MatchResult(double score)
|
||||
{
|
||||
return new MatchResult(score);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the value a perfect match?
|
||||
/// </summary>
|
||||
public bool IsPerfect() => MatchScores.IsPerfect(Score);
|
||||
|
||||
/// <summary>
|
||||
/// Create a MatchResult from multiple MatchResults.
|
||||
/// </summary>
|
||||
/// <param name="matchResults">A list of MatchResults.</param>
|
||||
/// <param name="matchOperator">The MatchOperator</param>
|
||||
/// <returns>MatchResult</returns>
|
||||
public static MatchResult From(IReadOnlyList<MatchResult> matchResults, MatchOperator matchOperator)
|
||||
{
|
||||
Guard.NotNullOrEmpty(matchResults);
|
||||
|
||||
if (matchResults.Count == 1)
|
||||
{
|
||||
return matchResults[0];
|
||||
}
|
||||
|
||||
return new MatchResult
|
||||
{
|
||||
Score = MatchScores.ToScore(matchResults.Select(r => r.Score).ToArray(), matchOperator),
|
||||
Exception = matchResults.Select(m => m.Exception).OfType<Exception>().ToArray().ToException()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expand to Tuple
|
||||
/// </summary>
|
||||
/// <returns>Tuple : Score and Exception</returns>
|
||||
public (double Score, Exception? Exception) Expand()
|
||||
{
|
||||
return (Score, Exception);
|
||||
}
|
||||
}
|
||||
85
src/WireMock.Net.Shared/Matchers/MatchScores.cs
Normal file
85
src/WireMock.Net.Shared/Matchers/MatchScores.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// MatchScores
|
||||
/// </summary>
|
||||
public static class MatchScores
|
||||
{
|
||||
/// <summary>
|
||||
/// The tolerance
|
||||
/// </summary>
|
||||
public const double Tolerance = 0.000001;
|
||||
|
||||
/// <summary>
|
||||
/// The default mismatch score
|
||||
/// </summary>
|
||||
public const double Mismatch = 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// The default perfect match score
|
||||
/// </summary>
|
||||
public const double Perfect = 1.0;
|
||||
|
||||
/// <summary>
|
||||
/// The almost perfect match score
|
||||
/// </summary>
|
||||
public const double AlmostPerfect = 0.99;
|
||||
|
||||
/// <summary>
|
||||
/// Is the value a perfect match?
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>true/false</returns>
|
||||
public static bool IsPerfect(double value)
|
||||
{
|
||||
return Math.Abs(value - Perfect) < Tolerance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a bool to the score.
|
||||
/// </summary>
|
||||
/// <param name="value">if set to <c>true</c> [value].</param>
|
||||
/// <returns>score</returns>
|
||||
public static double ToScore(bool value)
|
||||
{
|
||||
return value ? Perfect : Mismatch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the score from multiple values.
|
||||
/// </summary>
|
||||
/// <param name="values">The values.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/>.</param>
|
||||
/// <returns>average score</returns>
|
||||
public static double ToScore(IReadOnlyCollection<bool> values, MatchOperator matchOperator)
|
||||
{
|
||||
return ToScore(values.Select(ToScore).ToArray(), matchOperator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the score from multiple values.
|
||||
/// </summary>
|
||||
/// <param name="values">The values.</param>
|
||||
/// <param name="matchOperator"></param>
|
||||
/// <returns>average score</returns>
|
||||
public static double ToScore(IReadOnlyCollection<double> values, MatchOperator matchOperator)
|
||||
{
|
||||
if (!values.Any())
|
||||
{
|
||||
return Mismatch;
|
||||
}
|
||||
|
||||
return matchOperator switch
|
||||
{
|
||||
MatchOperator.Or => ToScore(values.Any(IsPerfect)),
|
||||
MatchOperator.And => ToScore(values.All(IsPerfect)),
|
||||
_ => values.Average()
|
||||
};
|
||||
}
|
||||
}
|
||||
84
src/WireMock.Net.Shared/Matchers/NotNullOrEmptyMatcher.cs
Normal file
84
src/WireMock.Net.Shared/Matchers/NotNullOrEmptyMatcher.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// NotNullOrEmptyMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IObjectMatcher" />
|
||||
public class NotNullOrEmptyMatcher : IObjectMatcher, IStringMatcher
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(NotNullOrEmptyMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotNullOrEmptyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
public NotNullOrEmptyMatcher(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
MatchBehaviour = matchBehaviour;
|
||||
Value = string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(object? input)
|
||||
{
|
||||
bool match;
|
||||
|
||||
switch (input)
|
||||
{
|
||||
case string @string:
|
||||
match = !string.IsNullOrEmpty(@string);
|
||||
break;
|
||||
|
||||
case byte[] bytes:
|
||||
match = bytes.Any();
|
||||
break;
|
||||
|
||||
default:
|
||||
match = input != null;
|
||||
break;
|
||||
}
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(string? input)
|
||||
{
|
||||
var match = !string.IsNullOrEmpty(input);
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator => MatchOperator.Or;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
144
src/WireMock.Net.Shared/Matchers/RegexMatcher.cs
Normal file
144
src/WireMock.Net.Shared/Matchers/RegexMatcher.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using AnyOfTypes;
|
||||
using JetBrains.Annotations;
|
||||
using Stef.Validation;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
using WireMock.RegularExpressions;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// Regular Expression Matcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IStringMatcher"/>
|
||||
/// <inheritdoc cref="IIgnoreCaseMatcher"/>
|
||||
public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
private readonly Regex[] _expressions;
|
||||
private readonly bool _useRegexExtended;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public RegexMatcher(
|
||||
[RegexPattern] AnyOf<string, StringPattern> pattern,
|
||||
bool ignoreCase = false,
|
||||
bool useRegexExtended = true,
|
||||
MatchOperator matchOperator = MatchOperator.Or) :
|
||||
this(MatchBehaviour.AcceptOnMatch, [pattern], ignoreCase, useRegexExtended, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public RegexMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
[RegexPattern] AnyOf<string, StringPattern> pattern,
|
||||
bool ignoreCase = false,
|
||||
bool useRegexExtended = true,
|
||||
MatchOperator matchOperator = MatchOperator.Or) :
|
||||
this(matchBehaviour, [pattern], ignoreCase, useRegexExtended, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public RegexMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
[RegexPattern] AnyOf<string, StringPattern>[] patterns,
|
||||
bool ignoreCase = false,
|
||||
bool useRegexExtended = true,
|
||||
MatchOperator matchOperator = MatchOperator.Or)
|
||||
{
|
||||
_patterns = Guard.NotNull(patterns);
|
||||
IgnoreCase = ignoreCase;
|
||||
_useRegexExtended = useRegexExtended;
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
|
||||
var options = RegexOptions.Compiled | RegexOptions.Multiline;
|
||||
|
||||
if (ignoreCase)
|
||||
{
|
||||
options |= RegexOptions.IgnoreCase;
|
||||
}
|
||||
|
||||
_expressions = patterns.Select(p => useRegexExtended ? new RegexExtended(p.GetPattern(), options) : new Regex(p.GetPattern(), options, RegexConstants.DefaultTimeout)).ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual MatchResult IsMatch(string? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? exception = null;
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
score = MatchScores.ToScore(_expressions.Select(e => e.IsMatch(input)).ToArray(), MatchOperator);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string Name => nameof(RegexMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IgnoreCase { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(_useRegexExtended)}, " +
|
||||
$"{MatchOperator.GetFullyQualifiedEnumValue()}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
97
src/WireMock.Net.Shared/Matchers/WildcardMatcher.cs
Normal file
97
src/WireMock.Net.Shared/Matchers/WildcardMatcher.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using AnyOfTypes;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// WildcardMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="RegexMatcher" />
|
||||
public class WildcardMatcher : RegexMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase</param>
|
||||
public WildcardMatcher(AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase</param>
|
||||
public WildcardMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase</param>
|
||||
public WildcardMatcher(AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
|
||||
public WildcardMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
AnyOf<string, StringPattern>[] patterns,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or) : base(matchBehaviour, CreateArray(patterns), ignoreCase, true, matchOperator)
|
||||
{
|
||||
_patterns = Guard.NotNull(patterns);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => nameof(WildcardMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||
$"{MatchOperator.GetFullyQualifiedEnumValue()}" +
|
||||
$")";
|
||||
}
|
||||
|
||||
private static AnyOf<string, StringPattern>[] CreateArray(AnyOf<string, StringPattern>[] patterns)
|
||||
{
|
||||
return patterns
|
||||
.Select(pattern => new AnyOf<string, StringPattern>(
|
||||
new StringPattern
|
||||
{
|
||||
Pattern = "^" + Regex.Escape(pattern.GetPattern()).Replace(@"\*", ".*").Replace(@"\?", ".") + "$",
|
||||
PatternAsFile = pattern.IsSecond ? pattern.Second.PatternAsFile : null
|
||||
}))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user