Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add support for non-static Handle method
  • Loading branch information
viceroypenguin committed Jul 18, 2025
commit 782dd696b936d8f654d7974cbb7c98d28fd8515e
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ Template template

handler.MethodName,
HandlerParameters = handler.Parameters,
handler.IsStatic,
handler.UseToken,

RequestType = handler.RequestType.Name,
Expand Down
1 change: 1 addition & 0 deletions src/Immediate.Handlers.Generators/Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public sealed record Handler

public required string MethodName { get; init; }
public required EquatableReadOnlyList<Parameter> Parameters { get; init; }
public required bool IsStatic { get; init; }
public required bool UseToken { get; init; }

public required GenericType RequestType { get; init; }
Expand Down
46 changes: 46 additions & 0 deletions src/Immediate.Handlers.Generators/Templates/Handler.sbntxt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ partial class {{ class_name }}
}
}

{{~ if is_static ~}}
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class HandleBehavior : global::Immediate.Handlers.Shared.Behavior<{{ request_type }}, {{ response_type }}>
{
Expand Down Expand Up @@ -111,6 +112,48 @@ partial class {{ class_name }}
{{~ end ~}}
}
}
{{~ else ~}}
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class HandleBehavior : global::Immediate.Handlers.Shared.Behavior<{{ request_type }}, {{ response_type }}>
{
private readonly {{ class_fully_qualified_name }} _container;

public HandleBehavior(
{{ class_fully_qualified_name }} container
)
{
_container = container;
}

public override async global::System.Threading.Tasks.ValueTask<{{ response_type }}> HandleAsync(
{{ request_type }} request,
global::System.Threading.CancellationToken cancellationToken
)
{
{{~ if !is_implicit_value_tuple ~}}
return await _container
.{{ method_name }}(
request
{{~ if use_token ~}}
, cancellationToken
{{~ end ~}}
)
.ConfigureAwait(false);
{{~ else ~}}
await _container
.{{ method_name }}(
request
{{~ if use_token ~}}
, cancellationToken
{{~ end ~}}
)
.ConfigureAwait(false);

return default;
{{~ end ~}}
}
}
{{~ end ~}}
{{~ if has_ms_di ~}}

[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
Expand All @@ -122,6 +165,9 @@ partial class {{ class_name }}
services.Add(new(typeof({{ class_fully_qualified_name }}.Handler), typeof({{ class_fully_qualified_name }}.Handler), lifetime));
services.Add(new(typeof(global::Immediate.Handlers.Shared.IHandler<{{ request_type }}, {{ response_type }}>), typeof({{ class_fully_qualified_name }}.Handler), lifetime));
services.Add(new(typeof({{ class_fully_qualified_name }}.HandleBehavior), typeof({{ class_fully_qualified_name }}.HandleBehavior), lifetime));
{{~ if !is_static ~}}
services.Add(new(typeof({{ class_fully_qualified_name }}), typeof({{ class_fully_qualified_name }}), lifetime));
{{~ end ~}}
return services;
}
{{~ end ~}}
Expand Down
33 changes: 23 additions & 10 deletions src/Immediate.Handlers.Generators/TransformHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,44 +19,54 @@ CancellationToken cancellationToken
var displayName = symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

cancellationToken.ThrowIfCancellationRequested();

if (symbol.ContainingType is not null)
return null;

if (symbol
.GetMembers()
.OfType<IMethodSymbol>()
.Where(m => m.IsStatic)
.Where(m => m.Name is "Handle" or "HandleAsync")
.ToList() is not [var handleMethod])
{
return null;
}

// must have request type and cancellation token
if (handleMethod.Parameters is [])
return null;

if (handleMethod.ReturnsVoid)
if (handleMethod
// must have request type and cancellation token
is { Parameters: [] }
// void means not a valuetask return
or { ReturnsVoid: true }
// only private methods are considered
or { DeclaredAccessibility: not Accessibility.Private })
{
return null;
}

