Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ISBN.sln
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ISBN", "src\ISBN\ISBN.cspro
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "src\Tests\Tests.csproj", "{981FC772-B5F4-4A38-9EA6-50904FBD0C9A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GroupsGenerator", "src\GroupsGenerator\GroupsGenerator.csproj", "{1A0A023F-2E23-44AC-8ABA-4BE5FAFF7F61}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -28,6 +30,10 @@ Global
{981FC772-B5F4-4A38-9EA6-50904FBD0C9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{981FC772-B5F4-4A38-9EA6-50904FBD0C9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{981FC772-B5F4-4A38-9EA6-50904FBD0C9A}.Release|Any CPU.Build.0 = Release|Any CPU
{1A0A023F-2E23-44AC-8ABA-4BE5FAFF7F61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A0A023F-2E23-44AC-8ABA-4BE5FAFF7F61}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A0A023F-2E23-44AC-8ABA-4BE5FAFF7F61}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A0A023F-2E23-44AC-8ABA-4BE5FAFF7F61}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
92 changes: 92 additions & 0 deletions src/GroupsGenerator/GroupsGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Newtonsoft.Json.Linq;
using Scriban;
using static System.Net.Mime.MediaTypeNames;

public record Range(string Min, string Max);
public record GroupDef(string Key, string Name, Range[] Ranges);

[Generator(LanguageNames.CSharp)]
public class GroupsGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context) { }

public void Execute(GeneratorExecutionContext context)
{
var jsFile = context.AdditionalFiles.FirstOrDefault(f => Path.GetFileName(f.Path) == "groups.js");

if (jsFile == null)
{
context.ReportDiagnostic(Diagnostic.Create("ISBN001", "Compiler", "groups.js not found",
DiagnosticSeverity.Error, DiagnosticSeverity.Error, true, 1));
return;
}

var text = File.ReadAllText(jsFile.Path);
text = text[text.IndexOf('{')..];

var source = RenderTemplate(text);

context.AddSource("ISBN.Groups.g", SourceText.From(source, Encoding.UTF8));
}

public static string RenderTemplate(string json)
{
var data = JObject.Parse(json);
var groups = new ConcurrentDictionary<string, ConcurrentDictionary<char, ConcurrentDictionary<string, GroupDef>>>();

foreach (var prop in data.Properties())
{
if (prop?.Value is JObject obj &&
obj.Value<string>("name") is string name &&
obj.Property("ranges") is JProperty ranges &&
ranges?.Value is JArray rangeValues)
{
var result = new List<Range>();
foreach (var range in rangeValues)
{
var values = range.Values<string>().ToArray();
if (values.Length != 2 ||
values[0] == null || values[1] == null)
continue;

result.Add(new Range(values[0]!, values[1]!));
}

var parts = prop.Name.Split('-');
var prefix = parts[0];
var group = parts[1];
var groupFirstDigit = group[0];

groups.GetOrAdd(prefix, _ => new())
.GetOrAdd(groupFirstDigit, _ => new())
[group] = new GroupDef(group, name, result.ToArray());
}
}

var path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().ManifestModule.FullyQualifiedName), "Template.sbntxt");
Template template;

if (File.Exists(path))
{
template = Template.Parse(File.ReadAllText(path));
}
else
{
using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("GroupsGenerator.Template.sbntxt");
using var reader = new StreamReader(stream);
template = Template.Parse(reader.ReadToEnd());
}

var output = template.Render(new { map = groups }, member => member.Name);

return output;
}
}
22 changes: 22 additions & 0 deletions src/GroupsGenerator/GroupsGenerator.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<LangVersion>Preview</LangVersion>
<SignAssembly>false</SignAssembly>
</PropertyGroup>

<ItemGroup>
<EmbeddedResource Include="Template.sbntxt" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" PrivateAssets="all" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" PrivateAssets="all" />
<PackageReference Include="Polyfill.NET" Version="1.0.12" PrivateAssets="all" />
<PackageReference Include="Scriban" Version="5.5.0" PrivateAssets="all" />
</ItemGroup>

</Project>
44 changes: 44 additions & 0 deletions src/GroupsGenerator/Template.sbntxt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System.Collections.Generic;

