diff --git a/src/DivertR/DiverterSettings.cs b/src/DivertR/DiverterSettings.cs index 8589b5e2..505e8c69 100644 --- a/src/DivertR/DiverterSettings.cs +++ b/src/DivertR/DiverterSettings.cs @@ -1,7 +1,5 @@ using DivertR.DispatchProxy; using DivertR.Dummy; -using DivertR.Dummy.Internal; -using DivertR.Internal; namespace DivertR { @@ -15,10 +13,8 @@ public class DiverterSettings public bool DefaultWithDummyRoot { get; } public IDummyFactory DummyFactory { get; } - - public IRedirectRepository DummyRedirectRepository { get; } - + public static DiverterSettings Global { get @@ -41,20 +37,11 @@ public static DiverterSettings Global public DiverterSettings( IProxyFactory? proxyFactory = null, bool defaultWithDummyRoot = true, - IRedirectRepository? dummyRedirectRepository = null, - IDummyFactory? defaultRootFactory = null) + IDummyFactory? dummyFactory = null) { ProxyFactory = proxyFactory ?? new DispatchProxyFactory(); DefaultWithDummyRoot = defaultWithDummyRoot; - DummyRedirectRepository = dummyRedirectRepository ?? CreateDummyRepository(); - DummyFactory = defaultRootFactory ?? new DummyFactory(); - } - - private static IRedirectRepository CreateDummyRepository() - { - var redirect = new Redirect(new DummyCallHandler()); - - return new RedirectRepository(new[] { redirect }); + DummyFactory = dummyFactory ?? new DummyFactory(); } } } \ No newline at end of file diff --git a/src/DivertR/Dummy/DummyFactory.cs b/src/DivertR/Dummy/DummyFactory.cs new file mode 100644 index 00000000..b672c334 --- /dev/null +++ b/src/DivertR/Dummy/DummyFactory.cs @@ -0,0 +1,44 @@ +using System; +using System.Linq.Expressions; +using DivertR.Dummy.Internal; +using DivertR.Internal; + +namespace DivertR.Dummy +{ + public class DummyFactory : IDummyFactory + { + public DummyFactory() + { + var redirect = new Redirect(new DummyCallHandler()); + RedirectRepository = new RedirectRepository(new[] { redirect }); + } + + public DummyFactory(IRedirectRepository redirectRepository) + { + RedirectRepository = redirectRepository; + } + + public TTarget Create<TTarget>(DiverterSettings diverterSettings) where TTarget : class + { + var via = new Via<TTarget>(diverterSettings, RedirectRepository); + + return via.Proxy(false); + } + + public IRedirectRepository RedirectRepository { get; } + + public IDummyBuilder<TReturn> To<TReturn>(Expression<Func<TReturn>> constraintExpression) + { + var redirectBuilder = RedirectBuilder.To(constraintExpression); + + return new DummyBuilder<TReturn>(RedirectRepository, redirectBuilder); + } + + public IDummyBuilder To(ICallConstraint? callConstraint = null) + { + var redirectBuilder = RedirectBuilder.To(callConstraint); + + return new DummyBuilder(RedirectRepository, redirectBuilder); + } + } +} \ No newline at end of file diff --git a/src/DivertR/Dummy/IDummyBuilder.cs b/src/DivertR/Dummy/IDummyBuilder.cs new file mode 100644 index 00000000..1ac8d3c3 --- /dev/null +++ b/src/DivertR/Dummy/IDummyBuilder.cs @@ -0,0 +1,22 @@ +using System; + +namespace DivertR.Dummy +{ + public interface IDummyBuilder<TReturn> + { + IDummyBuilder<TReturn> AddConstraint(ICallConstraint callConstraint); + IDummyBuilder<TReturn> Redirect(TReturn instance, Action<IRedirectOptionsBuilder>? optionsAction = null); + IDummyBuilder<TReturn> Redirect(Func<TReturn> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null); + IDummyBuilder<TReturn> Redirect(Func<IFuncRedirectCall<TReturn>, TReturn> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null); + IDummyBuilder<TReturn> Redirect(Func<IFuncRedirectCall<TReturn>, CallArguments, TReturn> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null); + } + + public interface IDummyBuilder + { + IDummyBuilder AddConstraint(ICallConstraint callConstraint); + IDummyBuilder Redirect(object instance, Action<IRedirectOptionsBuilder>? optionsAction = null); + IDummyBuilder Redirect(Func<object> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null); + IDummyBuilder Redirect(Func<IRedirectCall, object> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null); + IDummyBuilder Redirect(Func<IRedirectCall, CallArguments, object> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null); + } +} \ No newline at end of file diff --git a/src/DivertR/Dummy/Internal/DummyBuilder.cs b/src/DivertR/Dummy/Internal/DummyBuilder.cs new file mode 100644 index 00000000..55a0e0e2 --- /dev/null +++ b/src/DivertR/Dummy/Internal/DummyBuilder.cs @@ -0,0 +1,106 @@ +using System; + +namespace DivertR.Dummy.Internal +{ + internal class DummyBuilder<TReturn> : IDummyBuilder<TReturn> + { + private readonly IRedirectRepository _redirectRepository; + private readonly IFuncRedirectBuilder<TReturn> _redirectBuilder; + + public DummyBuilder(IRedirectRepository redirectRepository, IFuncRedirectBuilder<TReturn> redirectBuilder) + { + _redirectRepository = redirectRepository; + _redirectBuilder = redirectBuilder; + } + + public IDummyBuilder<TReturn> AddConstraint(ICallConstraint callConstraint) + { + _redirectBuilder.AddConstraint(callConstraint); + + return this; + } + + public IDummyBuilder<TReturn> Redirect(TReturn instance, Action<IRedirectOptionsBuilder>? optionsAction = null) + { + var redirect = _redirectBuilder.Build(instance, optionsAction); + _redirectRepository.InsertRedirect(redirect); + + return this; + } + + public IDummyBuilder<TReturn> Redirect(Func<TReturn> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null) + { + var redirect = _redirectBuilder.Build(redirectDelegate, optionsAction); + _redirectRepository.InsertRedirect(redirect); + + return this; + } + + public IDummyBuilder<TReturn> Redirect(Func<IFuncRedirectCall<TReturn>, TReturn> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null) + { + var redirect = _redirectBuilder.Build(redirectDelegate, optionsAction); + _redirectRepository.InsertRedirect(redirect); + + return this; + } + + public IDummyBuilder<TReturn> Redirect(Func<IFuncRedirectCall<TReturn>, CallArguments, TReturn> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null) + { + var redirect = _redirectBuilder.Build(redirectDelegate, optionsAction); + _redirectRepository.InsertRedirect(redirect); + + return this; + } + } + + internal class DummyBuilder : IDummyBuilder + { + private readonly IRedirectRepository _redirectRepository; + private readonly IRedirectBuilder _redirectBuilder; + + public DummyBuilder(IRedirectRepository redirectRepository, IRedirectBuilder redirectBuilder) + { + _redirectRepository = redirectRepository; + _redirectBuilder = redirectBuilder; + } + + public IDummyBuilder AddConstraint(ICallConstraint callConstraint) + { + _redirectBuilder.AddConstraint(callConstraint); + + return this; + } + + public IDummyBuilder Redirect(object instance, Action<IRedirectOptionsBuilder>? optionsAction = null) + { + var redirect = _redirectBuilder.Build(instance, optionsAction); + _redirectRepository.InsertRedirect(redirect); + + return this; + } + + public IDummyBuilder Redirect(Func<object> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null) + { + var redirect = _redirectBuilder.Build(redirectDelegate, optionsAction); + _redirectRepository.InsertRedirect(redirect); + + return this; + } + + public IDummyBuilder Redirect(Func<IRedirectCall, object> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null) + { + var redirect = _redirectBuilder.Build(redirectDelegate, optionsAction); + _redirectRepository.InsertRedirect(redirect); + + return this; + } + + public IDummyBuilder Redirect(Func<IRedirectCall, CallArguments, object> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null) + { + var redirect = _redirectBuilder.Build(redirectDelegate, optionsAction); + _redirectRepository.InsertRedirect(redirect); + + return this; + } + } +} \ No newline at end of file diff --git a/src/DivertR/Dummy/Internal/DummyFactory.cs b/src/DivertR/Dummy/Internal/DummyFactory.cs deleted file mode 100644 index 2358955d..00000000 --- a/src/DivertR/Dummy/Internal/DummyFactory.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace DivertR.Dummy.Internal -{ - internal class DummyFactory : IDummyFactory - { - public TTarget Create<TTarget>(DiverterSettings diverterSettings) where TTarget : class - { - var via = new Via<TTarget>(diverterSettings, diverterSettings.DummyRedirectRepository); - - return via.Proxy(false); - } - } -} \ No newline at end of file diff --git a/src/DivertR/IActionViaBuilder.cs b/src/DivertR/IActionViaBuilder.cs index c4273a49..60b454e4 100644 --- a/src/DivertR/IActionViaBuilder.cs +++ b/src/DivertR/IActionViaBuilder.cs @@ -4,13 +4,13 @@ namespace DivertR { - public interface IActionViaBuilder<TTarget> : IDelegateViaBuilder<TTarget> where TTarget : class + public interface IActionViaBuilder<TTarget> : IViaBuilder<TTarget> where TTarget : class { new IActionRedirectBuilder<TTarget> RedirectBuilder { get; } new IActionViaBuilder<TTarget> AddConstraint(ICallConstraint<TTarget> callConstraint); - new IActionViaBuilder<TTarget> Redirect(Delegate redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); + IActionViaBuilder<TTarget> Redirect(Delegate redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); IActionViaBuilder<TTarget> Redirect(Action redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); IActionViaBuilder<TTarget> Redirect(Action<IActionRedirectCall<TTarget>> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); IActionViaBuilder<TTarget> Redirect(Action<IActionRedirectCall<TTarget>, CallArguments> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); diff --git a/src/DivertR/IDelegateViaBuilder.cs b/src/DivertR/IDelegateViaBuilder.cs deleted file mode 100644 index bfbf7bdc..00000000 --- a/src/DivertR/IDelegateViaBuilder.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace DivertR -{ - public interface IDelegateViaBuilder<TTarget> : IViaBuilder<TTarget> where TTarget : class - { - new IDelegateRedirectBuilder<TTarget> RedirectBuilder { get; } - - IDelegateViaBuilder<TTarget> Redirect(Delegate redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); - } -} diff --git a/src/DivertR/IFuncViaBuilder.cs b/src/DivertR/IFuncViaBuilder.cs index 113cc706..b521c7d7 100644 --- a/src/DivertR/IFuncViaBuilder.cs +++ b/src/DivertR/IFuncViaBuilder.cs @@ -4,13 +4,13 @@ namespace DivertR { - public interface IFuncViaBuilder<TTarget, TReturn> : IDelegateViaBuilder<TTarget> where TTarget : class + public interface IFuncViaBuilder<TTarget, TReturn> : IViaBuilder<TTarget> where TTarget : class { new IFuncRedirectBuilder<TTarget, TReturn> RedirectBuilder { get; } new IFuncViaBuilder<TTarget, TReturn> AddConstraint(ICallConstraint<TTarget> callConstraint); - new IFuncViaBuilder<TTarget, TReturn> Redirect(Delegate redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); + IFuncViaBuilder<TTarget, TReturn> Redirect(Delegate redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); IFuncViaBuilder<TTarget, TReturn> Redirect(TReturn instance, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); IFuncViaBuilder<TTarget, TReturn> Redirect(Func<TReturn> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); IFuncViaBuilder<TTarget, TReturn> Redirect(Func<IFuncRedirectCall<TTarget, TReturn>, TReturn> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); diff --git a/src/DivertR/IRedirectBuilder.cs b/src/DivertR/IRedirectBuilder.cs index bb758c2c..57a6772e 100644 --- a/src/DivertR/IRedirectBuilder.cs +++ b/src/DivertR/IRedirectBuilder.cs @@ -6,7 +6,10 @@ namespace DivertR public interface IRedirectBuilder<TTarget> where TTarget : class { IRedirectBuilder<TTarget> AddConstraint(ICallConstraint<TTarget> callConstraint); - IRedirect Build(TTarget target, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); + IRedirect Build(object? instance, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); + IRedirect Build(Func<object?> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); + IRedirect Build(Func<IRedirectCall<TTarget>, object?> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); + IRedirect Build(Func<IRedirectCall<TTarget>, CallArguments, object?> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); IRedirect Build(ICallHandler<TTarget> callHandler, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); IRedirect Build(ICallHandler<TTarget> callHandler, IRedirectOptions redirectOptions); IRecordRedirect<TTarget> Record(Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); @@ -15,7 +18,10 @@ public interface IRedirectBuilder<TTarget> where TTarget : class public interface IRedirectBuilder { IRedirectBuilder AddConstraint(ICallConstraint callConstraint); - IRedirect Build(object target, Action<IRedirectOptionsBuilder>? optionsAction = null); + IRedirect Build(object? instance, Action<IRedirectOptionsBuilder>? optionsAction = null); + IRedirect Build(Func<object?> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null); + IRedirect Build(Func<IRedirectCall, object?> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null); + IRedirect Build(Func<IRedirectCall, CallArguments, object?> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null); IRedirect Build(ICallHandler callHandler, Action<IRedirectOptionsBuilder>? optionsAction = null); IRedirect Build(ICallHandler callHandler, IRedirectOptions redirectOptions); IRecordRedirect Record(Action<IRedirectOptionsBuilder>? optionsAction = null); diff --git a/src/DivertR/IViaBuilder.cs b/src/DivertR/IViaBuilder.cs index 7a91abf7..3c35756a 100644 --- a/src/DivertR/IViaBuilder.cs +++ b/src/DivertR/IViaBuilder.cs @@ -9,6 +9,10 @@ public interface IViaBuilder<TTarget> where TTarget : class IRedirectBuilder<TTarget> RedirectBuilder { get; } IViaBuilder<TTarget> AddConstraint(ICallConstraint<TTarget> callConstraint); + IViaBuilder<TTarget> Redirect(object? instance, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); + IViaBuilder<TTarget> Redirect(Func<object?> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); + IViaBuilder<TTarget> Redirect(Func<IRedirectCall<TTarget>, object?> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); + IViaBuilder<TTarget> Redirect(Func<IRedirectCall<TTarget>, CallArguments, object?> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); IViaBuilder<TTarget> Retarget(TTarget target, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); IRecordStream<TTarget> Record(Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null); } diff --git a/src/DivertR/Internal/ActionViaBuilder.cs b/src/DivertR/Internal/ActionViaBuilder.cs index 0052a9fb..9f2608c6 100644 --- a/src/DivertR/Internal/ActionViaBuilder.cs +++ b/src/DivertR/Internal/ActionViaBuilder.cs @@ -4,7 +4,7 @@ namespace DivertR.Internal { - internal class ActionViaBuilder<TTarget> : DelegateViaBuilder<TTarget>, IActionViaBuilder<TTarget> where TTarget : class + internal class ActionViaBuilder<TTarget> : ViaBuilder<TTarget>, IActionViaBuilder<TTarget> where TTarget : class { public ActionViaBuilder(IVia<TTarget> via, IActionRedirectBuilder<TTarget> redirectBuilder) : base(via, redirectBuilder) @@ -21,9 +21,10 @@ public ActionViaBuilder(IVia<TTarget> via, IActionRedirectBuilder<TTarget> redir return this; } - public new IActionViaBuilder<TTarget> Redirect(Delegate redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) + public IActionViaBuilder<TTarget> Redirect(Delegate redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) { - base.Redirect(redirectDelegate, optionsAction); + var redirect = RedirectBuilder.Build(redirectDelegate, optionsAction); + Via.RedirectRepository.InsertRedirect(redirect); return this; } diff --git a/src/DivertR/Internal/DelegateViaBuilder.cs b/src/DivertR/Internal/DelegateViaBuilder.cs deleted file mode 100644 index df11f285..00000000 --- a/src/DivertR/Internal/DelegateViaBuilder.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -namespace DivertR.Internal -{ - internal abstract class DelegateViaBuilder<TTarget> : ViaBuilder<TTarget>, IDelegateViaBuilder<TTarget> where TTarget : class - { - protected DelegateViaBuilder(IVia<TTarget> via, IDelegateRedirectBuilder<TTarget> redirectBuilder) - : base(via, redirectBuilder) - { - RedirectBuilder = redirectBuilder; - } - - public new IDelegateRedirectBuilder<TTarget> RedirectBuilder { get; } - - public IDelegateViaBuilder<TTarget> Redirect(Delegate redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) - { - var redirect = RedirectBuilder.Build(redirectDelegate, optionsAction); - Via.RedirectRepository.InsertRedirect(redirect); - - return this; - } - } -} diff --git a/src/DivertR/Internal/FuncViaBuilder.cs b/src/DivertR/Internal/FuncViaBuilder.cs index b553a80c..20278462 100644 --- a/src/DivertR/Internal/FuncViaBuilder.cs +++ b/src/DivertR/Internal/FuncViaBuilder.cs @@ -4,7 +4,7 @@ namespace DivertR.Internal { - internal class FuncViaBuilder<TTarget, TReturn> : DelegateViaBuilder<TTarget>, IFuncViaBuilder<TTarget, TReturn> where TTarget : class + internal class FuncViaBuilder<TTarget, TReturn> : ViaBuilder<TTarget>, IFuncViaBuilder<TTarget, TReturn> where TTarget : class { public FuncViaBuilder(IVia<TTarget> via, IFuncRedirectBuilder<TTarget, TReturn> redirectBuilder) : base(via, redirectBuilder) @@ -21,9 +21,10 @@ public FuncViaBuilder(IVia<TTarget> via, IFuncRedirectBuilder<TTarget, TReturn> return this; } - public new IFuncViaBuilder<TTarget, TReturn> Redirect(Delegate redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) + public IFuncViaBuilder<TTarget, TReturn> Redirect(Delegate redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) { - base.Redirect(redirectDelegate, optionsAction); + var redirect = RedirectBuilder.Build(redirectDelegate, optionsAction); + Via.RedirectRepository.InsertRedirect(redirect); return this; } diff --git a/src/DivertR/Internal/RedirectBuilder.cs b/src/DivertR/Internal/RedirectBuilder.cs index 05a63b8b..8cbd1987 100644 --- a/src/DivertR/Internal/RedirectBuilder.cs +++ b/src/DivertR/Internal/RedirectBuilder.cs @@ -23,13 +23,30 @@ public IRedirectBuilder<TTarget> AddConstraint(ICallConstraint<TTarget> callCons return this; } - public IRedirect Build(TTarget target, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) + public IRedirect Build(object? instance, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) { - ICallHandler<TTarget> callHandler = new TargetCallHandler<TTarget>(target); + return Build(call => instance, optionsAction); + } + + public IRedirect Build(Func<object?> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) + { + return Build(call => redirectDelegate.Invoke(), optionsAction); + } + public IRedirect Build(Func<IRedirectCall<TTarget>, object?> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) + { + var callHandler = new RedirectCallHandler<TTarget>(redirectDelegate); + return Build(callHandler, optionsAction); } - + + public IRedirect Build(Func<IRedirectCall<TTarget>, CallArguments, object?> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) + { + var callHandler = new RedirectArgsCallHandler<TTarget>(redirectDelegate); + + return Build(callHandler, optionsAction); + } + public IRedirect Build(ICallHandler<TTarget> callHandler, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) { var builder = new RedirectOptionsBuilder<TTarget>(); @@ -58,7 +75,7 @@ public IRecordRedirect<TTarget> Record(Action<IRedirectOptionsBuilder<TTarget>>? internal class RedirectBuilder : IRedirectBuilder { - protected CompositeCallConstraint CallConstraint { get; private set; } = CompositeCallConstraint.Empty; + private CompositeCallConstraint CallConstraint { get; set; } = CompositeCallConstraint.Empty; public RedirectBuilder(ICallConstraint? callConstraint = null) { @@ -75,13 +92,30 @@ public IRedirectBuilder AddConstraint(ICallConstraint callConstraint) return this; } - public IRedirect Build(object target, Action<IRedirectOptionsBuilder>? optionsAction = null) + public IRedirect Build(object? instance, Action<IRedirectOptionsBuilder>? optionsAction = null) { - var callHandler = new TargetCallHandler(target); + return Build(call => instance, optionsAction); + } + + public IRedirect Build(Func<object?> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null) + { + return Build(call => redirectDelegate.Invoke(), optionsAction); + } + public IRedirect Build(Func<IRedirectCall, object?> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null) + { + var callHandler = new RedirectCallHandler(redirectDelegate); + return Build(callHandler, optionsAction); } - + + public IRedirect Build(Func<IRedirectCall, CallArguments, object?> redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null) + { + var callHandler = new RedirectArgsCallHandler(redirectDelegate); + + return Build(callHandler, optionsAction); + } + public IRedirect Build(ICallHandler callHandler, Action<IRedirectOptionsBuilder>? optionsAction = null) { var builder = new RedirectOptionsBuilder(); diff --git a/src/DivertR/Internal/RedirectCallHandler.cs b/src/DivertR/Internal/RedirectCallHandler.cs index bd0ad2f6..0ee75b14 100644 --- a/src/DivertR/Internal/RedirectCallHandler.cs +++ b/src/DivertR/Internal/RedirectCallHandler.cs @@ -5,9 +5,9 @@ namespace DivertR.Internal { internal class RedirectCallHandler<TTarget> : ICallHandler<TTarget> where TTarget : class { - private readonly Action<IRedirectCall<TTarget>> _redirectDelegate; + private readonly Func<IRedirectCall<TTarget>, object?> _redirectDelegate; - public RedirectCallHandler(Action<IRedirectCall<TTarget>> redirectDelegate) + public RedirectCallHandler(Func<IRedirectCall<TTarget>, object?> redirectDelegate) { _redirectDelegate = redirectDelegate; } @@ -15,9 +15,55 @@ public RedirectCallHandler(Action<IRedirectCall<TTarget>> redirectDelegate) [MethodImpl(MethodImplOptions.AggressiveInlining)] public object? Handle(IRedirectCall<TTarget> call) { - _redirectDelegate.Invoke(call); + return _redirectDelegate.Invoke(call); + } + } + + internal class RedirectArgsCallHandler<TTarget> : ICallHandler<TTarget> where TTarget : class + { + private readonly Func<IRedirectCall<TTarget>, CallArguments, object?> _redirectDelegate; + + public RedirectArgsCallHandler(Func<IRedirectCall<TTarget>, CallArguments, object?> redirectDelegate) + { + _redirectDelegate = redirectDelegate; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object? Handle(IRedirectCall<TTarget> call) + { + return _redirectDelegate.Invoke(call, call.Args); + } + } + + internal class RedirectCallHandler : ICallHandler + { + private readonly Func<IRedirectCall, object?> _redirectDelegate; + + public RedirectCallHandler(Func<IRedirectCall, object?> redirectDelegate) + { + _redirectDelegate = redirectDelegate; + } - return default; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object? Handle(IRedirectCall call) + { + return _redirectDelegate.Invoke(call); + } + } + + internal class RedirectArgsCallHandler : ICallHandler + { + private readonly Func<IRedirectCall, CallArguments, object?> _redirectDelegate; + + public RedirectArgsCallHandler(Func<IRedirectCall, CallArguments, object?> redirectDelegate) + { + _redirectDelegate = redirectDelegate; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object? Handle(IRedirectCall call) + { + return _redirectDelegate.Invoke(call, call.Args); } } } \ No newline at end of file diff --git a/src/DivertR/Internal/ViaBuilder.cs b/src/DivertR/Internal/ViaBuilder.cs index 2af48245..8ad2b341 100644 --- a/src/DivertR/Internal/ViaBuilder.cs +++ b/src/DivertR/Internal/ViaBuilder.cs @@ -20,10 +20,43 @@ public IViaBuilder<TTarget> AddConstraint(ICallConstraint<TTarget> callConstrain return this; } - + + public IViaBuilder<TTarget> Redirect(object? instance, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) + { + var redirect = RedirectBuilder.Build(instance, optionsAction); + Via.RedirectRepository.InsertRedirect(redirect); + + return this; + } + + public IViaBuilder<TTarget> Redirect(Func<object?> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) + { + var redirect = RedirectBuilder.Build(redirectDelegate, optionsAction); + Via.RedirectRepository.InsertRedirect(redirect); + + return this; + } + + public IViaBuilder<TTarget> Redirect(Func<IRedirectCall<TTarget>, object?> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) + { + var redirect = RedirectBuilder.Build(redirectDelegate, optionsAction); + Via.RedirectRepository.InsertRedirect(redirect); + + return this; + } + + public IViaBuilder<TTarget> Redirect(Func<IRedirectCall<TTarget>, CallArguments, object?> redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) + { + var redirect = RedirectBuilder.Build(redirectDelegate, optionsAction); + Via.RedirectRepository.InsertRedirect(redirect); + + return this; + } + public IViaBuilder<TTarget> Retarget(TTarget target, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null) { - var redirect = RedirectBuilder.Build(target, optionsAction); + ICallHandler<TTarget> callHandler = new TargetCallHandler<TTarget>(target); + var redirect = RedirectBuilder.Build(callHandler, optionsAction); Via.RedirectRepository.InsertRedirect(redirect); return this; diff --git a/test/DivertR.UnitTests/DummyRootTests.cs b/test/DivertR.UnitTests/DummyRootTests.cs index 23348ab9..97dee939 100644 --- a/test/DivertR.UnitTests/DummyRootTests.cs +++ b/test/DivertR.UnitTests/DummyRootTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using DivertR.Dummy; using DivertR.UnitTests.Model; using Shouldly; using Xunit; @@ -11,7 +12,14 @@ namespace DivertR.UnitTests { public class DummyRootTests { - private readonly IVia<IFoo> _via = new Via<IFoo>(); + private readonly IVia<IFoo> _via; + private readonly DummyFactory _dummyFactory = new(); + + public DummyRootTests() + { + var settings = new DiverterSettings(dummyFactory: _dummyFactory); + _via = new Via<IFoo>(settings); + } [Fact] public void GivenDummyRootProxy_WhenStringPropertyGetterCalled_ShouldReturnNull() @@ -513,20 +521,18 @@ public void GivenDummyRootProxy_WhenCallHasTuple8Return_ShouldReturnTupleWithDef result.Item7.ShouldBe(Task.CompletedTask); result.Rest.Item1.ShouldBe(Task.CompletedTask); } - + [Fact] - public void GivenDummyRedirectRepositoryWithReturnTypeRedirect_WhenProxyMethodReturnTypeMatches_ShouldRedirect() + public void GivenDummyReturnTypeDelegateRedirect_WhenProxyMethodReturnTypeMatches_ShouldRedirect() { // ARRANGE - var redirect = RedirectBuilder + _dummyFactory .To(() => Is<string>.Return) - .Build(call => $"{call.Args.FirstOrDefault()} redirected".Trim()); + .Redirect(() => "redirected") + .Redirect(call => $"{call.CallNext()} call {call.Args.LastOrDefault()}".Trim()) + .Redirect((call, args) => $"{call.CallNext()} args {args.LastOrDefault()}".Trim()); - var diverterSettings = new DiverterSettings(); - diverterSettings.DummyRedirectRepository.InsertRedirect(redirect); - - var via = new Via<IFoo>(diverterSettings); - var proxy = via.Proxy(); + var proxy = _via.Proxy(); // ACT var result = proxy.EchoGeneric("hello"); @@ -534,9 +540,78 @@ public void GivenDummyRedirectRepositoryWithReturnTypeRedirect_WhenProxyMethodRe var objectReturn = proxy.EchoGeneric<object>("hello"); // ASSERT - result.ShouldBe("hello redirected"); - name.ShouldBe("redirected"); + result.ShouldBe("redirected call hello args hello"); + name.ShouldBe("redirected call args"); objectReturn.ShouldBeNull(); } + + [Fact] + public void GivenDummyReturnTypeInstanceRedirect_WhenProxyMethodReturnSubTypeMatches_ShouldNotRedirect() + { + // ARRANGE + _dummyFactory + .To(() => Is<object>.Return) + .Redirect("redirected"); + + var proxy = _via.Proxy(); + + // ACT + var result = proxy.EchoGeneric("hello"); + var name = proxy.Name; + var objectReturn = proxy.EchoGeneric<object>("hello"); + + // ASSERT + result.ShouldBeNull(); + name.ShouldBeNull(); + objectReturn.ShouldBe("redirected"); + } + + [Fact] + public void GivenDummyConstraintRedirect_WhenConstraintMatches_ShouldRedirect() + { + // ARRANGE + _dummyFactory + .To(new CallConstraint(call => call.Method.ReturnType.IsAssignableFrom(typeof(string)))) + .Redirect(() => "redirected") + .Redirect(call => $"{call.CallNext()} call {call.Args.LastOrDefault()}".Trim()) + .Redirect((call, args) => $"{call.CallNext()} args {args.LastOrDefault()}".Trim()); + + var proxy = _via.Proxy(); + + // ACT + var result = proxy.EchoGeneric("hello"); + var name = proxy.Name; + var objectReturn = proxy.EchoGeneric<object>("hello"); + var intReturn = proxy.EchoGeneric(1); + + // ASSERT + result.ShouldBe("redirected call hello args hello"); + name.ShouldBe("redirected call args"); + objectReturn.ShouldBe("redirected call hello args hello"); + intReturn.ShouldBe(0); + } + + [Fact] + public void GivenDummyConstraintInstanceRedirect_WhenConstraintMatches_ShouldRedirect() + { + // ARRANGE + _dummyFactory + .To(new CallConstraint(call => call.Method.ReturnType.IsAssignableFrom(typeof(string)))) + .Redirect("redirected"); + + var proxy = _via.Proxy(); + + // ACT + var result = proxy.EchoGeneric("hello"); + var name = proxy.Name; + var objectReturn = proxy.EchoGeneric<object>("hello"); + var intReturn = proxy.EchoGeneric(1); + + // ASSERT + result.ShouldBe("redirected"); + name.ShouldBe("redirected"); + objectReturn.ShouldBe("redirected"); + intReturn.ShouldBe(0); + } } } \ No newline at end of file diff --git a/test/DivertR.UnitTests/ViaRedirectTests.cs b/test/DivertR.UnitTests/ViaRedirectTests.cs index 17cfab04..a464efe5 100644 --- a/test/DivertR.UnitTests/ViaRedirectTests.cs +++ b/test/DivertR.UnitTests/ViaRedirectTests.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using DivertR.UnitTests.Model; using Moq; using Shouldly; @@ -993,5 +994,53 @@ public void GivenMoqIsArgumentSyntax_ShouldThrowException() // ASSERT testAction.ShouldThrow<ArgumentException>(); } + + [Fact] + public void GivenConstraintRedirect_WhenConstraintMatches_ShouldRedirect() + { + // ARRANGE + _via + .To(new CallConstraint<IFoo>(call => call.Method.ReturnType.IsAssignableFrom(typeof(string)))) + .Redirect(() => "redirected") + .Redirect(call => $"{call.CallNext()} call {call.Args.LastOrDefault()}".Trim()) + .Redirect((call, args) => $"{call.CallNext()} args {args.LastOrDefault()}".Trim()); + + var proxy = _via.Proxy(); + + // ACT + var result = proxy.EchoGeneric("hello"); + var name = proxy.Name; + var objectReturn = proxy.EchoGeneric<object>("hello"); + var intReturn = proxy.EchoGeneric(1); + + // ASSERT + result.ShouldBe("redirected call hello args hello"); + name.ShouldBe("redirected call args"); + objectReturn.ShouldBe("redirected call hello args hello"); + intReturn.ShouldBe(0); + } + + [Fact] + public void GivenConstraintInstanceRedirect_WhenConstraintMatches_ShouldRedirect() + { + // ARRANGE + _via + .To(new CallConstraint<IFoo>(call => call.Method.ReturnType.IsAssignableFrom(typeof(string)))) + .Redirect("redirected"); + + var proxy = _via.Proxy(); + + // ACT + var result = proxy.EchoGeneric("hello"); + var name = proxy.Name; + var objectReturn = proxy.EchoGeneric<object>("hello"); + var intReturn = proxy.EchoGeneric(1); + + // ASSERT + result.ShouldBe("redirected"); + name.ShouldBe("redirected"); + objectReturn.ShouldBe("redirected"); + intReturn.ShouldBe(0); + } } } \ No newline at end of file