cancellationToken.ThrowIfCancellationRequested();

var requestType = BuildGenericType(handleMethod.Parameters[0].Type);
var isStatic = handleMethod.IsStatic;
var useToken = handleMethod.Parameters[^1]
.Type.IsCancellationToken();

// non-static methods should not have parameters
if (!isStatic && handleMethod.Parameters.Length > (useToken ? 2 : 1))
return null;

cancellationToken.ThrowIfCancellationRequested();

var responseTypeSymbol = handleMethod.GetTaskReturnType();
if (responseTypeSymbol is null)
if (responseTypeSymbol is null
&& !handleMethod.ReturnType.IsValueTask())
{
cancellationToken.ThrowIfCancellationRequested();
if (!handleMethod.ReturnType.IsValueTask())
return null;
return null;
}

var responseType = BuildGenericType(responseTypeSymbol);

cancellationToken.ThrowIfCancellationRequested();

var parameters = handleMethod.Parameters
.Skip(1)
.Take(handleMethod.Parameters.Length - (useToken ? 2 : 1))
Expand All @@ -69,9 +79,11 @@ CancellationToken cancellationToken
.ToEquatableReadOnlyList();

cancellationToken.ThrowIfCancellationRequested();

var behaviors = GetOverrideBehaviors(symbol, cancellationToken);

cancellationToken.ThrowIfCancellationRequested();

