Skip to content

Commit f565ecc

Browse files
authored
feat: #6 New source of truth (#7)
A new source of truth uses the JSON embedded directly in the PDF specification from https://usb.org. Dependency on https://github.com/IntergatedCircuits/hid-usage-tables has been removed. All new code generator is written using Roslyn Source generators instead of T4 templating. Updated to latest HID Usage Table specifications, and latest NuGets. Note, there are quite a large number of enumeration value changes, etc. due to the update in specifications and tweaks to naming.
1 parent c256897 commit f565ecc

File tree

123 files changed

+27436
-26981
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

123 files changed

+27436
-26981
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,4 +337,8 @@ ASALocalRun/
337337
.localhistory/
338338

339339
# BeatPulse healthcheck temp database
340-
healthchecksdb
340+
healthchecksdb
341+
342+
# Ignore cached HID Table specifications
343+
/HIDDevices/Generated/*.json
344+
/HIDDevices/Generated/*.pdf

.gitmodules

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +0,0 @@
1-
[submodule "HIDDevices/Usages/hid-usage-tables"]
2-
path = HIDDevices/Usages/hid-usage-tables
3-
url = https://github.com/IntergatedCircuits/hid-usage-tables
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
## Release 3.0
2+
3+
### New Rules
4+
5+
Rule ID | Category | Severity | Notes
6+
--------|----------|----------|--------------------
7+
HUT0001 | Generation | Info | Generation succeeded
8+
HUT00FF | Generation | Error | Generation was cancelled
9+
HUT1001 | Caching | Warning | Could not find/create HID Usage Table Caching folder.
10+
HUT1002 | Caching | Warning | Caching disabled as cache file locations could not be determined.
11+
HUT1003 | Caching | Error | PDF Not found
12+
HUT2001 | Deserialization | Error | Deserialization of the JSON HID USage Tables failed.
13+
HUT2002 | Deserialization | Error | JSON attachment not found in PDF file.
14+
HUT2003 | Deserialization | Error | Error extracting JSON attachment from PDF file.
15+
16+
; See https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md for explanation
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
### New Rules
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License").
2+
// See the LICENSE file in the project root for more information.
3+
4+
using System.Globalization;
5+
using System.Runtime.CompilerServices;
6+
using System.Text;
7+
using Microsoft.CodeAnalysis;
8+
9+
namespace HIDDevices.Generator;
10+
11+
public static class Diagnostics
12+
{
13+
// ReSharper disable ArrangeObjectCreationWhenTypeEvident - See https://github.com/dotnet/roslyn-analyzers/issues/5957
14+
#pragma warning disable IDE0090 // Use 'new(...)'
15+
public static readonly DiagnosticDescriptor Completed = new DiagnosticDescriptor(
16+
"HUT0001",
17+
"Generation succeeded",
18+
"Generation succeeded for version {0}. {1} usages in {2} usage pages in {3:g}.",
19+
"Generation",
20+
DiagnosticSeverity.Info,
21+
true);
22+
23+
public static readonly DiagnosticDescriptor Cancelled = new DiagnosticDescriptor(
24+
"HUT00FF",
25+
"Cancelled",
26+
"Generation was cancelled",
27+
"Generation",
28+
DiagnosticSeverity.Error,
29+
true);
30+
31+
public static readonly DiagnosticDescriptor CacheFolderCreationFailed = new DiagnosticDescriptor(
32+
"HUT1001",
33+
"Could not find/create HID Usage Table Caching folder",
34+
"Folder {0} could not be created: {1}",
35+
"Caching",
36+
DiagnosticSeverity.Warning,
37+
true);
38+
39+
public static readonly DiagnosticDescriptor CachingDisabled = new DiagnosticDescriptor(
40+
"HUT1002",
41+
"Caching disabled",
42+
"Caching disabled as cache file locations could not be determined",
43+
"Caching",
44+
DiagnosticSeverity.Warning,
45+
true);
46+
47+
public static readonly DiagnosticDescriptor PdfNotFound = new DiagnosticDescriptor(
48+
"HUT1003",
49+
"PDF Not found",
50+
"Failed to find the PDF file {0}: {1}",
51+
"Caching",
52+
DiagnosticSeverity.Error,
53+
true);
54+
55+
public static readonly DiagnosticDescriptor JsonDeserializationFailed = new DiagnosticDescriptor(
56+
"HUT2001",
57+
"JSON Deserialization Failure",
58+
"Deserialization of the JSON HID USage Tables failed: {0}",
59+
"Deserialization",
60+
DiagnosticSeverity.Error,
61+
true);
62+
63+
public static readonly DiagnosticDescriptor JsonAttachmentNotFound = new DiagnosticDescriptor(
64+
"HUT2002",
65+
"JSON attachment not found in PDF file",
66+
"Could not find the JSON attachment in '{0}'",
67+
"Deserialization",
68+
DiagnosticSeverity.Error,
69+
true);
70+
71+
public static readonly DiagnosticDescriptor JsonExtractionFailed = new DiagnosticDescriptor(
72+
"HUT2003",
73+
"Error extracting JSON attachment from PDF file",
74+
"Extracting the JSON attachment from '{0}' failed: {1}",
75+
"Deserialization",
76+
DiagnosticSeverity.Error,
77+
true);
78+
// ReSharper restore ArrangeObjectCreationWhenTypeEvident
79+
#pragma warning restore IDE0090 // Use 'new(...)'
80+
81+
/// <summary>
82+
/// Adds a <see cref="Diagnostic" /> to the users compilation based on a <see cref="DiagnosticDescriptor" />.
83+
/// </summary>
84+
/// <param name="context">The execution context.</param>
85+
/// <param name="descriptor">The diagnostic descriptor.</param>
86+
/// <param name="location"></param>
87+
/// <param name="messageArgs"></param>
88+
/// <remarks>
89+
/// The severity of the diagnostic may cause the compilation to fail, depending on the <see cref="Compilation" />
90+
/// settings.
91+
/// </remarks>
92+
/// <seealso cref="GeneratorExecutionContext.ReportDiagnostic(Diagnostic)" />
93+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
94+
public static void Report(this GeneratorExecutionContext context, DiagnosticDescriptor descriptor,
95+
Location location, params object[] messageArgs)
96+
=> context.ReportDiagnostic(Diagnostic.Create(descriptor, location, messageArgs));
97+
98+
/// <summary>
99+
/// Get's a C#-safe version of a name.
100+
/// </summary>
101+
/// <param name="unsafe"></param>
102+
/// <returns></returns>
103+
public static string GetSafe(this string @unsafe)
104+
{
105+
// Create safe name
106+
var builder = new StringBuilder(@unsafe.Length);
107+
var afterSpace = true;
108+
foreach (var ch in @unsafe)
109+
{
110+
switch (char.GetUnicodeCategory(ch))
111+
{
112+
case UnicodeCategory.UppercaseLetter:
113+
case UnicodeCategory.LowercaseLetter:
114+
case UnicodeCategory.TitlecaseLetter:
115+
case UnicodeCategory.ModifierLetter:
116+
case UnicodeCategory.OtherLetter:
117+
// Always allowed in C# class names
118+
break;
119+
120+
case UnicodeCategory.ConnectorPunctuation:
121+
// Language specification allows '_' as first character.
122+
if (builder.Length < 1 && ch != '_')
123+
{
124+
continue;
125+
}
126+
127+
break;
128+
129+
case UnicodeCategory.LetterNumber:
130+
case UnicodeCategory.NonSpacingMark:
131+
case UnicodeCategory.SpacingCombiningMark:
132+
case UnicodeCategory.DecimalDigitNumber:
133+
case UnicodeCategory.Format:
134+
// Only valid after first character, so add a '_' prefix.
135+
if (builder.Length < 1)
136+
{
137+
builder.Append('_');
138+
}
139+
140+
break;
141+
142+
case UnicodeCategory.SpaceSeparator:
143+
afterSpace = true;
144+
continue;
145+
default:
146+
// Skip characters
147+
continue;
148+
}
149+
150+
char c;
151+
if (afterSpace)
152+
{
153+
afterSpace = false;
154+
c = char.ToUpperInvariant(ch);
155+
}
156+
else
157+
{
158+
c = ch;
159+
}
160+
161+
builder.Append(c);
162+
}
163+
164+
return builder.ToString();
165+
}
166+
167+
public static IndentStringBuilder AppendComment(this IndentStringBuilder builder, string comment,
168+
bool cStyle = false)
169+
{
170+
if (cStyle)
171+
{
172+
builder.AppendLine("/*").Indent(" *").AppendLine(comment).Outdent().AppendLine(" */");
173+
}
174+
else
175+
{
176+
builder.Indent("// ").AppendLine(comment).Outdent();
177+
}
178+
179+
return builder;
180+
}
181+
182+
public static IndentStringBuilder AppendSummary(this IndentStringBuilder builder, string comment)
183+
=> builder.Indent("// ").AppendLine("<summary>").Indent().AppendLine(comment).Outdent().AppendLine("</summary>")
184+
.Outdent();
185+
186+
public static IndentStringBuilder AppendDescription(this IndentStringBuilder builder, string description)
187+
=> builder.Append("[Description(").AppendQuoted(description).AppendLine(")]");
188+
189+
public static IndentStringBuilder OpenBrace(this IndentStringBuilder builder)
190+
=> builder.AppendLine("{").Indent();
191+
192+
public static IndentStringBuilder CloseBrace(this IndentStringBuilder builder)
193+
=> builder.Outdent().AppendLine("}");
194+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
<LangVersion>latest</LangVersion>
6+
<Nullable>enable</Nullable>
7+
<IsRoslynComponent>true</IsRoslynComponent>
8+
<Configurations>Debug;Release;GenerateFromCache;GenerateFromSource</Configurations>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="iTextSharp" Version="5.5.13.3" GeneratePathProperty="true" PrivateAssets="all" >
13+
<NoWarn>NU1701</NoWarn>
14+
</PackageReference>
15+
<PackageReference Include="BouncyCastle" Version="1.8.9" GeneratePathProperty="true" PrivateAssets="all" >
16+
<NoWarn>NU1701</NoWarn>
17+
</PackageReference>
18+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" PrivateAssets="all" />
19+
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
20+
<PrivateAssets>all</PrivateAssets>
21+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
22+
</PackageReference>
23+
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" GeneratePathProperty="true" PrivateAssets="all" />
24+
</ItemGroup>
25+
26+
27+
<PropertyGroup>
28+
<GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
29+
</PropertyGroup>
30+
31+
<Target Name="GetDependencyTargetPaths">
32+
<ItemGroup>
33+
<TargetPathWithTargetPlatformMoniker Include="$(PKGNewtonsoft_Json)\lib\netstandard2.0\Newtonsoft.Json.dll" IncludeRuntimeDependency="false" />
34+
<TargetPathWithTargetPlatformMoniker Include="$(PKGiTextSharp)\lib\iTextSharp.dll" IncludeRuntimeDependency="false" />
35+
<TargetPathWithTargetPlatformMoniker Include="$(PKGBouncyCastle)\lib\BouncyCastle.Crypto.dll" IncludeRuntimeDependency="false" />
36+
</ItemGroup>
37+
</Target>
38+
</Project>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License").
2+
// See the LICENSE file in the project root for more information.
3+
4+
using System.Collections.Generic;
5+
using Newtonsoft.Json;
6+
7+
namespace HIDDevices.Generator;
8+
9+
/// <summary>
10+
/// Generates HidUsages on-the-fly (according to a pattern) rather than at compile-time.
11+
/// This is used primarily by the Button/Ordinal pages, where there are 65535 UsageIds (all 'pre-defined').
12+
/// It would be silly (and wasteful) to pre-generate all of these, so rather it is done on demand.
13+
/// </summary>
14+
[JsonObject(MemberSerialization.OptIn)]
15+
public class HidUsageGenerator
16+
{
17+
/// <summary>
18+
/// Initializes a new instance of the <see cref="HidUsageGenerator" /> class.
19+
/// </summary>
20+
/// <param name="namePrefix">Name of every generated Usage.</param>
21+
/// <param name="startUsageId">First valid UsageId.</param>
22+
/// <param name="endUsageId">Last valid UsageId.</param>
23+
/// <param name="kinds">Kinds to associate with generated UsageIds.</param>
24+
public HidUsageGenerator(string namePrefix, ushort startUsageId, ushort endUsageId,
25+
IReadOnlyCollection<HidUsageKind> kinds)
26+
{
27+
NamePrefix = namePrefix;
28+
SafeNamePrefix = namePrefix.GetSafe();
29+
StartUsageId = startUsageId;
30+
EndUsageId = endUsageId;
31+
Kinds = kinds;
32+
}
33+
34+
/// <summary>
35+
/// Gets the Name all generated Usages shall have.
36+
/// </summary>
37+
[JsonProperty]
38+
public string NamePrefix { get; }
39+
40+
/// <summary>
41+
/// Gets the Safe Name all generated Usages shall have.
42+
/// </summary>
43+
public string SafeNamePrefix { get; }
44+
45+
/// <summary>
46+
/// Gets the first valid UsageId for this generator. All IDs between <see cref="StartUsageId" /> and
47+
/// <see cref="EndUsageId" /> (inclusive) are valid.
48+
/// </summary>
49+
[JsonProperty]
50+
public ushort StartUsageId { get; }
51+
52+
/// <summary>
53+
/// Gets the ast valid UsageId for this generator. All IDs between <see cref="StartUsageId" /> and
54+
/// <see cref="EndUsageId" /> (inclusive) are valid.
55+
/// </summary>
56+
[JsonProperty]
57+
public ushort EndUsageId { get; }
58+
59+
/// <summary>
60+
/// Gets the Usage kinds as defined the HID Usage Table. Most UsageIds will only have a single kind.
61+
/// </summary>
62+
[JsonProperty]
63+
public IReadOnlyCollection<HidUsageKind> Kinds { get; }
64+
65+
/// <inheritdoc />
66+
public override string ToString() => $"[{StartUsageId}-{EndUsageId}]";
67+
}

0 commit comments

Comments
 (0)