Skip to content

Commit

Permalink
Merge pull request #5 from thedmi/fix/clarify-interface-generation
Browse files Browse the repository at this point in the history
Fix: clarify interface generation
  • Loading branch information
thedmi authored Feb 7, 2024
2 parents e757356 + 109aecb commit 6b69b6f
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 12 deletions.
22 changes: 16 additions & 6 deletions docs/docs/30_usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,25 @@ The following subsections provide further details on these two parts.

### Interface Generation

Capsule generator generates a capsule interface for each capsule implementation. By default, the interface has the implementation class' name with an "I" prefix. This can be customized through the `CapsuleAttribute.InterfaceName` property.
Capsule generator is able to generate a capsule interface for each capsule implementation.

Also, you can bring your own capsule interface. Capsule uses the following logic to decide interface generation:
By default, Capsule considers the list of implemented interfaces on the Capsule implementation:

1. If `CapsuleAttribute.GenerateInterface` is specified, that value determines if an interface is generated.
1. If the implementation implements exactly one interface that is not `[CapsuleIgnore]` attributed, that interface will be used and no additional interface will be generated.
1. Otherwise, the generator defaults to generating an interface.
- If there is exactly one interface that is not `[CapsuleIgnore]` attributed, that interface is used and no additional interface is generated.
- Otherwise, an interface with the same name as the implementation, but prefixed with an "I", will be generated.

If you provide the interface yourself, you'll need to ensure it matches the exposed methods and properties.
Interface generation can be customized through `CapsuleAttribute.InterfaceGeneration`:

- `Enable`: Generate an interface.
- `Disable`: Do not generate an interface.
- `Auto`: The default behavior described above applied.

The interface name can be customized through `CapsuleAttribute.InterfaceName`:

- If this property is specified and non-null, that interface name will be used.
- Otherwise, the default behavior applies.

If you bring your own interface, you'll need to ensure it matches the exposed methods and properties.


### Exposing Methods & Properties
Expand Down
2 changes: 1 addition & 1 deletion sources/Capsule.Core/Attribution/CapsuleAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ public sealed class CapsuleAttribute : Attribute
/// <summary>
/// Whether or not to generate the capsule interface.
/// </summary>
public bool GenerateInterface { get; init; } = true;
public CapsuleInterfaceGeneration InterfaceGeneration { get; init; } = CapsuleInterfaceGeneration.Auto;
}
19 changes: 19 additions & 0 deletions sources/Capsule.Core/Attribution/CapsuleInterfaceGeneration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Capsule.Attribution;

public enum CapsuleInterfaceGeneration
{
/// <summary>
/// Do not generate Capsule interface if there is a single candidate interface present. Generate otherwise.
/// </summary>
Auto,

/// <summary>
/// Do not generate Capsule interface.
/// </summary>
Disable,

/// <summary>
/// Generate Capsule interface.
/// </summary>
Enable
}
16 changes: 13 additions & 3 deletions sources/Capsule.Generator/CapsuleDefinitionResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ internal class CapsuleDefinitionResolver
{
private const string InterfaceNamePropertyName = "InterfaceName";

private const string GenerateInterfacePropertyName = "GenerateInterface";
private const string InterfaceGenerationPropertyName = "InterfaceGeneration";

public CapsuleDefinition GetCapsuleDefinition(INamedTypeSymbol classSymbol)
{
Expand All @@ -25,9 +25,19 @@ public CapsuleDefinition GetCapsuleDefinition(INamedTypeSymbol classSymbol)
var interfaceName = capsuleAttribute.GetProperty(InterfaceNamePropertyName)?.Value as string ??
singleInterface?.Name ?? "I" + classSymbol.Name;

var generateInterface = capsuleAttribute.GetProperty(GenerateInterfacePropertyName)?.Value as bool? ??
singleInterface == null;
var interfaceGeneration = DetermineInterfaceGeneration(capsuleAttribute) ??
(singleInterface == null
? InterfaceGeneration.Enable
: InterfaceGeneration.Disable);

var generateInterface = interfaceGeneration == InterfaceGeneration.Enable ||
(interfaceGeneration == InterfaceGeneration.Auto && singleInterface == null);

return new(interfaceName, generateInterface);
}

private static InterfaceGeneration? DetermineInterfaceGeneration(AttributeData capsuleAttribute) =>
capsuleAttribute.GetProperty(InterfaceGenerationPropertyName)?.Value is int intVal
? (InterfaceGeneration)intVal
: null;
}
10 changes: 10 additions & 0 deletions sources/Capsule.Generator/InterfaceGeneration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Capsule.Generator;

internal enum InterfaceGeneration
{
// Enum int values must match those in CapsuleInterfaceGeneration.

Auto,
Disable,
Enable
}
2 changes: 1 addition & 1 deletion sources/Capsule.Generator/Synchronization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
internal enum Synchronization
{
// Enum literals must match method names in CapsuleSynchronizer, and int values
// must match those in CapsulSynchronization.
// must match those in CapsuleSynchronization.

EnqueueAwaitResult,
EnqueueAwaitReception,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Capsule.Test.AutomatedTests.DeviceSample.Impl;

[Capsule(InterfaceName = nameof(IDevice), GenerateInterface = false)]
[Capsule(InterfaceName = nameof(IDevice), InterfaceGeneration = CapsuleInterfaceGeneration.Disable)]
public class FooDevice : IDevice, CapsuleFeature.IInitializer, CapsuleFeature.ITimers
{
// ReSharper disable once NotAccessedField.Local
Expand Down

0 comments on commit 6b69b6f

Please sign in to comment.