// Copyright © WireMock.Net using System; using System.Linq; using System.Reflection; using System.Text; using AnyOfTypes; using Newtonsoft.Json.Linq; using Stef.Validation; using WireMock.Exceptions; using WireMock.Extensions; using WireMock.Models; using WireMock.Util; namespace WireMock.Matchers; /// /// CSharpCode / CS-Script Matcher /// /// public class CSharpCodeMatcher : ICSharpCodeMatcher { private const string TemplateForIsMatchWithString = "public class CodeHelper {{ public bool IsMatch(string it) {{ {0} }} }}"; private const string TemplateForIsMatchWithDynamic = "public class CodeHelper {{ public bool IsMatch(dynamic it) {{ {0} }} }}"; private readonly string[] _usings = [ "System", "System.Linq", "System.Collections.Generic", "Microsoft.CSharp", "Newtonsoft.Json.Linq" ]; /// public MatchBehaviour MatchBehaviour { get; } /// public object Value { get; } private readonly AnyOf[] _patterns; /// /// Initializes a new instance of the class. /// /// The patterns. public CSharpCodeMatcher(params AnyOf[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns) { } /// /// Initializes a new instance of the class. /// /// The match behaviour. /// The to use. (default = "Or") /// The patterns. public CSharpCodeMatcher(MatchBehaviour matchBehaviour, MatchOperator matchOperator = MatchOperator.Or, params AnyOf[] patterns) { _patterns = Guard.NotNull(patterns); MatchBehaviour = matchBehaviour; MatchOperator = matchOperator; Value = patterns; } /// public MatchResult IsMatch(string? input) => IsMatchInternal(input); /// public MatchResult IsMatch(object? input) => IsMatchInternal(input); /// public string GetCSharpCodeArguments() { return $"new {Name}" + $"(" + $"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " + $"{MatchOperator.GetFullyQualifiedEnumValue()}, " + $"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" + $")"; } private MatchResult IsMatchInternal(object? input) { var score = MatchScores.Mismatch; Exception? exception = null; if (input != null) { try { score = MatchScores.ToScore(_patterns.Select(pattern => IsMatch(input, pattern.GetPattern())).ToArray(), MatchOperator); } catch (Exception ex) { exception = ex; } } return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception); } private bool IsMatch(dynamic input, string pattern) { var isMatchWithString = input is string; var inputValue = isMatchWithString ? input : JObject.FromObject(input); var source = GetSourceForIsMatchWithString(pattern, isMatchWithString); object? result; //#if (NET451 || NET452) // var compilerParams = new System.CodeDom.Compiler.CompilerParameters // { // GenerateInMemory = true, // GenerateExecutable = false, // ReferencedAssemblies = // { // "System.dll", // "System.Core.dll", // "Microsoft.CSharp.dll", // "Newtonsoft.Json.dll" // } // }; // using (var codeProvider = new Microsoft.CSharp.CSharpCodeProvider()) // { // var compilerResults = codeProvider.CompileAssemblyFromSource(compilerParams, source); // if (compilerResults.Errors.Count != 0) // { // var errors = from System.CodeDom.Compiler.CompilerError er in compilerResults.Errors select er.ToString(); // throw new WireMockException(string.Join(", ", errors)); // } // var helper = compilerResults.CompiledAssembly?.CreateInstance("CodeHelper"); // if (helper == null) // { // throw new WireMockException("CSharpCodeMatcher: Unable to create instance from WireMock.CodeHelper"); // } // var methodInfo = helper.GetType().GetMethod("IsMatch"); // if (methodInfo == null) // { // throw new WireMockException("CSharpCodeMatcher: Unable to find method 'IsMatch' in WireMock.CodeHelper"); // } // try // { // result = methodInfo.Invoke(helper, new[] { inputValue }); // } // catch (Exception ex) // { // throw new WireMockException("CSharpCodeMatcher: Unable to call method 'IsMatch' in WireMock.CodeHelper", ex); // } // } //#elif (NET46 || NET461) // dynamic script; // try // { // script = CSScriptLibrary.CSScript.Evaluator.CompileCode(source).CreateObject("*"); // } // catch (Exception ex) // { // throw new WireMockException("CSharpCodeMatcher: Unable to create compiler for WireMock.CodeHelper", ex); // } // try // { // result = script.IsMatch(inputValue); // } // catch (Exception ex) // { // throw new WireMockException("CSharpCodeMatcher: Problem calling method 'IsMatch' in WireMock.CodeHelper", ex); // } //#elif (NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP3_1 || NET5_0_OR_GREATER || NET48) Assembly assembly; try { assembly = CSScriptLib.CSScript.Evaluator.CompileCode(source); } catch (Exception ex) { throw new WireMockException($"CSharpCodeMatcher: Unable to compile code `{source}` for WireMock.CodeHelper", ex); } dynamic script; try { script = CSScripting.ReflectionExtensions.CreateObject(assembly, "*"); } catch (Exception ex) { throw new WireMockException("CSharpCodeMatcher: Unable to create object from assembly", ex); } try { result = script.IsMatch(inputValue); } catch (Exception ex) { throw new WireMockException("CSharpCodeMatcher: Problem calling method 'IsMatch' in WireMock.CodeHelper", ex); } //#else // throw new NotSupportedException("The 'CSharpCodeMatcher' cannot be used in netstandard 1.3"); //#endif try { return (bool)result; } catch { throw new WireMockException($"Unable to cast result '{result}' to bool"); } } private string GetSourceForIsMatchWithString(string pattern, bool isMatchWithString) { var template = isMatchWithString ? TemplateForIsMatchWithString : TemplateForIsMatchWithDynamic; var stringBuilder = new StringBuilder(); foreach (var @using in _usings) { stringBuilder.AppendLine($"using {@using};"); } stringBuilder.AppendLine(); stringBuilder.AppendFormat(template, pattern); return stringBuilder.ToString(); } /// public AnyOf[] GetPatterns() { return _patterns; } /// public MatchOperator MatchOperator { get; } /// public string Name => nameof(CSharpCodeMatcher); }