Compare commits
4 commits
main
...
test-gener
Author | SHA1 | Date | |
---|---|---|---|
6386a9ec16 | |||
e09b82884b | |||
79a791d79b | |||
be0d5c08bf |
30 changed files with 876 additions and 581 deletions
295
.editorconfig
295
.editorconfig
|
@ -1,5 +1,4 @@
|
|||
root = true
|
||||
; EditorConfig to support per-solution formatting.
|
||||
; EditorConfig to support per-solution formatting.
|
||||
; Use the EditorConfig VS add-in to make this work.
|
||||
; http://editorconfig.org/
|
||||
;
|
||||
|
@ -12,11 +11,12 @@
|
|||
; then the rule will be silently ignored.
|
||||
|
||||
; This is the default for the codeline.
|
||||
root = true
|
||||
|
||||
; For disabling MA0048 and MA0051 in a specific folder, e.g., Migrations
|
||||
[**/Migrations/*.cs]
|
||||
dotnet_diagnostic.ma0048.severity = none
|
||||
dotnet_diagnostic.ma0051.severity = none
|
||||
dotnet_diagnostic.MA0048.severity = none
|
||||
dotnet_diagnostic.MA0051.severity = none
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
|
@ -25,19 +25,14 @@ trim_trailing_whitespace = true
|
|||
insert_final_newline = true
|
||||
file_header_template = Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
[*.{yaml,yml}]
|
||||
[*.(yaml|yml)]
|
||||
indent_size = 2
|
||||
|
||||
[*.cs]
|
||||
# ReSharper properties
|
||||
resharper_csharp_wrap_arguments_style = chop_if_long
|
||||
resharper_csharp_wrap_before_invocation_rpar = true
|
||||
resharper_wrap_chained_binary_expressions = chop_if_long
|
||||
resharper_wrap_chained_method_calls = chop_if_long
|
||||
dotnet_diagnostic.ma0004.severity = none
|
||||
dotnet_diagnostic.MA0004.severity = none
|
||||
indent_size = 4
|
||||
dotnet_sort_system_directives_first = true
|
||||
dotnet_diagnostic.ma0007.severity = none
|
||||
dotnet_diagnostic.MA0007.severity = none
|
||||
#MA0004.report = DetectContext # (default) Try to detect the current context and report only if it considers ConfigureAwait is needed
|
||||
# Don't use this. qualifier
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
|
@ -58,7 +53,7 @@ csharp_style_var_elsewhere = true:suggestion
|
|||
csharp_style_throw_expression = false:suggestion
|
||||
|
||||
# Newline settings
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_before_open_brace = none
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_finally = true
|
||||
|
@ -111,367 +106,367 @@ charset = utf-8-bom
|
|||
[*.{cs,vb}]
|
||||
|
||||
# SYSLIB1054: Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time
|
||||
dotnet_diagnostic.syslib1054.severity = warning
|
||||
dotnet_diagnostic.SYSLIB1054.severity = warning
|
||||
|
||||
# CA1018: Mark attributes with AttributeUsageAttribute
|
||||
dotnet_diagnostic.ca1018.severity = warning
|
||||
dotnet_diagnostic.CA1018.severity = warning
|
||||
|
||||
# CA1047: Do not declare protected member in sealed type
|
||||
dotnet_diagnostic.ca1047.severity = warning
|
||||
dotnet_diagnostic.CA1047.severity = warning
|
||||
|
||||
# CA1305: Specify IFormatProvider
|
||||
dotnet_diagnostic.ca1305.severity = warning
|
||||
dotnet_diagnostic.CA1305.severity = warning
|
||||
|
||||
# CA1507: Use nameof to express symbol names
|
||||
dotnet_diagnostic.ca1507.severity = warning
|
||||
dotnet_diagnostic.CA1507.severity = warning
|
||||
|
||||
# CA1510: Use ArgumentNullException throw helper
|
||||
dotnet_diagnostic.ca1510.severity = warning
|
||||
dotnet_diagnostic.CA1510.severity = warning
|
||||
|
||||
# CA1511: Use ArgumentException throw helper
|
||||
dotnet_diagnostic.ca1511.severity = warning
|
||||
dotnet_diagnostic.CA1511.severity = warning
|
||||
|
||||
# CA1512: Use ArgumentOutOfRangeException throw helper
|
||||
dotnet_diagnostic.ca1512.severity = warning
|
||||
dotnet_diagnostic.CA1512.severity = warning
|
||||
|
||||
# CA1513: Use ObjectDisposedException throw helper
|
||||
dotnet_diagnostic.ca1513.severity = warning
|
||||
dotnet_diagnostic.CA1513.severity = warning
|
||||
|
||||
# CA1725: Parameter names should match base declaration
|
||||
dotnet_diagnostic.ca1725.severity = suggestion
|
||||
dotnet_diagnostic.CA1725.severity = suggestion
|
||||
|
||||
# CA1802: Use literals where appropriate
|
||||
dotnet_diagnostic.ca1802.severity = warning
|
||||
dotnet_diagnostic.CA1802.severity = warning
|
||||
|
||||
# CA1805: Do not initialize unnecessarily
|
||||
dotnet_diagnostic.ca1805.severity = warning
|
||||
dotnet_diagnostic.CA1805.severity = warning
|
||||
|
||||
# CA1810: Do not initialize unnecessarily
|
||||
dotnet_diagnostic.ca1810.severity = warning
|
||||
dotnet_diagnostic.CA1810.severity = warning
|
||||
|
||||
# CA1821: Remove empty Finalizers
|
||||
dotnet_diagnostic.ca1821.severity = warning
|
||||
dotnet_diagnostic.CA1821.severity = warning
|
||||
|
||||
# CA1822: Make member static
|
||||
dotnet_diagnostic.ca1822.severity = warning
|
||||
dotnet_code_quality.ca1822.api_surface = private, internal
|
||||
dotnet_diagnostic.CA1822.severity = warning
|
||||
dotnet_code_quality.CA1822.api_surface = private, internal
|
||||
|
||||
# CA1823: Avoid unused private fields
|
||||
dotnet_diagnostic.ca1823.severity = warning
|
||||
dotnet_diagnostic.CA1823.severity = warning
|
||||
|
||||
# CA1825: Avoid zero-length array allocations
|
||||
dotnet_diagnostic.ca1825.severity = warning
|
||||
dotnet_diagnostic.CA1825.severity = warning
|
||||
|
||||
# CA1826: Do not use Enumerable methods on indexable collections. Instead use the collection directly
|
||||
dotnet_diagnostic.ca1826.severity = warning
|
||||
dotnet_diagnostic.CA1826.severity = warning
|
||||
|
||||
# CA1827: Do not use Count() or LongCount() when Any() can be used
|
||||
dotnet_diagnostic.ca1827.severity = warning
|
||||
dotnet_diagnostic.CA1827.severity = warning
|
||||
|
||||
# CA1828: Do not use CountAsync() or LongCountAsync() when AnyAsync() can be used
|
||||
dotnet_diagnostic.ca1828.severity = warning
|
||||
dotnet_diagnostic.CA1828.severity = warning
|
||||
|
||||
# CA1829: Use Length/Count property instead of Count() when available
|
||||
dotnet_diagnostic.ca1829.severity = warning
|
||||
dotnet_diagnostic.CA1829.severity = warning
|
||||
|
||||
# CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder
|
||||
dotnet_diagnostic.ca1830.severity = warning
|
||||
dotnet_diagnostic.CA1830.severity = warning
|
||||
|
||||
# CA1831: Use AsSpan or AsMemory instead of Range-based indexers when appropriate
|
||||
dotnet_diagnostic.ca1831.severity = warning
|
||||
dotnet_diagnostic.CA1831.severity = warning
|
||||
|
||||
# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate
|
||||
dotnet_diagnostic.ca1832.severity = warning
|
||||
dotnet_diagnostic.CA1832.severity = warning
|
||||
|
||||
# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate
|
||||
dotnet_diagnostic.ca1833.severity = warning
|
||||
dotnet_diagnostic.CA1833.severity = warning
|
||||
|
||||
# CA1834: Consider using 'StringBuilder.Append(char)' when applicable
|
||||
dotnet_diagnostic.ca1834.severity = warning
|
||||
dotnet_diagnostic.CA1834.severity = warning
|
||||
|
||||
# CA1835: Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync'
|
||||
dotnet_diagnostic.ca1835.severity = warning
|
||||
dotnet_diagnostic.CA1835.severity = warning
|
||||
|
||||
# CA1836: Prefer IsEmpty over Count
|
||||
dotnet_diagnostic.ca1836.severity = warning
|
||||
dotnet_diagnostic.CA1836.severity = warning
|
||||
|
||||
# CA1837: Use 'Environment.ProcessId'
|
||||
dotnet_diagnostic.ca1837.severity = warning
|
||||
dotnet_diagnostic.CA1837.severity = warning
|
||||
|
||||
# CA1838: Avoid 'StringBuilder' parameters for P/Invokes
|
||||
dotnet_diagnostic.ca1838.severity = warning
|
||||
dotnet_diagnostic.CA1838.severity = warning
|
||||
|
||||
# CA1839: Use 'Environment.ProcessPath'
|
||||
dotnet_diagnostic.ca1839.severity = warning
|
||||
dotnet_diagnostic.CA1839.severity = warning
|
||||
|
||||
# CA1840: Use 'Environment.CurrentManagedThreadId'
|
||||
dotnet_diagnostic.ca1840.severity = warning
|
||||
dotnet_diagnostic.CA1840.severity = warning
|
||||
|
||||
# CA1841: Prefer Dictionary.Contains methods
|
||||
dotnet_diagnostic.ca1841.severity = warning
|
||||
dotnet_diagnostic.CA1841.severity = warning
|
||||
|
||||
# CA1842: Do not use 'WhenAll' with a single task
|
||||
dotnet_diagnostic.ca1842.severity = warning
|
||||
dotnet_diagnostic.CA1842.severity = warning
|
||||
|
||||
# CA1843: Do not use 'WaitAll' with a single task
|
||||
dotnet_diagnostic.ca1843.severity = warning
|
||||
dotnet_diagnostic.CA1843.severity = warning
|
||||
|
||||
# CA1844: Provide memory-based overrides of async methods when subclassing 'Stream'
|
||||
dotnet_diagnostic.ca1844.severity = warning
|
||||
dotnet_diagnostic.CA1844.severity = warning
|
||||
|
||||
# CA1845: Use span-based 'string.Concat'
|
||||
dotnet_diagnostic.ca1845.severity = warning
|
||||
dotnet_diagnostic.CA1845.severity = warning
|
||||
|
||||
# CA1846: Prefer AsSpan over Substring
|
||||
dotnet_diagnostic.ca1846.severity = warning
|
||||
dotnet_diagnostic.CA1846.severity = warning
|
||||
|
||||
# CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters
|
||||
dotnet_diagnostic.ca1847.severity = warning
|
||||
dotnet_diagnostic.CA1847.severity = warning
|
||||
|
||||
# CA1852: Seal internal types
|
||||
dotnet_diagnostic.ca1852.severity = warning
|
||||
dotnet_diagnostic.CA1852.severity = warning
|
||||
|
||||
# CA1854: Prefer the IDictionary.TryGetValue(TKey, out TValue) method
|
||||
dotnet_diagnostic.ca1854.severity = warning
|
||||
dotnet_diagnostic.CA1854.severity = warning
|
||||
|
||||
# CA1855: Prefer 'Clear' over 'Fill'
|
||||
dotnet_diagnostic.ca1855.severity = warning
|
||||
dotnet_diagnostic.CA1855.severity = warning
|
||||
|
||||
# CA1856: Incorrect usage of ConstantExpected attribute
|
||||
dotnet_diagnostic.ca1856.severity = error
|
||||
dotnet_diagnostic.CA1856.severity = error
|
||||
|
||||
# CA1857: A constant is expected for the parameter
|
||||
dotnet_diagnostic.ca1857.severity = warning
|
||||
dotnet_diagnostic.CA1857.severity = warning
|
||||
|
||||
# CA1858: Use 'StartsWith' instead of 'IndexOf'
|
||||
dotnet_diagnostic.ca1858.severity = warning
|
||||
dotnet_diagnostic.CA1858.severity = warning
|
||||
|
||||
# CA2007: Consider calling ConfigureAwait on the awaited task
|
||||
dotnet_diagnostic.ca2007.severity = none
|
||||
dotnet_diagnostic.CA2007.severity = none
|
||||
|
||||
# CA2008: Do not create tasks without passing a TaskScheduler
|
||||
dotnet_diagnostic.ca2008.severity = warning
|
||||
dotnet_diagnostic.CA2008.severity = warning
|
||||
|
||||
# CA2009: Do not call ToImmutableCollection on an ImmutableCollection value
|
||||
dotnet_diagnostic.ca2009.severity = warning
|
||||
dotnet_diagnostic.CA2009.severity = warning
|
||||
|
||||
# CA2011: Avoid infinite recursion
|
||||
dotnet_diagnostic.ca2011.severity = warning
|
||||
dotnet_diagnostic.CA2011.severity = warning
|
||||
|
||||
# CA2012: Use ValueTask correctly
|
||||
dotnet_diagnostic.ca2012.severity = warning
|
||||
dotnet_diagnostic.CA2012.severity = warning
|
||||
|
||||
# CA2013: Do not use ReferenceEquals with value types
|
||||
dotnet_diagnostic.ca2013.severity = warning
|
||||
dotnet_diagnostic.CA2013.severity = warning
|
||||
|
||||
# CA2014: Do not use stackalloc in loops.
|
||||
dotnet_diagnostic.ca2014.severity = warning
|
||||
dotnet_diagnostic.CA2014.severity = warning
|
||||
|
||||
# CA2016: Forward the 'CancellationToken' parameter to methods that take one
|
||||
dotnet_diagnostic.ca2016.severity = warning
|
||||
dotnet_diagnostic.CA2016.severity = warning
|
||||
|
||||
# CA2200: Rethrow to preserve stack details
|
||||
dotnet_diagnostic.ca2200.severity = warning
|
||||
dotnet_diagnostic.CA2200.severity = warning
|
||||
|
||||
# CA2201: Do not raise reserved exception types
|
||||
dotnet_diagnostic.ca2201.severity = warning
|
||||
dotnet_diagnostic.CA2201.severity = warning
|
||||
|
||||
# CA2208: Instantiate argument exceptions correctly
|
||||
dotnet_diagnostic.ca2208.severity = warning
|
||||
dotnet_diagnostic.CA2208.severity = warning
|
||||
|
||||
# CA2245: Do not assign a property to itself
|
||||
dotnet_diagnostic.ca2245.severity = warning
|
||||
dotnet_diagnostic.CA2245.severity = warning
|
||||
|
||||
# CA2246: Assigning symbol and its member in the same statement
|
||||
dotnet_diagnostic.ca2246.severity = warning
|
||||
dotnet_diagnostic.CA2246.severity = warning
|
||||
|
||||
# CA2249: Use string.Contains instead of string.IndexOf to improve readability.
|
||||
dotnet_diagnostic.ca2249.severity = warning
|
||||
dotnet_diagnostic.CA2249.severity = warning
|
||||
|
||||
# IDE0005: Remove unnecessary usings
|
||||
dotnet_diagnostic.ide0005.severity = warning
|
||||
dotnet_diagnostic.IDE0005.severity = warning
|
||||
|
||||
# IDE0011: Curly braces to surround blocks of code
|
||||
dotnet_diagnostic.ide0011.severity = warning
|
||||
dotnet_diagnostic.IDE0011.severity = warning
|
||||
|
||||
# IDE0020: Use pattern matching to avoid is check followed by a cast (with variable)
|
||||
dotnet_diagnostic.ide0020.severity = warning
|
||||
dotnet_diagnostic.IDE0020.severity = warning
|
||||
|
||||
# IDE0029: Use coalesce expression (non-nullable types)
|
||||
dotnet_diagnostic.ide0029.severity = warning
|
||||
dotnet_diagnostic.IDE0029.severity = warning
|
||||
|
||||
# IDE0030: Use coalesce expression (nullable types)
|
||||
dotnet_diagnostic.ide0030.severity = warning
|
||||
dotnet_diagnostic.IDE0030.severity = warning
|
||||
|
||||
# IDE0031: Use null propagation
|
||||
dotnet_diagnostic.ide0031.severity = warning
|
||||
dotnet_diagnostic.IDE0031.severity = warning
|
||||
|
||||
# IDE0035: Remove unreachable code
|
||||
dotnet_diagnostic.ide0035.severity = warning
|
||||
dotnet_diagnostic.IDE0035.severity = warning
|
||||
|
||||
# IDE0036: Order modifiers
|
||||
csharp_preferred_modifier_order = public, private, protected, internal, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async:suggestion
|
||||
dotnet_diagnostic.ide0036.severity = warning
|
||||
dotnet_diagnostic.IDE0036.severity = warning
|
||||
|
||||
# IDE0038: Use pattern matching to avoid is check followed by a cast (without variable)
|
||||
dotnet_diagnostic.ide0038.severity = warning
|
||||
dotnet_diagnostic.IDE0038.severity = warning
|
||||
|
||||
# IDE0043: Format string contains invalid placeholder
|
||||
dotnet_diagnostic.ide0043.severity = warning
|
||||
dotnet_diagnostic.IDE0043.severity = warning
|
||||
|
||||
# IDE0044: Make field readonly
|
||||
dotnet_diagnostic.ide0044.severity = warning
|
||||
dotnet_diagnostic.IDE0044.severity = warning
|
||||
|
||||
# IDE0051: Remove unused private members
|
||||
dotnet_diagnostic.ide0051.severity = warning
|
||||
dotnet_diagnostic.IDE0051.severity = warning
|
||||
|
||||
# IDE0055: All formatting rules
|
||||
dotnet_diagnostic.ide0055.severity = suggestion
|
||||
dotnet_diagnostic.IDE0055.severity = suggestion
|
||||
|
||||
# IDE0059: Unnecessary assignment to a value
|
||||
dotnet_diagnostic.ide0059.severity = warning
|
||||
dotnet_diagnostic.IDE0059.severity = warning
|
||||
|
||||
# IDE0060: Remove unused parameter
|
||||
dotnet_code_quality_unused_parameters = non_public
|
||||
dotnet_diagnostic.ide0060.severity = warning
|
||||
dotnet_diagnostic.IDE0060.severity = warning
|
||||
|
||||
# IDE0062: Make local function static
|
||||
dotnet_diagnostic.ide0062.severity = warning
|
||||
dotnet_diagnostic.IDE0062.severity = warning
|
||||
|
||||
# IDE0073: File header
|
||||
dotnet_diagnostic.ide0073.severity = warning
|
||||
dotnet_diagnostic.IDE0073.severity = warning
|
||||
# Not a dotnet foundation project
|
||||
#file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
# IDE0161: Convert to file-scoped namespace
|
||||
dotnet_diagnostic.ide0161.severity = warning
|
||||
dotnet_diagnostic.IDE0161.severity = warning
|
||||
|
||||
# IDE0200: Lambda expression can be removed
|
||||
dotnet_diagnostic.ide0200.severity = warning
|
||||
dotnet_diagnostic.IDE0200.severity = warning
|
||||
|
||||
# IDE2000: Disallow multiple blank lines
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = false
|
||||
dotnet_diagnostic.ide2000.severity = warning
|
||||
dotnet_diagnostic.IDE2000.severity = warning
|
||||
|
||||
[{eng/tools/**.cs,**/{test,testassets,samples,Samples,perf,benchmarkapps,scripts,stress}/**.cs,src/Hosting/Server.IntegrationTesting/**.cs,src/Servers/IIS/IntegrationTesting.IIS/**.cs,src/Shared/Http2cat/**.cs,src/Testing/**.cs}]
|
||||
# CA1018: Mark attributes with AttributeUsageAttribute
|
||||
dotnet_diagnostic.ca1018.severity = suggestion
|
||||
dotnet_diagnostic.CA1018.severity = suggestion
|
||||
# CA1507: Use nameof to express symbol names
|
||||
dotnet_diagnostic.ca1507.severity = suggestion
|
||||
dotnet_diagnostic.CA1507.severity = suggestion
|
||||
# CA1510: Use ArgumentNullException throw helper
|
||||
dotnet_diagnostic.ca1510.severity = suggestion
|
||||
dotnet_diagnostic.CA1510.severity = suggestion
|
||||
# CA1511: Use ArgumentException throw helper
|
||||
dotnet_diagnostic.ca1511.severity = suggestion
|
||||
dotnet_diagnostic.CA1511.severity = suggestion
|
||||
# CA1512: Use ArgumentOutOfRangeException throw helper
|
||||
dotnet_diagnostic.ca1512.severity = suggestion
|
||||
dotnet_diagnostic.CA1512.severity = suggestion
|
||||
# CA1513: Use ObjectDisposedException throw helper
|
||||
dotnet_diagnostic.ca1513.severity = suggestion
|
||||
dotnet_diagnostic.CA1513.severity = suggestion
|
||||
# CA1802: Use literals where appropriate
|
||||
dotnet_diagnostic.ca1802.severity = suggestion
|
||||
dotnet_diagnostic.CA1802.severity = suggestion
|
||||
# CA1805: Do not initialize unnecessarily
|
||||
dotnet_diagnostic.ca1805.severity = suggestion
|
||||
dotnet_diagnostic.CA1805.severity = suggestion
|
||||
# CA1810: Do not initialize unnecessarily
|
||||
dotnet_diagnostic.ca1810.severity = suggestion
|
||||
dotnet_diagnostic.CA1810.severity = suggestion
|
||||
# CA1822: Make member static
|
||||
dotnet_diagnostic.ca1822.severity = suggestion
|
||||
dotnet_diagnostic.CA1822.severity = suggestion
|
||||
# CA1823: Avoid zero-length array allocations
|
||||
dotnet_diagnostic.ca1825.severity = suggestion
|
||||
dotnet_diagnostic.CA1825.severity = suggestion
|
||||
# CA1826: Do not use Enumerable methods on indexable collections. Instead use the collection directly
|
||||
dotnet_diagnostic.ca1826.severity = suggestion
|
||||
dotnet_diagnostic.CA1826.severity = suggestion
|
||||
# CA1827: Do not use Count() or LongCount() when Any() can be used
|
||||
dotnet_diagnostic.ca1827.severity = suggestion
|
||||
dotnet_diagnostic.CA1827.severity = suggestion
|
||||
# CA1829: Use Length/Count property instead of Count() when available
|
||||
dotnet_diagnostic.ca1829.severity = suggestion
|
||||
dotnet_diagnostic.CA1829.severity = suggestion
|
||||
# CA1831: Use AsSpan or AsMemory instead of Range-based indexers when appropriate
|
||||
dotnet_diagnostic.ca1831.severity = suggestion
|
||||
dotnet_diagnostic.CA1831.severity = suggestion
|
||||
# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate
|
||||
dotnet_diagnostic.ca1832.severity = suggestion
|
||||
dotnet_diagnostic.CA1832.severity = suggestion
|
||||
# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate
|
||||
dotnet_diagnostic.ca1833.severity = suggestion
|
||||
dotnet_diagnostic.CA1833.severity = suggestion
|
||||
# CA1834: Consider using 'StringBuilder.Append(char)' when applicable
|
||||
dotnet_diagnostic.ca1834.severity = suggestion
|
||||
dotnet_diagnostic.CA1834.severity = suggestion
|
||||
# CA1835: Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync'
|
||||
dotnet_diagnostic.ca1835.severity = suggestion
|
||||
dotnet_diagnostic.CA1835.severity = suggestion
|
||||
# CA1837: Use 'Environment.ProcessId'
|
||||
dotnet_diagnostic.ca1837.severity = suggestion
|
||||
dotnet_diagnostic.CA1837.severity = suggestion
|
||||
# CA1838: Avoid 'StringBuilder' parameters for P/Invokes
|
||||
dotnet_diagnostic.ca1838.severity = suggestion
|
||||
dotnet_diagnostic.CA1838.severity = suggestion
|
||||
# CA1841: Prefer Dictionary.Contains methods
|
||||
dotnet_diagnostic.ca1841.severity = suggestion
|
||||
dotnet_diagnostic.CA1841.severity = suggestion
|
||||
# CA1844: Provide memory-based overrides of async methods when subclassing 'Stream'
|
||||
dotnet_diagnostic.ca1844.severity = suggestion
|
||||
dotnet_diagnostic.CA1844.severity = suggestion
|
||||
# CA1845: Use span-based 'string.Concat'
|
||||
dotnet_diagnostic.ca1845.severity = suggestion
|
||||
dotnet_diagnostic.CA1845.severity = suggestion
|
||||
# CA1846: Prefer AsSpan over Substring
|
||||
dotnet_diagnostic.ca1846.severity = suggestion
|
||||
dotnet_diagnostic.CA1846.severity = suggestion
|
||||
# CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters
|
||||
dotnet_diagnostic.ca1847.severity = suggestion
|
||||
dotnet_diagnostic.CA1847.severity = suggestion
|
||||
# CA1852: Seal internal types
|
||||
dotnet_diagnostic.ca1852.severity = suggestion
|
||||
dotnet_diagnostic.CA1852.severity = suggestion
|
||||
# CA1854: Prefer the IDictionary.TryGetValue(TKey, out TValue) method
|
||||
dotnet_diagnostic.ca1854.severity = suggestion
|
||||
dotnet_diagnostic.CA1854.severity = suggestion
|
||||
# CA1855: Prefer 'Clear' over 'Fill'
|
||||
dotnet_diagnostic.ca1855.severity = suggestion
|
||||
dotnet_diagnostic.CA1855.severity = suggestion
|
||||
# CA1856: Incorrect usage of ConstantExpected attribute
|
||||
dotnet_diagnostic.ca1856.severity = suggestion
|
||||
dotnet_diagnostic.CA1856.severity = suggestion
|
||||
# CA1857: A constant is expected for the parameter
|
||||
dotnet_diagnostic.ca1857.severity = suggestion
|
||||
dotnet_diagnostic.CA1857.severity = suggestion
|
||||
# CA1858: Use 'StartsWith' instead of 'IndexOf'
|
||||
dotnet_diagnostic.ca1858.severity = suggestion
|
||||
dotnet_diagnostic.CA1858.severity = suggestion
|
||||
# CA2007: Consider calling ConfigureAwait on the awaited task
|
||||
dotnet_diagnostic.ca2007.severity = suggestion
|
||||
dotnet_diagnostic.CA2007.severity = suggestion
|
||||
# CA2008: Do not create tasks without passing a TaskScheduler
|
||||
dotnet_diagnostic.ca2008.severity = suggestion
|
||||
dotnet_diagnostic.CA2008.severity = suggestion
|
||||
# CA2012: Use ValueTask correctly
|
||||
dotnet_diagnostic.ca2012.severity = suggestion
|
||||
dotnet_diagnostic.CA2012.severity = suggestion
|
||||
# CA2201: Do not raise reserved exception types
|
||||
dotnet_diagnostic.ca2201.severity = suggestion
|
||||
dotnet_diagnostic.CA2201.severity = suggestion
|
||||
# CA2249: Use string.Contains instead of string.IndexOf to improve readability.
|
||||
dotnet_diagnostic.ca2249.severity = suggestion
|
||||
dotnet_diagnostic.CA2249.severity = suggestion
|
||||
# IDE0005: Remove unnecessary usings
|
||||
dotnet_diagnostic.ide0005.severity = suggestion
|
||||
dotnet_diagnostic.IDE0005.severity = suggestion
|
||||
# IDE0020: Use pattern matching to avoid is check followed by a cast (with variable)
|
||||
dotnet_diagnostic.ide0020.severity = suggestion
|
||||
dotnet_diagnostic.IDE0020.severity = suggestion
|
||||
# IDE0029: Use coalesce expression (non-nullable types)
|
||||
dotnet_diagnostic.ide0029.severity = suggestion
|
||||
dotnet_diagnostic.IDE0029.severity = suggestion
|
||||
# IDE0030: Use coalesce expression (nullable types)
|
||||
dotnet_diagnostic.ide0030.severity = suggestion
|
||||
dotnet_diagnostic.IDE0030.severity = suggestion
|
||||
# IDE0031: Use null propagation
|
||||
dotnet_diagnostic.ide0031.severity = suggestion
|
||||
dotnet_diagnostic.IDE0031.severity = suggestion
|
||||
# IDE0038: Use pattern matching to avoid is check followed by a cast (without variable)
|
||||
dotnet_diagnostic.ide0038.severity = suggestion
|
||||
dotnet_diagnostic.IDE0038.severity = suggestion
|
||||
# IDE0044: Make field readonly
|
||||
dotnet_diagnostic.ide0044.severity = suggestion
|
||||
dotnet_diagnostic.IDE0044.severity = suggestion
|
||||
# IDE0051: Remove unused private members
|
||||
dotnet_diagnostic.ide0051.severity = suggestion
|
||||
dotnet_diagnostic.IDE0051.severity = suggestion
|
||||
# IDE0059: Unnecessary assignment to a value
|
||||
dotnet_diagnostic.ide0059.severity = suggestion
|
||||
dotnet_diagnostic.IDE0059.severity = suggestion
|
||||
# IDE0060: Remove unused parameters
|
||||
dotnet_diagnostic.ide0060.severity = suggestion
|
||||
dotnet_diagnostic.IDE0060.severity = suggestion
|
||||
# IDE0062: Make local function static
|
||||
dotnet_diagnostic.ide0062.severity = suggestion
|
||||
dotnet_diagnostic.IDE0062.severity = suggestion
|
||||
# IDE0200: Lambda expression can be removed
|
||||
dotnet_diagnostic.ide0200.severity = suggestion
|
||||
dotnet_diagnostic.IDE0200.severity = suggestion
|
||||
|
||||
# CA2016: Forward the 'CancellationToken' parameter to methods that take one
|
||||
dotnet_diagnostic.ca2016.severity = suggestion
|
||||
dotnet_diagnostic.CA2016.severity = suggestion
|
||||
|
||||
# Defaults for content in the shared src/ and shared runtime dir
|
||||
|
||||
[{**/Shared/runtime/**.{cs,vb},src/Shared/test/Shared.Tests/runtime/**.{cs,vb},**/microsoft.extensions.hostfactoryresolver.sources/**.{cs,vb}}]
|
||||
# CA1822: Make member static
|
||||
dotnet_diagnostic.ca1822.severity = silent
|
||||
dotnet_diagnostic.CA1822.severity = silent
|
||||
# IDE0011: Use braces
|
||||
dotnet_diagnostic.ide0011.severity = silent
|
||||
dotnet_diagnostic.IDE0011.severity = silent
|
||||
# IDE0055: Fix formatting
|
||||
dotnet_diagnostic.ide0055.severity = silent
|
||||
dotnet_diagnostic.IDE0055.severity = silent
|
||||
# IDE0060: Remove unused parameters
|
||||
dotnet_diagnostic.ide0060.severity = silent
|
||||
dotnet_diagnostic.IDE0060.severity = silent
|
||||
# IDE0062: Make local function static
|
||||
dotnet_diagnostic.ide0062.severity = silent
|
||||
dotnet_diagnostic.IDE0062.severity = silent
|
||||
# IDE0161: Convert to file-scoped namespace
|
||||
dotnet_diagnostic.ide0161.severity = silent
|
||||
dotnet_diagnostic.IDE0161.severity = silent
|
||||
|
||||
[{**/Shared/**.cs,**/microsoft.extensions.hostfactoryresolver.sources/**.{cs,vb}}]
|
||||
# IDE0005: Remove unused usings. Ignore for shared src files since imports for those depend on the projects in which they are included.
|
||||
dotnet_diagnostic.ide0005.severity = silent
|
||||
dotnet_diagnostic.IDE0005.severity = silent
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
name: Release Rai.PacketMediator
|
||||
name: Release Rai.PacketMediator
|
||||
run-name: ${{ gitea.actor }} is building the Server application
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*.*.*
|
||||
- 'v*.*.*'
|
||||
paths-ignore:
|
||||
- .run/**
|
||||
- .run/**
|
||||
|
||||
jobs:
|
||||
preprocess:
|
||||
|
@ -13,75 +13,75 @@ jobs:
|
|||
outputs:
|
||||
sanitized_branch_name: ${{ steps.sanitize.outputs.sanitized_branch_name }}
|
||||
steps:
|
||||
- name: Sanitize branch name
|
||||
id: sanitize
|
||||
run: echo "::set-output name=sanitized_branch_name::$(echo ${{ github.ref_name }} | sed 's/\//-/g')"
|
||||
- name: Sanitize branch name
|
||||
id: sanitize
|
||||
run: echo "::set-output name=sanitized_branch_name::$(echo ${{ github.ref_name }} | sed 's/\//-/g')"
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: global.json
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: |
|
||||
cd RaiNote.PacketMediator
|
||||
dotnet build RaiNote.PacketMediator.csproj -c Release
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: global.json
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: |
|
||||
cd RaiNote.PacketMediator
|
||||
dotnet build RaiNote.PacketMediator.csproj -c Release
|
||||
|
||||
publish:
|
||||
needs: [build]
|
||||
needs: [ build ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: global.json
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Extract Version
|
||||
run: |
|
||||
VERSION=${GITHUB_REF#refs/tags/v}
|
||||
echo "Extracted version is $VERSION"
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
- name: Pack nuget package
|
||||
run: |
|
||||
cd RaiNote.PacketMediator
|
||||
dotnet build RaiNote.PacketMediator.csproj -c Release -p:PackageVersion=${{env.VERSION}}
|
||||
dotnet pack -c Release -p:PackageVersion=${{env.VERSION}} --output .
|
||||
- name: Push to NuGet
|
||||
run: |
|
||||
cd RaiNote.PacketMediator
|
||||
dotnet nuget push "*.nupkg" --api-key ${{secrets.nuget_api_key}} --source https://forge.rainote.dev/api/packages/rainote/nuget/index.json
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: global.json
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Extract Version
|
||||
run: |
|
||||
VERSION=${GITHUB_REF#refs/tags/v}
|
||||
echo "Extracted version is $VERSION"
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
- name: Pack nuget package
|
||||
run: |
|
||||
cd RaiNote.PacketMediator
|
||||
dotnet build RaiNote.PacketMediator.csproj -c Release -p:PackageVersion=${{env.VERSION}}
|
||||
dotnet pack -c Release -p:PackageVersion=${{env.VERSION}} --output .
|
||||
- name: Push to NuGet
|
||||
run: |
|
||||
cd RaiNote.PacketMediator
|
||||
dotnet nuget push "*.nupkg" --api-key ${{secrets.nuget_api_key}} --source https://forge.rainote.dev/api/packages/rainote/nuget/index.json
|
||||
|
||||
generate-licences:
|
||||
needs: [build, preprocess]
|
||||
needs: [ build, preprocess ]
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-latest@sha256:efe7d859ca6f98a21b3b0471ab48455af597a064d719f65332d0f105aadcad65
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: |
|
||||
7.0
|
||||
8.0
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
dotnet restore
|
||||
echo "::add-path::$HOME/.dotnet/tools"
|
||||
- name: Install nuget-license
|
||||
run: dotnet tool install --global dotnet-project-licenses
|
||||
- name: Export licenses
|
||||
run: dotnet-project-licenses -i . -u --projects-filter projects_ignore_licenses.json -m -j -e -f licenses
|
||||
- name: Upload licenses
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: licenses
|
||||
path: licenses
|
||||
retention-days: 31
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: |
|
||||
7.0
|
||||
8.0
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
dotnet restore
|
||||
echo "::add-path::$HOME/.dotnet/tools"
|
||||
- name: Install nuget-license
|
||||
run: dotnet tool install --global dotnet-project-licenses
|
||||
- name: Export licenses
|
||||
run: dotnet-project-licenses -i . -u --projects-filter projects_ignore_licenses.json -m -j -e -f licenses
|
||||
- name: Upload licenses
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: licenses
|
||||
path: licenses
|
||||
retention-days: 31
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- repo: local
|
||||
hooks:
|
||||
#Use dotnet format already installed on your machine
|
||||
- id: dotnet-format
|
||||
name: dotnet-format
|
||||
language: system
|
||||
entry: dotnet format --include
|
||||
types_or: [c#, vb]
|
||||
- repo: https://github.com/Mateusz-Grzelinski/actionlint-py
|
||||
rev: v1.7.1.15
|
||||
hooks:
|
||||
- id: actionlint
|
||||
additional_dependencies: [pyflakes>=3.0.1, shellcheck-py>=0.9.0.5]
|
||||
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
|
||||
rev: v2.14.0
|
||||
hooks:
|
||||
- id: pretty-format-yaml
|
||||
args: [--autofix, --indent, '2']
|
||||
- id: dotnet-format
|
||||
name: dotnet-format
|
||||
language: system
|
||||
entry: dotnet format --include
|
||||
types_or: [ c#, vb ]
|
||||
- repo: https://github.com/Mateusz-Grzelinski/actionlint-py
|
||||
rev: v1.6.26.11
|
||||
hooks:
|
||||
- id: actionlint
|
||||
additional_dependencies: [ pyflakes>=3.0.1, shellcheck-py>=0.9.0.5 ]
|
||||
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
|
||||
rev: v2.12.0
|
||||
hooks:
|
||||
- id: pretty-format-yaml
|
||||
args: [ --autofix, --indent, '2' ]
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using RaiNote.PacketMediator;
|
||||
|
||||
namespace PacketMediator.Generator.Sample;
|
||||
|
||||
public enum OperationCode {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E = 10,
|
||||
F,
|
||||
G,
|
||||
H = 100,
|
||||
I = 5000,
|
||||
}
|
||||
|
||||
[GamePacketId(OperationCode.A)]
|
||||
public struct StructA : IIncomingPacket {
|
||||
public void Deserialize(byte[] data) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[GamePacketId(OperationCode.B)]
|
||||
public struct StructB : IPacket, IIncomingPacket {
|
||||
public void Deserialize(byte[] data) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[GamePacketId(OperationCode.E)]
|
||||
public struct StructC : IIncomingPacket {
|
||||
public void Deserialize(byte[] data) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[GamePacketId(OperationCode.H)]
|
||||
public struct StructD : IIncomingPacket {
|
||||
public void Deserialize(byte[] data) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
|
||||
public class GamePacketIdAttribute : PacketIdAttribute<OperationCode> {
|
||||
public GamePacketIdAttribute(OperationCode code) : base(code) {
|
||||
}
|
||||
}
|
||||
|
||||
public class RandomSession {
|
||||
}
|
||||
|
||||
public class HandlerA : IPacketHandler<StructA, RandomSession> {
|
||||
public async Task HandleAsync(StructA packet, RandomSession session, CancellationToken cancellationToken) {
|
||||
await Task.Delay(2000, cancellationToken);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestHandler : IPacketHandler<StructB, RandomSession> {
|
||||
public Task HandleAsync(StructB packet, RandomSession session, CancellationToken cancellationToken) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class BHandler : IPacketHandler<StructC, RandomSession> {
|
||||
public Task HandleAsync(StructC packet, RandomSession session, CancellationToken cancellationToken) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class DHandler : IPacketHandler<StructD, RandomSession> {
|
||||
public Task HandleAsync(StructD packet, RandomSession session, CancellationToken cancellationToken) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)Generated</CompilerGeneratedFilesOutputPath>
|
||||
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>PacketMediator.Generator.Sample</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\RaiNote.PacketMediator\RaiNote.PacketMediator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,28 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
<RootNamespace>PacketMediator.Generator.Tests</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.12.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.1">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\RaiNote.PacketMediator\RaiNote.PacketMediator.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,37 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using RaiNote.PacketMediator;
|
||||
using Xunit;
|
||||
|
||||
namespace PacketMediator.Generator.Tests;
|
||||
|
||||
public class SourceGeneratorWithAdditionalFilesTests
|
||||
{
|
||||
private static readonly string[] _expected = ["PacketMediatorStatic.g.cs", "StructDictionary.g.cs"];
|
||||
|
||||
[Fact]
|
||||
public void GenerateClassesBasedOnDDDRegistry()
|
||||
{
|
||||
// Create an instance of the source generator.
|
||||
var generator = new PacketMediatorGenerator();
|
||||
|
||||
// Source generators should be tested using 'GeneratorDriver'.
|
||||
GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
|
||||
|
||||
// To run generators, we can use an empty compilation.
|
||||
var compilation = CSharpCompilation.Create(nameof(SourceGeneratorWithAdditionalFilesTests));
|
||||
|
||||
// Run generators. Don't forget to use the new compilation rather than the previous one.
|
||||
driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out _);
|
||||
|
||||
// Retrieve all files in the compilation.
|
||||
var generatedFiles = newCompilation.SyntaxTrees
|
||||
.Select(t => Path.GetFileName(t.FilePath))
|
||||
.ToArray();
|
||||
|
||||
// In this case, it is enough to check the file name.
|
||||
Assert.Equivalent(_expected, generatedFiles);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using RaiNote.PacketMediator;
|
||||
using Xunit;
|
||||
|
||||
namespace PacketMediator.Generator.Tests;
|
||||
|
||||
public class SourceGeneratorWithAttributesTests
|
||||
{
|
||||
private const string VectorClassText = @"
|
||||
namespace TestNamespace;
|
||||
|
||||
[Generators.Report]
|
||||
public partial class Vector3
|
||||
{
|
||||
public float X { get; set; }
|
||||
public float Y { get; set; }
|
||||
public float Z { get; set; }
|
||||
}";
|
||||
|
||||
private const string ExpectedGeneratedClassText = @"// <auto-generated/>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TestNamespace;
|
||||
|
||||
partial class Vector3
|
||||
{
|
||||
public IEnumerable<string> Report()
|
||||
{
|
||||
yield return $""X:{this.X}"";
|
||||
yield return $""Y:{this.Y}"";
|
||||
yield return $""Z:{this.Z}"";
|
||||
}
|
||||
}
|
||||
";
|
||||
|
||||
[Fact]
|
||||
public void GenerateReportMethod()
|
||||
{
|
||||
// Create an instance of the source generator.
|
||||
var generator = new PacketMediatorGenerator();
|
||||
|
||||
// Source generators should be tested using 'GeneratorDriver'.
|
||||
var driver = CSharpGeneratorDriver.Create(generator);
|
||||
|
||||
// We need to create a compilation with the required source code.
|
||||
var compilation = CSharpCompilation.Create(nameof(SourceGeneratorWithAdditionalFilesTests),
|
||||
new[] { CSharpSyntaxTree.ParseText(VectorClassText) },
|
||||
new[]
|
||||
{
|
||||
// To support 'System.Attribute' inheritance, add reference to 'System.Private.CoreLib'.
|
||||
MetadataReference.CreateFromFile(typeof(object).Assembly.Location)
|
||||
}
|
||||
);
|
||||
|
||||
// Run generators and retrieve all results.
|
||||
var runResult = driver.RunGenerators(compilation).GetRunResult();
|
||||
|
||||
// All generated files can be found in 'RunResults.GeneratedTrees'.
|
||||
var generatedFileSyntax = runResult.GeneratedTrees.Single(t => t.FilePath.EndsWith("Vector3.g.cs"));
|
||||
|
||||
// Complex generators should be tested using text comparison.
|
||||
Assert.Equal(ExpectedGeneratedClassText,
|
||||
generatedFileSyntax.GetText().ToString(),
|
||||
ignoreLineEndingDifferences: true
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using System.Threading;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace PacketMediator.Generator.Tests.Utils;
|
||||
|
||||
public class TestAdditionalFile : AdditionalText
|
||||
{
|
||||
private readonly SourceText _text;
|
||||
|
||||
public TestAdditionalFile(string path, string text)
|
||||
{
|
||||
Path = path;
|
||||
_text = SourceText.From(text);
|
||||
}
|
||||
|
||||
public override SourceText GetText(CancellationToken cancellationToken = new()) => _text;
|
||||
|
||||
public override string Path { get; }
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
@ -11,4 +11,8 @@
|
|||
<ProjectReference Include="..\RaiNote.PacketMediator\RaiNote.PacketMediator.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
Console.WriteLine("Hello World!");
|
||||
Console.WriteLine("Hello, World!");
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
namespace PacketMediator.Samples;
|
||||
|
||||
public class Sample
|
||||
{
|
||||
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
|
@ -10,14 +10,14 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="Shouldly" Version="4.2.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
|
11
PacketMediator.Tests/UnitTest1.cs
Normal file
11
PacketMediator.Tests/UnitTest1.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
namespace PacketMediator.Tests;
|
||||
|
||||
public class UnitTest1
|
||||
{
|
||||
[Fact]
|
||||
public void Test1()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RaiNote.PacketMediator", "RaiNote.PacketMediator\RaiNote.PacketMediator.csproj", "{13243A92-DC1E-4DBF-8E2C-13CE9ABFEAD3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PacketMediator.Samples", "PacketMediator.Samples\PacketMediator.Samples.csproj", "{C20254E6-0C15-43A4-9C4A-1D0B547F7983}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PacketMediator.Tests", "PacketMediator.Tests\PacketMediator.Tests.csproj", "{C0508314-2B9C-4285-9EE5-6DED3DD8C2DA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{13243A92-DC1E-4DBF-8E2C-13CE9ABFEAD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{13243A92-DC1E-4DBF-8E2C-13CE9ABFEAD3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{13243A92-DC1E-4DBF-8E2C-13CE9ABFEAD3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{13243A92-DC1E-4DBF-8E2C-13CE9ABFEAD3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C20254E6-0C15-43A4-9C4A-1D0B547F7983}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C20254E6-0C15-43A4-9C4A-1D0B547F7983}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C20254E6-0C15-43A4-9C4A-1D0B547F7983}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C20254E6-0C15-43A4-9C4A-1D0B547F7983}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C0508314-2B9C-4285-9EE5-6DED3DD8C2DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C0508314-2B9C-4285-9EE5-6DED3DD8C2DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C0508314-2B9C-4285-9EE5-6DED3DD8C2DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C0508314-2B9C-4285-9EE5-6DED3DD8C2DA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
7
PacketMediator.slnx
Normal file
7
PacketMediator.slnx
Normal file
|
@ -0,0 +1,7 @@
|
|||
<Solution>
|
||||
<Project Path="PacketMediator.Generator\PacketMediator.Generator.Sample\PacketMediator.Generator.Sample.csproj" Type="Classic C#" />
|
||||
<Project Path="PacketMediator.Generator\PacketMediator.Generator.Tests\PacketMediator.Generator.Tests.csproj" Type="Classic C#" />
|
||||
<Project Path="PacketMediator.Samples\PacketMediator.Samples.csproj" Type="Classic C#" />
|
||||
<Project Path="PacketMediator.Tests\PacketMediator.Tests.csproj" Type="Classic C#" />
|
||||
<Project Path="RaiNote.PacketMediator\RaiNote.PacketMediator.csproj" Type="Classic C#" />
|
||||
</Solution>
|
|
@ -1,8 +0,0 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace RaiNote.PacketMediator;
|
||||
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public interface IBidirectionalPacket : IOutgoingPacket, IIncomingPacket;
|
|
@ -1,12 +0,0 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace RaiNote.PacketMediator;
|
||||
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public interface IIncomingPacket : IPacket
|
||||
{
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public void Deserialize(byte[] data);
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace RaiNote.PacketMediator;
|
||||
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public interface IOutgoingPacket : IPacket
|
||||
{
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public byte[] Serialize();
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
namespace RaiNote.PacketMediator;
|
||||
|
||||
public interface IPacket;
|
|
@ -1,35 +0,0 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
using System.Diagnostics;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace RaiNote.PacketMediator;
|
||||
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public interface IPacketHandler<in TIncomingPacket, in TSession> : IPacketHandler<TSession>
|
||||
where TIncomingPacket : IIncomingPacket
|
||||
{
|
||||
async Task<bool> IPacketHandler<TSession>.TryHandleAsync(IIncomingPacket packet, TSession session,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (packet is not TIncomingPacket tPacket)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using var activity = new ActivitySource(nameof(PacketMediator)).StartActivity(nameof(HandleAsync));
|
||||
activity?.AddTag("Handler", ToString());
|
||||
activity?.AddTag("Packet", packet.ToString());
|
||||
await HandleAsync(tPacket, session, cancellationToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public Task HandleAsync(TIncomingPacket packet, TSession session, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public interface IPacketHandler<in TSession>
|
||||
{
|
||||
Task<bool> TryHandleAsync(IIncomingPacket packet, TSession session, CancellationToken cancellationToken);
|
||||
}
|
17
RaiNote.PacketMediator/IntermediatePacketHandlerData.cs
Normal file
17
RaiNote.PacketMediator/IntermediatePacketHandlerData.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace RaiNote.PacketMediator;
|
||||
|
||||
class IntermediatePacketHandlerData {
|
||||
public IntermediatePacketHandlerData(INamedTypeSymbol? symbol, string packetHandlerIdentifier, IntermediatePacketStructHandlerData? packetStructHandlerData) {
|
||||
Symbol = symbol;
|
||||
PacketHandlerIdentifier = packetHandlerIdentifier;
|
||||
PacketStructHandlerData = packetStructHandlerData;
|
||||
}
|
||||
|
||||
public INamedTypeSymbol? Symbol { get; set; }
|
||||
public string PacketHandlerIdentifier { get; set; }
|
||||
public IntermediatePacketStructHandlerData? PacketStructHandlerData { get; set; }
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace RaiNote.PacketMediator;
|
||||
|
||||
class IntermediatePacketStructHandlerData {
|
||||
public IntermediatePacketStructHandlerData(ITypeSymbol packetStructSymbol, ITypeSymbol sessionSymbol) {
|
||||
PacketStructSymbol = packetStructSymbol;
|
||||
SessionSymbol = sessionSymbol;
|
||||
}
|
||||
|
||||
public ITypeSymbol PacketStructSymbol { get; set; }
|
||||
public ITypeSymbol SessionSymbol { get; set; }
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
using System.Threading.Channels;
|
||||
using DotNext.Collections.Generic;
|
||||
using DotNext.Linq.Expressions;
|
||||
using DotNext.Metaprogramming;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace RaiNote.PacketMediator;
|
||||
|
||||
public class PacketDistributor<TPacketIdEnum, TSession> where TPacketIdEnum : Enum
|
||||
{
|
||||
private readonly Channel<ValueTuple<byte[], TPacketIdEnum, TSession>> _channel;
|
||||
|
||||
private readonly ImmutableDictionary<TPacketIdEnum,
|
||||
Func<byte[], IIncomingPacket>> _deserializationMap;
|
||||
|
||||
private readonly ConcurrentDictionary<TPacketIdEnum, IPacketHandler<TSession>?> _packetHandlersInstantiation;
|
||||
|
||||
public PacketDistributor(IServiceProvider serviceProvider,
|
||||
IEnumerable<Assembly> sourcesContainingPackets, IEnumerable<Assembly> sourcesContainingPacketHandlers)
|
||||
{
|
||||
_channel = Channel.CreateUnbounded<ValueTuple<byte[], TPacketIdEnum, TSession>>(
|
||||
new UnboundedChannelOptions
|
||||
{
|
||||
AllowSynchronousContinuations = false,
|
||||
SingleReader = false,
|
||||
SingleWriter = false
|
||||
}
|
||||
);
|
||||
var containingPackets = sourcesContainingPackets as Assembly[] ?? sourcesContainingPackets.ToArray();
|
||||
var allIncomingPackets = GetAllPackets(containingPackets, typeof(IIncomingPacket));
|
||||
var allOutgoingPackets = GetAllPackets(containingPackets, typeof(IOutgoingPacket));
|
||||
|
||||
var packetHandlers = GetAllPacketHandlersWithId(sourcesContainingPacketHandlers);
|
||||
|
||||
PacketIdMap = allOutgoingPackets.Select(x => new { PacketId = x.Key, Type = x.Value })
|
||||
.ToImmutableDictionary(x => x.Type, x => x.PacketId);
|
||||
|
||||
var tempDeserializationMap =
|
||||
new ConcurrentDictionary<TPacketIdEnum, Func<byte[], IIncomingPacket>>();
|
||||
_packetHandlersInstantiation = new ConcurrentDictionary<TPacketIdEnum, IPacketHandler<TSession>?>();
|
||||
packetHandlers.ForEach(packetHandlerPair =>
|
||||
{
|
||||
var packetHandler =
|
||||
ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider,
|
||||
packetHandlerPair.Value
|
||||
);
|
||||
_packetHandlersInstantiation.TryAdd(packetHandlerPair.Key, packetHandler as IPacketHandler<TSession>);
|
||||
}
|
||||
);
|
||||
allIncomingPackets.ForEach(packetsType =>
|
||||
{
|
||||
var lambda = CodeGenerator.Lambda<Func<byte[], IIncomingPacket>>(fun =>
|
||||
{
|
||||
var argPacketData = fun[0];
|
||||
var newPacket = packetsType.Value.New();
|
||||
|
||||
var packetVariable = CodeGenerator.DeclareVariable(packetsType.Value, "packet");
|
||||
CodeGenerator.Assign(packetVariable, newPacket);
|
||||
CodeGenerator.Call(packetVariable, nameof(IIncomingPacket.Deserialize), argPacketData);
|
||||
|
||||
CodeGenerator.Return(packetVariable);
|
||||
}
|
||||
)
|
||||
.Compile();
|
||||
tempDeserializationMap.TryAdd(packetsType.Key, lambda);
|
||||
}
|
||||
);
|
||||
|
||||
_deserializationMap = tempDeserializationMap.ToImmutableDictionary();
|
||||
}
|
||||
|
||||
public ImmutableDictionary<Type, TPacketIdEnum> PacketIdMap { get; }
|
||||
|
||||
private static IEnumerable<KeyValuePair<TPacketIdEnum, Type>> GetAllPackets(
|
||||
IEnumerable<Assembly> sourcesContainingPackets, Type packetType)
|
||||
{
|
||||
var packetsWithId = sourcesContainingPackets.SelectMany(a => a.GetTypes()
|
||||
.Where(type => type is { IsInterface: false, IsAbstract: false } &&
|
||||
type.GetInterfaces().Contains(packetType) &&
|
||||
type.GetCustomAttributes<PacketIdAttribute<TPacketIdEnum>>().Any()
|
||||
)
|
||||
)
|
||||
.Select(type =>
|
||||
new { Type = type, Attribute = type.GetCustomAttribute<PacketIdAttribute<TPacketIdEnum>>() }
|
||||
)
|
||||
.Select(x => new KeyValuePair<TPacketIdEnum, Type>(x.Attribute!.Code, x.Type));
|
||||
|
||||
return packetsWithId;
|
||||
}
|
||||
|
||||
private static IEnumerable<KeyValuePair<TPacketIdEnum, Type>> GetAllPacketHandlersWithId(
|
||||
IEnumerable<Assembly> sourcesContainingPacketHandlers)
|
||||
{
|
||||
var packetHandlersWithId = sourcesContainingPacketHandlers.SelectMany(assembly => assembly.GetTypes()
|
||||
.Where(t =>
|
||||
t is { IsClass: true, IsAbstract: false } &&
|
||||
Array.Exists(t
|
||||
.GetInterfaces(),
|
||||
i =>
|
||||
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPacketHandler<,>)
|
||||
)
|
||||
)
|
||||
.Select(packetHandlerType => new
|
||||
{
|
||||
Type = packetHandlerType,
|
||||
PacketId = packetHandlerType
|
||||
.GetInterfaces()
|
||||
.First(t1 =>
|
||||
t1 is { IsGenericType: true } &&
|
||||
t1.GetGenericTypeDefinition() == typeof(IPacketHandler<,>)
|
||||
)
|
||||
.GetGenericArguments()
|
||||
.First(genericType => genericType.GetInterfaces()
|
||||
.Any(packetType =>
|
||||
packetType == typeof(IPacket)
|
||||
)
|
||||
)
|
||||
.GetCustomAttribute<PacketIdAttribute<TPacketIdEnum>>()
|
||||
}
|
||||
)
|
||||
)
|
||||
.Where(x => x.PacketId != null)
|
||||
.Select(x => new KeyValuePair<TPacketIdEnum, Type>(x.PacketId!.Code, x.Type));
|
||||
|
||||
return packetHandlersWithId;
|
||||
}
|
||||
|
||||
public async Task AddPacketAsync(byte[] packetData, TPacketIdEnum operationCode, TSession session)
|
||||
{
|
||||
await _channel.Writer.WriteAsync((packetData, operationCode, session));
|
||||
}
|
||||
|
||||
public async Task DequeuePacketAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (await _channel.Reader.WaitToReadAsync(cancellationToken))
|
||||
{
|
||||
while (_channel.Reader.TryRead(out var item))
|
||||
{
|
||||
await InvokePacketHandlerAsync(item, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InvokePacketHandlerAsync((byte[], TPacketIdEnum, TSession) valueTuple,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var (packetData, operationCode, session) = valueTuple;
|
||||
if (!_deserializationMap.TryGetValue(operationCode, out var func))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var packet = func(packetData);
|
||||
|
||||
await _packetHandlersInstantiation[operationCode]?.TryHandleAsync(packet, session, cancellationToken)!;
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace RaiNote.PacketMediator;
|
||||
|
||||
public class PacketDistributorService<TPacketIdEnum, TSession> : IHostedService
|
||||
where TPacketIdEnum : Enum
|
||||
{
|
||||
private readonly PacketDistributor<TPacketIdEnum, TSession> _packetDistributor;
|
||||
|
||||
public PacketDistributorService(IServiceProvider serviceProvider,
|
||||
IEnumerable<Assembly> sourcesContainingPackets, IEnumerable<Assembly> sourcesContainingPacketHandlers)
|
||||
{
|
||||
_packetDistributor = new PacketDistributor<TPacketIdEnum, TSession>(serviceProvider,
|
||||
sourcesContainingPackets,
|
||||
sourcesContainingPacketHandlers
|
||||
);
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await _packetDistributor.DequeuePacketAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task AddPacketAsync(byte[] packetData, TPacketIdEnum operationCode, TSession session)
|
||||
{
|
||||
return _packetDistributor.AddPacketAsync(packetData, operationCode, session);
|
||||
}
|
||||
|
||||
public DotNext.Optional<TPacketIdEnum> GetOperationCodeByPacketType(IPacket packet)
|
||||
{
|
||||
var type = packet.GetType();
|
||||
_packetDistributor.PacketIdMap.TryGetValue(type, out var value);
|
||||
return value ?? DotNext.Optional<TPacketIdEnum>.None;
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
namespace RaiNote.PacketMediator;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public abstract class PacketIdAttribute<TPacketIdEnum> : Attribute where TPacketIdEnum : Enum
|
||||
{
|
||||
protected PacketIdAttribute(TPacketIdEnum code)
|
||||
{
|
||||
Code = code;
|
||||
}
|
||||
|
||||
public TPacketIdEnum Code { get; }
|
||||
}
|
303
RaiNote.PacketMediator/PacketMediatorGenerator.cs
Normal file
303
RaiNote.PacketMediator/PacketMediatorGenerator.cs
Normal file
|
@ -0,0 +1,303 @@
|
|||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace RaiNote.PacketMediator;
|
||||
|
||||
[Generator]
|
||||
public class PacketMediatorGenerator : IIncrementalGenerator {
|
||||
private readonly DiagnosticDescriptor _rpmGen001Diagnostic = new(
|
||||
id: "RPMGen001",
|
||||
title: "Struct does not implement required interface",
|
||||
messageFormat:
|
||||
"The struct '{0}' must implement at least one of: 'IPacket', 'IIncomingPacket', 'IOutgoingPacket', 'IBidirectionalPacket'",
|
||||
category: "SourceGenerator",
|
||||
DiagnosticSeverity.Error,
|
||||
isEnabledByDefault: true);
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context) {
|
||||
context.RegisterPostInitializationOutput(ctx => {
|
||||
ctx.AddSource("PacketMediatorStatic.g.cs", SourceText.From(@"
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RaiNote.PacketMediator;
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
|
||||
public abstract class PacketIdAttribute<TPacketIdEnum> : Attribute where TPacketIdEnum : Enum
|
||||
{
|
||||
protected PacketIdAttribute(TPacketIdEnum code)
|
||||
{
|
||||
Code = code;
|
||||
}
|
||||
|
||||
public TPacketIdEnum Code { get; }
|
||||
}
|
||||
public interface IPacket;
|
||||
public interface IIncomingPacket : IPacket
|
||||
{
|
||||
public void Deserialize(byte[] data);
|
||||
}
|
||||
public interface IOutgoingPacket : IPacket
|
||||
{
|
||||
public byte[] Serialize();
|
||||
}
|
||||
|
||||
public interface IBidirectionalPacket : IOutgoingPacket, IIncomingPacket;
|
||||
|
||||
public interface IPacketHandler<in TIncomingPacket, in TSession> : IPacketHandler<TSession>
|
||||
where TIncomingPacket : IIncomingPacket
|
||||
{
|
||||
async Task<bool> IPacketHandler<TSession>.TryHandleAsync(IIncomingPacket packet, TSession session,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (packet is not TIncomingPacket tPacket)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using var activity = new ActivitySource(nameof(PacketMediator)).StartActivity(nameof(HandleAsync));
|
||||
activity?.AddTag(""Handler"", ToString());
|
||||
activity?.AddTag(""Packet"", packet.ToString());
|
||||
await HandleAsync(tPacket, session, cancellationToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Task HandleAsync(TIncomingPacket packet, TSession session, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public interface IPacketHandler<in TSession>
|
||||
{
|
||||
Task<bool> TryHandleAsync(IIncomingPacket packet, TSession session, CancellationToken cancellationToken);
|
||||
}
|
||||
", Encoding.UTF8));
|
||||
});
|
||||
// Find all struct declarations
|
||||
var structsWithAttributes = context.SyntaxProvider
|
||||
.CreateSyntaxProvider(
|
||||
predicate: (node, _) => node is StructDeclarationSyntax { AttributeLists.Count: > 0},
|
||||
transform: (syntaxContext, cancellationToken) => {
|
||||
var structDeclaration = (StructDeclarationSyntax)syntaxContext.Node;
|
||||
var model = syntaxContext.SemanticModel;
|
||||
var symbol =
|
||||
ModelExtensions.GetDeclaredSymbol(model, structDeclaration,
|
||||
cancellationToken: cancellationToken) as
|
||||
INamedTypeSymbol;
|
||||
var requiredInterfaces = new[] {
|
||||
"IPacket", "IIncomingPacket", "IOutgoingPacket", "IBidirectionalPacket"
|
||||
};
|
||||
var implementsInterface = symbol != null && symbol.AllInterfaces
|
||||
.Any(i => requiredInterfaces.Contains(i.Name, StringComparer.Ordinal));
|
||||
if (!implementsInterface)
|
||||
return default;
|
||||
// Check for the marker attribute
|
||||
var attribute = symbol?.GetAttributes()
|
||||
.FirstOrDefault(attr => {
|
||||
var attrClass = attr.AttributeClass;
|
||||
while (attrClass != null) {
|
||||
if (string.Equals(attrClass.Name, "PacketIdAttribute", StringComparison.Ordinal) &&
|
||||
string.Equals(attrClass.ContainingNamespace.ToDisplayString(),
|
||||
"RaiNote.PacketMediator", StringComparison.Ordinal)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
attrClass = attrClass.BaseType;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
if (attribute == null) {
|
||||
return default;
|
||||
}
|
||||
|
||||
var attributeConstructorArgument = attribute.ConstructorArguments[0];
|
||||
var enumType = attributeConstructorArgument.Type;
|
||||
var enumValue = attributeConstructorArgument.Value;
|
||||
|
||||
var enumMember = enumType?.GetMembers()
|
||||
.OfType<IFieldSymbol>()
|
||||
.FirstOrDefault(f => f.ConstantValue?.Equals(enumValue) == true);
|
||||
|
||||
var enumMaxValue = enumType?.GetMembers()
|
||||
.OfType<IFieldSymbol>().Max(x => x.ConstantValue);
|
||||
|
||||
return (symbol, structName: structDeclaration.Identifier.Text, value: enumValue, enumType,
|
||||
enumMember,
|
||||
enumMaxValue, implementsInterface);
|
||||
})
|
||||
.Where(result => result != default);
|
||||
|
||||
var packetHandlers = context.SyntaxProvider.CreateSyntaxProvider(
|
||||
predicate: (node, _) => node is ClassDeclarationSyntax,
|
||||
(syntaxContext, cancellationToken) => {
|
||||
var classDeclaration = (ClassDeclarationSyntax)syntaxContext.Node;
|
||||
var model = syntaxContext.SemanticModel;
|
||||
var symbol =
|
||||
ModelExtensions.GetDeclaredSymbol(model, classDeclaration, cancellationToken: cancellationToken) as
|
||||
INamedTypeSymbol;
|
||||
|
||||
var packetStruct = (symbol?.Interfaces.Select(interfaceSyntax => {
|
||||
if (!interfaceSyntax.Name.SequenceEqual("IPacketHandler")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (interfaceSyntax.TypeArguments.Length != 2)
|
||||
return null;
|
||||
|
||||
var genericStructArgument = interfaceSyntax.TypeArguments[0];
|
||||
var genericSessionArgument = interfaceSyntax.TypeArguments[1];
|
||||
|
||||
return new IntermediatePacketStructHandlerData(genericStructArgument, genericSessionArgument);
|
||||
}) ?? throw new InvalidOperationException("1")).FirstOrDefault(x => x != null);
|
||||
|
||||
if (packetStruct == null)
|
||||
return null;
|
||||
return new IntermediatePacketHandlerData(symbol, classDeclaration.Identifier.Text, packetStruct);
|
||||
}).Where(result => result != null);
|
||||
|
||||
var location =
|
||||
context.SyntaxProvider.CreateSyntaxProvider(predicate: static (node, _) => InterceptorPredicate(node),
|
||||
transform: static (context, ct) => InterceptorTransform(context, ct))
|
||||
.Where(candidate => candidate is not null);
|
||||
|
||||
var combinedResults = structsWithAttributes.Collect().Combine(packetHandlers.Collect());
|
||||
|
||||
// Collect and generate the dictionary
|
||||
context.RegisterSourceOutput(combinedResults, (ctx, result) => {
|
||||
var (packetStructs, packetHandlerDatas) = result;
|
||||
|
||||
var combinedInfo = packetHandlerDatas.Select(handler => {
|
||||
var respectiveStruct = packetStructs.FirstOrDefault(pStruct =>
|
||||
pStruct.structName.SequenceEqual(handler?.PacketStructHandlerData?.PacketStructSymbol.Name ??
|
||||
string.Empty));
|
||||
return (handler, respectiveStruct);
|
||||
});
|
||||
var packetHandlerData = packetHandlerDatas.FirstOrDefault();
|
||||
var usedValues = new List<long>();
|
||||
var highestValue = long.Parse(packetStructs.FirstOrDefault().enumMaxValue?.ToString());
|
||||
var ms = new MemoryStream();
|
||||
var sw = new StreamWriter(ms, Encoding.UTF8);
|
||||
sw.AutoFlush = true;
|
||||
|
||||
var enumTypeString = packetStructs.FirstOrDefault().enumType?.ToDisplayString();
|
||||
var sessionTypeString = packetHandlerData?.PacketStructHandlerData?.SessionSymbol.ToDisplayString();
|
||||
sw.WriteLine($$"""
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
public static class PacketHandlerMediator
|
||||
{
|
||||
public async static Task Handle(IServiceProvider serviceProvider, byte[] data,{{enumTypeString}} opcode ,{{packetHandlerData?.PacketStructHandlerData?.SessionSymbol}} session, CancellationToken cancellationToken){
|
||||
switch(opcode)
|
||||
{
|
||||
""");
|
||||
|
||||
foreach (var ((handler, (symbol, structName, value, _, enumMember, _, implementsInterface)), i) in
|
||||
combinedInfo.Select((value, i)=> (value, i))) {
|
||||
if (!implementsInterface) {
|
||||
var diagnostic = Diagnostic.Create(_rpmGen001Diagnostic, symbol?.Locations.FirstOrDefault(),
|
||||
structName);
|
||||
|
||||
ctx.ReportDiagnostic(diagnostic);
|
||||
continue;
|
||||
}
|
||||
|
||||
var tempVal = long.Parse(value?.ToString()!,
|
||||
new NumberFormatInfo());
|
||||
usedValues.Add(tempVal);
|
||||
sw.WriteLine($"""
|
||||
case {enumMember}:
|
||||
var packet = new {handler?.PacketStructHandlerData?.PacketStructSymbol.ToDisplayString()}();
|
||||
packet.Deserialize(data);
|
||||
_ = {handler?.Symbol?.ToDisplayString()}.HandleAsync(packet, session, cancellationToken);
|
||||
return;
|
||||
""");
|
||||
}
|
||||
|
||||
// Forced jumptable on asm generation
|
||||
for (long i = 0; i <= highestValue; i++) {
|
||||
if (!usedValues.Contains(i)) {
|
||||
sw.WriteLine($"""
|
||||
case (({enumTypeString}){i}):
|
||||
_ = StubHandler.HandleAsync(data, opcode, session, cancellationToken);
|
||||
return;
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: allow overriding
|
||||
sw.WriteLine($$"""
|
||||
}
|
||||
}
|
||||
}
|
||||
public static class StubHandler {
|
||||
public static async Task HandleAsync(byte[] data,{{enumTypeString}} opcode, {{sessionTypeString}} session, CancellationToken cancellationToken) {
|
||||
// Stub method
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
sw.Flush();
|
||||
ctx.AddSource("PacketHandlerMediator.g.cs", SourceText.From(sw.BaseStream, Encoding.UTF8, canBeEmbedded: true));
|
||||
sw.Close();
|
||||
ms.Close();
|
||||
var stringWriter = new StringWriter();
|
||||
var idWriter = new IndentedTextWriter(stringWriter);
|
||||
idWriter.WriteLine("using Microsoft.Extensions.DependencyInjection;");
|
||||
idWriter.WriteLine("public static class ServiceExtensions{");
|
||||
idWriter.Indent++;
|
||||
idWriter.WriteLine("public static void AddPacketHandlerServices(this IServiceCollection serviceCollection){");
|
||||
idWriter.Indent++;
|
||||
idWriter.WriteLine("// PacketHandler Service Generation");
|
||||
foreach (var handlerData in packetHandlerDatas) {
|
||||
idWriter.WriteLine($"serviceCollection.AddScoped<{handlerData?.Symbol?.ToDisplayString()}>();");
|
||||
}
|
||||
idWriter.Indent--;
|
||||
idWriter.WriteLine("}");
|
||||
idWriter.Indent--;
|
||||
idWriter.WriteLine("}");
|
||||
ctx.AddSource("ServiceExtensions.g.cs", SourceText.From(stringWriter.ToString(),Encoding.UTF8));
|
||||
});
|
||||
}
|
||||
|
||||
// https://andrewlock.net/creating-a-source-generator-part-11-implementing-an-interceptor-with-a-source-generator/
|
||||
private static CandidateInvocation? InterceptorTransform(GeneratorSyntaxContext context, CancellationToken cancellationToken) {
|
||||
if (context.Node is InvocationExpressionSyntax {
|
||||
Expression: MemberAccessExpressionSyntax { Name: { } nameSyntax }
|
||||
} invocation &&
|
||||
context.SemanticModel.GetOperation(context.Node, cancellationToken) is IInvocationOperation targetOperation &&
|
||||
targetOperation.TargetMethod is
|
||||
{ Name: "AddPacketHandlerServices", ContainingNamespace: { Name: "RaiNote.PacketMediator" } }
|
||||
) {
|
||||
#pragma warning disable RSEXPERIMENTAL002 // / Experimental interceptable location API
|
||||
if (context.SemanticModel.GetInterceptableLocation(invocation, cancellationToken: cancellationToken) is { } location) {
|
||||
// Return the location details and the full type details
|
||||
return new CandidateInvocation(location);
|
||||
}
|
||||
#pragma warning restore RSEXPERIMENTAL002
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool InterceptorPredicate(SyntaxNode node) {
|
||||
return node is InvocationExpressionSyntax {
|
||||
Expression: MemberAccessExpressionSyntax {
|
||||
Name.Identifier.ValueText: "AddPacketHandlerServices"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#pragma warning disable RSEXPERIMENTAL002 // / Experimental interceptable location API
|
||||
public record CandidateInvocation(InterceptableLocation Location);
|
||||
#pragma warning restore RSEXPERIMENTAL002
|
||||
}
|
9
RaiNote.PacketMediator/Properties/launchSettings.json
Normal file
9
RaiNote.PacketMediator/Properties/launchSettings.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"DebugRoslynSourceGenerator": {
|
||||
"commandName": "DebugRoslynComponent",
|
||||
"targetProject": "../PacketMediator.Generator/PacketMediator.Generator.Sample/PacketMediator.Generator.Sample.csproj"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PackageVersion>$(PackageVersion)</PackageVersion>
|
||||
|
@ -20,21 +20,31 @@
|
|||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<WarningsAsErrors>Nullable</WarningsAsErrors>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<IsRoslynComponent>true</IsRoslynComponent>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<Features>InterceptorsPreview</Features>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../README.md" Pack="true" PackagePath="\"/>
|
||||
<PackageReference Include="DotNext" Version="5.12.0"/>
|
||||
<PackageReference Include="DotNext.Metaprogramming" Version="5.12.0"/>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2024.2.0"/>
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.186">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1"/>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.11.20">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.12.19">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Polyfill" Version="1.32.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"sdk": {
|
||||
"version": "8.0.400",
|
||||
"version": "9.0.0",
|
||||
"rollForward": "latestMinor",
|
||||
"allowPrerelease": false
|
||||
"allowPrerelease": true
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue