Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class AutomaticInterfaceGenerator : IIncrementalGenerator
public const string IgnoreAutomaticInterfaceAttributeName = "IgnoreAutomaticInterface";
public const string NamespaceParameterName = "namespaceName";
public const string InterfaceParameterName = "interfaceName";
public const string AsInternalParameterName = "asInternal";

public void Initialize(IncrementalGeneratorInitializationContext context)
{
Expand Down
42 changes: 2 additions & 40 deletions AutomaticInterface/AutomaticInterface/Builder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,12 @@ is not ClassDeclarationSyntax classSyntax
{
return string.Empty;
}
var generationAttribute = GetGenerationAttribute(typeSymbol);
var asInternal = GetAsInternal(generationAttribute);

var symbolDetails = GetSymbolDetails(typeSymbol, classSyntax);
var interfaceGenerator = new InterfaceBuilder(
symbolDetails.NamespaceName,
symbolDetails.InterfaceName,
asInternal
symbolDetails.AccessLevel
);

interfaceGenerator.AddClassDocumentation(GetDocumentationForClass(classSyntax));
Expand All @@ -80,43 +79,6 @@ is not ClassDeclarationSyntax classSyntax
return generatedCode;
}

private static AttributeData? GetGenerationAttribute(ISymbol typeSymbol)
{
return typeSymbol
.GetAttributes()
.FirstOrDefault(x =>
x.AttributeClass != null
&& x.AttributeClass.Name.Contains(AutomaticInterfaceGenerator.DefaultAttributeName)
);
}

private static string GetNameSpace(ISymbol typeSymbol, AttributeData? generationAttribute)
{
if (generationAttribute == null)
{
return typeSymbol.ContainingNamespace.ToDisplayString();
}

var customNs = generationAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString();

return string.IsNullOrWhiteSpace(customNs)
? typeSymbol.ContainingNamespace.ToDisplayString()
: customNs!;
}

private static bool GetAsInternal(AttributeData? generationAttribute)
{
if (generationAttribute == null)
{
return false;
}

var asInternal = (bool?)
generationAttribute.ConstructorArguments.Skip(2).FirstOrDefault().Value;

return asInternal.GetValueOrDefault();
}

private static GeneratedSymbolDetails GetSymbolDetails(
ITypeSymbol typeSymbol,
ClassDeclarationSyntax classSyntax
Expand Down
36 changes: 28 additions & 8 deletions AutomaticInterface/AutomaticInterface/GeneratedSymbolDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,31 @@ ClassDeclarationSyntax classSyntax
$"I{classSyntax.GetClassName()}"
);

private static string PrepareValue(
AttributeData? generationAttribute,
string key,
string defaultValue
)
/// <summary>
/// Determines the access level for the generated interface.
/// This property is derived from the presence of <see cref="AutomaticInterfaceGenerator.AsInternalParameterName"/>
/// that, if set, defines the interface as `internal`. Otherwise, the interface defaults to `public`.
/// </summary>
public string AccessLevel { get; } =
PrepareValue(
generationAttribute,
AutomaticInterfaceGenerator.AsInternalParameterName,
false
)
? "internal"
: "public";

/// <summary>
/// Prepares a value by retrieving it from an attribute's constructor arguments if available; otherwise, returns the provided default value.
/// </summary>
/// <typeparam name="T">The type of the value to prepare.</typeparam>
/// <param name="generationAttribute">The attribute data containing constructor arguments.</param>
/// <param name="key">The key to identify the relevant parameter in the constructor arguments.</param>
/// <param name="defaultValue">The default value to return if the attribute does not provide a value.</param>
/// <returns>
/// The retrieved value from the attribute constructor's arguments, or the provided default value if the key is not found.
/// </returns>
private static T PrepareValue<T>(AttributeData? generationAttribute, string key, T defaultValue)
{
var parameterSymbol = generationAttribute?.AttributeConstructor?.Parameters.SingleOrDefault(
x => x.Name == key
Expand All @@ -49,10 +69,10 @@ string defaultValue
var index = generationAttribute!.AttributeConstructor!.Parameters.IndexOf(
parameterSymbol
);
var result = generationAttribute.ConstructorArguments[index].Value!.ToString();
if (!string.IsNullOrWhiteSpace(result))
var result = generationAttribute.ConstructorArguments[index].Value;
if (result != null)
{
return result;
return (T)result;
}
}

Expand Down
4 changes: 2 additions & 2 deletions AutomaticInterface/AutomaticInterface/InterfaceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ internal sealed record MethodInfo(

public record EventInfo(string Name, string Type, string Documentation);

public class InterfaceBuilder(string nameSpaceName, string interfaceName, bool asInternal)
public class InterfaceBuilder(string nameSpaceName, string interfaceName, string accessLevel)
{
private const string Autogenerated = """
//--------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -104,7 +104,7 @@ public string Build()
cb.AppendLine(
"[global::System.CodeDom.Compiler.GeneratedCode(\"AutomaticInterface\", \"\")]"
);
cb.AppendLine($"{(asInternal ? "internal" : "public")} partial interface {interfaceName}{genericType}");
cb.AppendLine($"{accessLevel} partial interface {interfaceName}{genericType}");
cb.AppendLine("{");

cb.Indent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace AutomaticInterface
[AttributeUsage(AttributeTargets.Class)]
internal sealed class {{{AutomaticInterfaceGenerator.DefaultAttributeName}}}Attribute : Attribute
{
internal {{{AutomaticInterfaceGenerator.DefaultAttributeName}}}Attribute(string {{{AutomaticInterfaceGenerator.NamespaceParameterName}}} = "", string {{{AutomaticInterfaceGenerator.InterfaceParameterName}}} = "", bool asInternal = false) { }
internal {{{AutomaticInterfaceGenerator.DefaultAttributeName}}}Attribute(string? {{{AutomaticInterfaceGenerator.NamespaceParameterName}}} = default(string), string? {{{AutomaticInterfaceGenerator.InterfaceParameterName}}} = default(string), bool {{{AutomaticInterfaceGenerator.AsInternalParameterName}}} = false) { }
}
}
""",
Expand Down
16 changes: 8 additions & 8 deletions AutomaticInterface/Tests/Misc/Misc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,8 @@ class DemoClass
}

[Fact]
public async Task AsInternal()
{
public async Task AsInternal()
{
const string code = """

using AutomaticInterface;
Expand All @@ -381,13 +381,13 @@ public string AMethod(DemoClass? x, string y)
}
}

""";
""";
await Verify(Infrastructure.GenerateCode(code));
}
}

[Fact]
public async Task AsInternalExplicitFalse()
{
public async Task AsInternalExplicitFalse()
{
const string code = """

using AutomaticInterface;
Expand All @@ -401,7 +401,7 @@ public string AMethod(DemoClass? x, string y)
}
}

""";
""";
await Verify(Infrastructure.GenerateCode(code));
}
}
Loading