Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Separation of Concerns with interface and client #1191

Open
tellybrown opened this issue Jun 25, 2021 · 0 comments
Open

feature: Separation of Concerns with interface and client #1191

tellybrown opened this issue Jun 25, 2021 · 0 comments

Comments

@tellybrown
Copy link

tellybrown commented Jun 25, 2021

Similar request to #542

It would be nice if you could alter the source generator to include adding the code to a partial class via a class with an attribute (Or some other means). It may be better to utilize an abstract class to do this where you can create your class from the abstract and the abstract class uses the attributes so that way the interface doesnt have to refer to the refit library.

Is your feature request related to a problem? Please describe.
The problem is that I want Separation of Concerns when it comes to my interfaces. I want the interface in one library and the client and all the framework dependencies in another. Much like what MS does with Abstractions libraries. The interface would be in the abstractions library while the client would be in the non-abstract library.

Describe the solution you'd like
Add another way for the source generator to choose the class it should implement. The refit source generator would detect the attribute and inspect the type being passed in to do the same code generation as before. The only difference is that the name of the class is exactly what we choose.

[RefitClient(typeof(IMyInterface))]
public partial class MyInterfaceClient
{
}


internal sealed class RefitSyntaxReceiver : ISyntaxReceiver
{
    public List<ClassDeclarationSyntax> Classes { get; } = new List<ClassDeclarationSyntax>();
    public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
    {
        if (syntaxNode is ClassDeclarationSyntax classSyntax && HaveAttribute(classSyntax, "RefitClient"))
        {
            Classes.Add(classSyntax);
        }
    }
    private bool HaveAttribute(ClassDeclarationSyntax classSyntax, string attributeName)
    {
        foreach (var attributeLists in classSyntax.AttributeLists)
        {
            foreach (var attribute in attributeLists.Attributes)
            {
                if (attribute.Name.NormalizeWhitespace().ToString() == attributeName)
                {
                    return true;
                }
            }
        }
        return false;
    }
}

Source Generator could find the interface like this...

  foreach (var @class in syntaxReciver.Classes)
  {
        if (!TryGetParentSyntax(@class, out NamespaceDeclarationSyntax namespaceDeclarationSyntax))
       {
             throw new Exception($"Namespace not found for {@class.ToFullString()}");
      }
      var @interface = GetInterface(@class);
      var type = context.Compilation.GetTypeByMetadataName($"{namespaceDeclarationSyntax.Name.ToString().Trim()}.{@interface}");
  }
           
    private string GetInterface(ClassDeclarationSyntax classSyntax)
    {
        foreach (var attributeLists in classSyntax.AttributeLists)
        {
            foreach (var attribute in attributeLists.Attributes)
            {
                if (attribute.Name.NormalizeWhitespace().ToString() == "RefitClient")
                {
                    var argument = attribute.ArgumentList.Arguments[0];

                    return argument.Expression.ToString().Replace("typeof(", "").Replace(")", "");
                }
            }
        }
        return "Not Found";
    }
    public bool TryGetParentSyntax<T>(SyntaxNode syntaxNode, out T result)
        where T : SyntaxNode
    {
        // set defaults
        result = null;

        if (syntaxNode == null)
        {
            return false;
        }

        try
        {
            syntaxNode = syntaxNode.Parent;

            if (syntaxNode == null)
            {
                return false;
            }

            if (syntaxNode.GetType() == typeof(T))
            {
                result = syntaxNode as T;
                return true;
            }

            return TryGetParentSyntax<T>(syntaxNode, out result);
        }
        catch
        {
            return false;
        }
    }
@tellybrown tellybrown changed the title feature: Title feature: Separation of Concerns with interface and client Jun 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant