-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Юрий Турбин
committed
Apr 9, 2024
1 parent
fc4d429
commit 101073d
Showing
12 changed files
with
1,449 additions
and
1 deletion.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,52 @@ | ||
# SourceGenLinq | ||
Simple implementation of flexible parameterized sorting for IQuariable using IncrementalGenerator | ||
|
||
Simple implementation of flexible parameterized sorting for IQuariable using IncrementalGenerator. | ||
|
||
## Usage | ||
|
||
> Tested on net8 | ||
0. Install library: | ||
|
||
```xml | ||
<ItemGroup> | ||
<PackageReference Include="SourceGenLinq.Abstractions" Version="0.0.1" /> | ||
<PackageReference Include="SourceGenLinq.Generator" Version="0.0.1" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/> | ||
</ItemGroup> | ||
``` | ||
|
||
1. Define DTO class: | ||
|
||
```csharp | ||
public class Tester | ||
{ | ||
public Guid Id { get; set; } | ||
|
||
public required string Name { get; set; } | ||
public required string Comment { get; set; } | ||
public required double Test { get; set; } | ||
|
||
public QazWsx? QazWsx { get; set; } | ||
} | ||
|
||
public class QazWsx; | ||
``` | ||
|
||
2. Define sorter with generic attribute `SortQuariable`: | ||
|
||
```csharp | ||
[SortQuariable<Tester>] | ||
public static partial class MyTesterSorter; | ||
``` | ||
|
||
3. Use: | ||
|
||
```csharp | ||
IQueryable<Tester> testers = default!; | ||
|
||
testers = testers.Sort(new() | ||
{ | ||
{ TesterSort.TesterProperty.IdProperty, SortMode.Asc }, | ||
{ TesterSort.TesterProperty.CommentProperty, SortMode.Desc }, | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
using SourceGenLinq.Abstractions; | ||
|
||
using TesterSample; | ||
|
||
Console.WriteLine("Hello, World!"); | ||
|
||
IQueryable<Tester> testers = new Tester[] | ||
{ | ||
new() | ||
{ | ||
Id = Guid.NewGuid(), | ||
Name = "Test", | ||
Comment = "Test", | ||
Test = 22, | ||
}, | ||
new() | ||
{ | ||
Id = Guid.NewGuid(), | ||
Name = "Test 2", | ||
Comment = "Test 3", | ||
Test = 100, | ||
}, | ||
}.AsQueryable(); | ||
|
||
IQueryable<Tester> orderedTesters = testers.Sort(new() | ||
{ | ||
{ TesterSort.TesterProperty.IdProperty, SortMode.Asc }, | ||
{ TesterSort.TesterProperty.CommentProperty, SortMode.Desc }, | ||
}); | ||
|
||
Console.WriteLine(); | ||
|
||
namespace TesterSample | ||
{ | ||
[SortQuariable<Tester>] | ||
public static partial class TesterSort2; | ||
|
||
public class Tester | ||
{ | ||
public Guid Id { get; set; } | ||
|
||
public required string Name { get; set; } | ||
public required string Comment { get; set; } | ||
public required double Test { get; set; } | ||
|
||
public QazWsx? QazWsx { get; set; } | ||
} | ||
|
||
[SortQuariable<QazWsx>] | ||
public static partial class QazWsxSorter; | ||
|
||
public class QazWsx | ||
{ | ||
public Guid Id { get; set; } | ||
public Guid Говно { get; set; } | ||
|
||
public required string Name { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\SourceGenLinq.Abstractions\SourceGenLinq.Abstractions.csproj" /> | ||
<ProjectReference Include="..\SourceGenLinq.Generator\SourceGenLinq.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace SourceGenLinq.Abstractions; | ||
|
||
public enum SortMode | ||
{ | ||
Asc, | ||
Desc, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
namespace SourceGenLinq.Abstractions; | ||
|
||
[AttributeUsage(AttributeTargets.Class)] | ||
public class SortQuariableAttribute<T> : Attribute where T : class; |
26 changes: 26 additions & 0 deletions
26
SourceGenLinq.Abstractions/SourceGenLinq.Abstractions.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<LangVersion>latest</LangVersion> | ||
<Version>0.0.1</Version> | ||
<PackageId>SourceGenLinq.Abstractions</PackageId> | ||
<Title>SourceGenLinq.Abstractions</Title> | ||
<PackageReadmeFile>README.md</PackageReadmeFile> | ||
<Description>Simple implementation of flexible parameterized sorting for IQuariable using IncrementalGenerator</Description> | ||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression> | ||
<RepositoryUrl>https://github.com/yurvon-screamo/SourceGenLinq</RepositoryUrl> | ||
<IncludeSymbols>True</IncludeSymbols> | ||
<SymbolPackageFormat>snupkg</SymbolPackageFormat> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<None Include="..\README.md"> | ||
<Pack>True</Pack> | ||
<PackagePath>\</PackagePath> | ||
</None> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 17 | ||
VisualStudioVersion = 17.5.002.0 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SourceGenLinq.Abstractions", "SourceGenLinq.Abstractions.csproj", "{70FD2DB6-3039-4170-B474-6D9C959CA79F}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{70FD2DB6-3039-4170-B474-6D9C959CA79F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{70FD2DB6-3039-4170-B474-6D9C959CA79F}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{70FD2DB6-3039-4170-B474-6D9C959CA79F}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{70FD2DB6-3039-4170-B474-6D9C959CA79F}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {ED9260E0-3F67-4748-8AD0-224C89971EDA} | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
|
||
namespace SourceGenLinq.Generator; | ||
|
||
[Generator] | ||
public class OrderByIncrementalGenerator : IIncrementalGenerator | ||
{ | ||
public void Initialize(IncrementalGeneratorInitializationContext context) | ||
{ | ||
IncrementalValuesProvider<Target> valuesProvider = context.SyntaxProvider | ||
.CreateSyntaxProvider( | ||
predicate: (p, _) => IsSyntaxTargetForGeneration(p), | ||
transform: (p, _) => GetSemanticTarget(p)) | ||
.Where(t => t.DataType is not null); | ||
|
||
context.RegisterSourceOutput(valuesProvider, (c, p) => | ||
{ | ||
if (p.DataType is null || p.NamespacePrefix is null) | ||
{ | ||
return; | ||
} | ||
string identity = p.DataType; | ||
string enumSource = string.Empty; | ||
string orderSource = string.Empty; | ||
foreach (string propertyName in p.PropertyNames) | ||
{ | ||
enumSource += $"\n {propertyName}Property,"; | ||
orderSource += $"\n {identity}Property.{propertyName}Property when item.Value is SourceGenLinq.Abstractions.SortMode.Asc => sources.OrderBy(t => t.{propertyName}),"; | ||
orderSource += $"\n {identity}Property.{propertyName}Property when item.Value is SourceGenLinq.Abstractions.SortMode.Desc => sources.OrderByDescending(t => t.{propertyName}),"; | ||
orderSource += '\n'; | ||
} | ||
string source = @"using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
"; | ||
source += p.NamespacePrefix == string.Empty | ||
? string.Empty | ||
: $"namespace {p.NamespacePrefix};\n\n"; | ||
source += $@"public static partial class {identity}Sort | ||
{{ | ||
public enum {identity}Property | ||
{{{enumSource} | ||
}} | ||
public sealed class {identity}SortInput : Dictionary<{identity}Property, SourceGenLinq.Abstractions.SortMode>; | ||
public static IQueryable<{identity}> Sort(this IQueryable<{identity}> sources, {identity}SortInput input) | ||
{{ | ||
foreach (KeyValuePair<{identity}Property, SourceGenLinq.Abstractions.SortMode> item in input) | ||
{{ | ||
sources = item.Key switch | ||
{{{orderSource} | ||
_ => throw new NotImplementedException(), | ||
}}; | ||
}} | ||
return sources; | ||
}} | ||
}} | ||
"; | ||
string file; | ||
if (p.NamespacePrefix == string.Empty) | ||
{ | ||
file = p.DataType; | ||
} | ||
else | ||
{ | ||
file = p.NamespacePrefix + '.' + p.DataType; | ||
} | ||
c.AddSource($"{file}.Generated.cs", source); | ||
}); | ||
} | ||
|
||
static bool IsSyntaxTargetForGeneration(SyntaxNode node) | ||
{ | ||
return node is ClassDeclarationSyntax classDeclaration && | ||
classDeclaration.AttributeLists.Any(); | ||
} | ||
|
||
static Target GetSemanticTarget(GeneratorSyntaxContext context) | ||
{ | ||
if (context.Node is ClassDeclarationSyntax classDeclaration) | ||
{ | ||
foreach (AttributeListSyntax listSyntax in classDeclaration.AttributeLists) | ||
{ | ||
foreach (AttributeSyntax attribute in listSyntax.Attributes) | ||
{ | ||
TypeInfo typeInfo = context.SemanticModel.GetTypeInfo(attribute); | ||
|
||
if (typeInfo.Type?.Name == "SortQuariableAttribute") | ||
{ | ||
IEnumerable<GenericNameSyntax> childNodes = attribute | ||
.ChildNodes() | ||
.OfType<GenericNameSyntax>(); | ||
|
||
foreach (GenericNameSyntax node in childNodes) | ||
{ | ||
TypeArgumentListSyntax argumentListSyntax = node.TypeArgumentList; | ||
|
||
foreach (TypeSyntax typeSyntax in argumentListSyntax.Arguments) | ||
{ | ||
ITypeSymbol? typeSymbol = context | ||
.SemanticModel | ||
.GetTypeInfo(typeSyntax) | ||
.Type; | ||
|
||
if (typeSymbol is null) | ||
{ | ||
continue; | ||
} | ||
|
||
INamespaceSymbol namespaceSymbol = typeSymbol.ContainingNamespace; | ||
|
||
IEnumerable<IPropertySymbol> propertySymbols = typeSymbol | ||
.GetMembers() | ||
.OfType<IPropertySymbol>(); | ||
|
||
List<string> propertyNames = new(); | ||
|
||
foreach (IPropertySymbol property in propertySymbols) | ||
{ | ||
if (property.IsStatic || property.IsWriteOnly) | ||
{ | ||
continue; | ||
} | ||
|
||
ITypeSymbol propertyType = property.Type; | ||
|
||
if (propertyType.IsValueType || propertyType.Name == nameof(String)) | ||
{ | ||
propertyNames.Add(property.Name); | ||
} | ||
} | ||
|
||
string namespacePrefix = | ||
namespaceSymbol.IsGlobalNamespace | ||
? string.Empty | ||
: namespaceSymbol.ToDisplayString(); | ||
|
||
return new( | ||
typeSymbol.Name, | ||
namespacePrefix, | ||
propertyNames.ToImmutableArray()); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
return new(null, null, default); | ||
} | ||
|
||
private readonly struct Target(string? dataType, string? namespacePrefix, ImmutableArray<string> propertyNames) | ||
{ | ||
public string? DataType { get; } = dataType; | ||
public string? NamespacePrefix { get; } = namespacePrefix; | ||
public ImmutableArray<string> PropertyNames { get; } = propertyNames; | ||
} | ||
} |
Oops, something went wrong.