partial record ISBN
{
static readonly Dictionary<string, Dictionary<char, Dictionary<string, GroupDef>>> groups = new()
{
{{~ for pair in map ~}}
{
"{{ pair.Key }}",
new()
{
{{~ for cp in pair.Value ~}}
{
'{{ cp.Key }}',
new()
{
{{~ for gp in cp.Value ~}}
{
"{{~ gp.Key ~}}",
new("{{~ gp.Value.Key ~}}", "{{~ gp.Value.Name ~}}", new Range[]
{
{{~ for r in gp.Value.Ranges ~}}
new Range("{{~ r.Min ~}}", "{{~ r.Max ~}}"),
{{~ end ~}}
})
},
{{~ end ~}}
}
},
{{~ end ~}}
}
},
{{~ end ~}}
};
}
37 changes: 0 additions & 37 deletions src/ISBN/EmbeddedResource.cs

This file was deleted.

45 changes: 2 additions & 43 deletions src/ISBN/ISBN.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Linq;

/// <summary>
/// Adapted to C# from https://github.com/inventaire/isbn3
Expand All @@ -13,44 +10,6 @@ public partial record ISBN
record Range(string Min, string Max);
record GroupDef(string Key, string Name, Range[] Ranges);

static readonly ConcurrentDictionary<string, ConcurrentDictionary<char, ConcurrentDictionary<string, GroupDef>>> groupsMap = new();

static ISBN()
{
var raw = EmbeddedResource.GetContent("groups.js");
var json = raw[raw.IndexOf('{')..];
var data = JObject.Parse(json);

foreach (var prop in data.Properties())
{
if (prop?.Value is JObject obj &&
obj.Value<string>("name") is string name &&
obj.Property("ranges") is JProperty ranges &&
ranges?.Value is JArray rangeValues)
{
var result = new List<Range>();
foreach (var range in rangeValues)
{
var values = range.Values<string>().ToArray();
if (values.Length != 2 ||
values[0] == null || values[1] == null)
continue;

result.Add(new Range(values[0]!, values[1]!));
}

var parts = prop.Name.Split('-');
var prefix = parts[0];
var group = parts[1];
var groupFirstDigit = group[0];

groupsMap.GetOrAdd(prefix, _ => new())
.GetOrAdd(groupFirstDigit, _ => new())
[group] = new GroupDef(group, name, result.ToArray());
}
}
}

/// <summary>
/// Tries parsing the given <paramref name="isbn"/> as a <see cref="ISBN"/> structure.
/// </summary>
Expand Down Expand Up @@ -175,7 +134,7 @@ static bool VerifyChecksum10(string isbn)
static (GroupDef group, string restAfterGroup)? GetGroup(string isbn)
{
var prefix = isbn[..3];
if (!groupsMap.TryGetValue(prefix, out var groupMap))
if (!groups.TryGetValue(prefix, out var groupMap))
return null;

var restAfterPrefix = isbn[3..];
Expand Down
13 changes: 8 additions & 5 deletions src/ISBN/ISBN.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,22 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="IsExternalInit" Version="1.0.3" PrivateAssets="all" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Nullable" Version="1.3.1" PrivateAssets="all" />
<PackageReference Include="NuGetizer" Version="0.9.0" PrivateAsset="all" />
<PackageReference Include="Polyfill.NET" Version="1.0.12" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<None Remove="groups.js" />
<None Include="..\..\readme.md" PackagePath="readme.md" />
<Compile Remove="Range.cs" Condition="$(TargetFramework) != 'netstandard2.0'" />
<AdditionalFiles Include="groups.js" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="groups.js" CopyToOutputDirectory="PreserveNewest" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" GeneratePathProperty="true" Pack="false" ExcludeAssets="all" />
<PackageReference Include="Scriban" Version="5.5.0" GeneratePathProperty="true" Pack="false" ExcludeAssets="all" />
<ProjectReference Include="..\GroupsGenerator\GroupsGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" Pack="false" />
<Analyzer Include="$(PkgNewtonsoft_Json)\lib\netstandard2.0\Newtonsoft.Json.dll" />
<Analyzer Include="$(PkgScriban)\lib\netstandard2.0\Scriban.dll" />
</ItemGroup>

</Project>
Loading