return new()
{
Namespace = @namespace,
Expand All @@ -80,6 +92,7 @@ CancellationToken cancellationToken

MethodName = handleMethod.Name,
Parameters = parameters,
IsStatic = isStatic,
UseToken = useToken,

RequestType = requestType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,23 @@ public Handler(
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class HandleBehavior : global::Immediate.Handlers.Shared.Behavior<global::Dummy.GetUsersQuery.Query, global::System.Collections.Generic.IEnumerable<global::Dummy.User>>
{
private readonly global::Dummy.UsersService _usersService;
private readonly global::Dummy.GetUsersQuery _container;

public HandleBehavior(
global::Dummy.UsersService usersService
global::Dummy.GetUsersQuery container
)
{
_usersService = usersService;
_container = container;
}

public override async global::System.Threading.Tasks.ValueTask<global::System.Collections.Generic.IEnumerable<global::Dummy.User>> HandleAsync(
global::Dummy.GetUsersQuery.Query request,
global::System.Threading.CancellationToken cancellationToken
)
{
return await global::Dummy.GetUsersQuery
return await _container
.HandleAsync(
request
, _usersService
, cancellationToken
)
.ConfigureAwait(false);
Expand All @@ -74,6 +73,7 @@ public static IServiceCollection AddHandlers(
services.Add(new(typeof(global::Dummy.GetUsersQuery.Handler), typeof(global::Dummy.GetUsersQuery.Handler), lifetime));
services.Add(new(typeof(global::Immediate.Handlers.Shared.IHandler<global::Dummy.GetUsersQuery.Query, global::System.Collections.Generic.IEnumerable<global::Dummy.User>>), typeof(global::Dummy.GetUsersQuery.Handler), lifetime));
services.Add(new(typeof(global::Dummy.GetUsersQuery.HandleBehavior), typeof(global::Dummy.GetUsersQuery.HandleBehavior), lifetime));
services.Add(new(typeof(global::Dummy.GetUsersQuery), typeof(global::Dummy.GetUsersQuery), lifetime));
return services;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,23 @@ public Handler(
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class HandleBehavior : global::Immediate.Handlers.Shared.Behavior<global::Dummy.GetUsersQuery.Query, global::System.Collections.Generic.IEnumerable<global::Dummy.User>>
{
private readonly global::Dummy.UsersService _usersService;
private readonly global::Dummy.GetUsersQuery _container;

public HandleBehavior(
global::Dummy.UsersService usersService
global::Dummy.GetUsersQuery container
)
{
_usersService = usersService;
_container = container;
}

public override async global::System.Threading.Tasks.ValueTask<global::System.Collections.Generic.IEnumerable<global::Dummy.User>> HandleAsync(
global::Dummy.GetUsersQuery.Query request,
global::System.Threading.CancellationToken cancellationToken
)
{
return await global::Dummy.GetUsersQuery
return await _container
.HandleAsync(
request
, _usersService
, cancellationToken
)
.ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,12 @@ public ValueTask<IEnumerable<User>> GetUsers() =>
}

[Handler]
public static partial class GetUsersQuery
public partial class GetUsersQuery(UsersService usersService)
{
public record Query;

private static ValueTask<IEnumerable<User>> HandleAsync(
private ValueTask<IEnumerable<User>> HandleAsync(
Query _,
UsersService usersService,
CancellationToken token)
{
return usersService.GetUsers();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//HintName: IH.Dummy.GetUsersQuery.g.cs
#pragma warning disable CS1591

namespace Dummy;

partial class GetUsersQuery
{
public sealed partial class Handler : global::Immediate.Handlers.Shared.IHandler<global::Dummy.GetUsersQuery.Query, int>
{
private readonly global::Dummy.GetUsersQuery.HandleBehavior _handleBehavior;

public Handler(
global::Dummy.GetUsersQuery.HandleBehavior handleBehavior
)
{
var handlerType = typeof(GetUsersQuery);

_handleBehavior = handleBehavior;

}

public async global::System.Threading.Tasks.ValueTask<int> HandleAsync(
global::Dummy.GetUsersQuery.Query request,
global::System.Threading.CancellationToken cancellationToken = default
)
{
return await _handleBehavior
.HandleAsync(request, cancellationToken)
.ConfigureAwait(false);
}
}

[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class HandleBehavior : global::Immediate.Handlers.Shared.Behavior<global::Dummy.GetUsersQuery.Query, int>
{
private readonly global::Dummy.GetUsersQuery _container;

public HandleBehavior(
global::Dummy.GetUsersQuery container
)
{
_container = container;
}

public override async global::System.Threading.Tasks.ValueTask<int> HandleAsync(
global::Dummy.GetUsersQuery.Query request,
global::System.Threading.CancellationToken cancellationToken
)
{
return await _container
.HandleAsync(
request
, cancellationToken
)
.ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//HintName: IH.Dummy.GetUsersQuery.g.cs
#pragma warning disable CS1591

namespace Dummy;

partial class GetUsersQuery
{
public sealed partial class Handler : global::Immediate.Handlers.Shared.IHandler<global::Dummy.GetUsersQuery.Query, int>
{
private readonly global::Dummy.GetUsersQuery.HandleBehavior _handleBehavior;

public Handler(
global::Dummy.GetUsersQuery.HandleBehavior handleBehavior
)
{
var handlerType = typeof(GetUsersQuery);

_handleBehavior = handleBehavior;

}

public async global::System.Threading.Tasks.ValueTask<int> HandleAsync(
global::Dummy.GetUsersQuery.Query request,
global::System.Threading.CancellationToken cancellationToken = default
)
{
return await _handleBehavior
.HandleAsync(request, cancellationToken)
.ConfigureAwait(false);
}
}

[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class HandleBehavior : global::Immediate.Handlers.Shared.Behavior<global::Dummy.GetUsersQuery.Query, int>
{
private readonly global::Dummy.GetUsersQuery _container;

public HandleBehavior(
global::Dummy.GetUsersQuery container
)
{
_container = container;
}

public override async global::System.Threading.Tasks.ValueTask<int> HandleAsync(
global::Dummy.GetUsersQuery.Query request,
global::System.Threading.CancellationToken cancellationToken
)
{
return await _container
.HandleAsync(
request
)
.ConfigureAwait(false);
}
}
}
Loading
Loading