Skip to content

Commit

Permalink
Imported Tingle.Extensions.Primitives
Browse files Browse the repository at this point in the history
  • Loading branch information
mburumaxwell committed Mar 7, 2024
1 parent fd186cf commit 326640b
Show file tree
Hide file tree
Showing 68 changed files with 45,300 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .codespellrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[codespell]
skip = .git,*.pdf,*.svg,*.csv,pnpm-lock.yaml,*/wwwroot
skip = .git,*.pdf,*.svg,*.csv,*.g.cs,countries-20200124.json,iso-639-3.tab,iso-4217.json,countries.json,currencies.json,languages.json
# some modules, parts of regexes, and variable names to ignore
ignore-words-list = aci
1 change: 1 addition & 0 deletions .github/workflows/cleanup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
- { name: 'Tingle.Extensions.Http.Authentication' }
- { name: 'Tingle.Extensions.JsonPatch' }
- { name: 'Tingle.Extensions.PhoneValidators' }
- { name: 'Tingle.Extensions.Primitives' }
- { name: 'Tingle.Extensions.Processing' }
- { name: 'Tingle.Extensions.PushNotifications' }
- { name: 'Tingle.Extensions.Serilog' }
Expand Down
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"cSpell.words": [
"etag",
"Ksuid",
"libphonenumber",
"Newtonsoft",
"Serilog",
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This repository contains projects/libraries for adding useful functionality to d
|[`Tingle.Extensions.Http.Authentication`](https://www.nuget.org/packages/Tingle.Extensions.Http.Authentication/)|Authentication providers for use with HttpClient and includes support for DI via `Microsoft.Extensions.Http`. See [docs](./src/Tingle.Extensions.Http.Authentication/README.md) and [sample](./samples/HttpAuthenticationSample).|
|[`Tingle.Extensions.JsonPatch`](https://www.nuget.org/packages/Tingle.Extensions.JsonPatch/)|JSON Patch (RFC 6902) support for .NET to easily generate JSON Patch documents using `System.Text.Json` for client applications. See [docs](./src/Tingle.Extensions.JsonPatch/README.md).|
|[`Tingle.Extensions.PhoneValidators`](https://www.nuget.org/packages/Tingle.Extensions.PhoneValidators/)|Convenience for validation of phone numbers either via attributes or resolvable services. See [docs](./src/Tingle.Extensions.PhoneValidators/README.md).|
|[`Tingle.Extensions.Primitives`](https://www.nuget.org/packages/Tingle.Extensions.Primitives/)|Extra primitive types such as `Money`, `Currency`, `Duration` etc. See [docs](./src/Tingle.Extensions.Primitives/README.md).|
|[`Tingle.Extensions.Processing`](https://www.nuget.org/packages/Tingle.Extensions.Processing/)|Helpers for making processing of bulk in memory tasks. See [docs](./src/Tingle.Extensions.Processing/README.md).|
|[`Tingle.Extensions.PushNotifications`](https://www.nuget.org/packages/Tingle.Extensions.PushNotifications/)|Clients for sending push notifications via FCM, APNS etc. See [docs](./src/Tingle.Extensions.PushNotifications/README.md).|
|[`Tingle.Extensions.Serilog`](https://www.nuget.org/packages/Tingle.Extensions.Serilog/)|Extensions for working with [Serilog](https://serilog.net/). Including easier registration when working with different host setups, and general basics. See [docs](./src/Tingle.Extensions.Serilog/README.md) and [sample](./samples/SerilogSample).|
Expand Down
23 changes: 23 additions & 0 deletions Tingle.Extensions.sln
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.JsonPatch
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.PhoneValidators", "src\Tingle.Extensions.PhoneValidators\Tingle.Extensions.PhoneValidators.csproj", "{F46ADD04-B716-4E9B-9799-7C47DDDB08FC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tingle.Extensions.Primitives", "src\Tingle.Extensions.Primitives\Tingle.Extensions.Primitives.csproj", "{3012AF4E-270B-4293-9C7B-51003F00A8D6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.Processing", "src\Tingle.Extensions.Processing\Tingle.Extensions.Processing.csproj", "{A803DE4B-B050-48F2-82A1-8E947D8FB96C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.PushNotifications", "src\Tingle.Extensions.PushNotifications\Tingle.Extensions.PushNotifications.csproj", "{6A1901A5-D01F-47E2-9ED5-2BE4CCE95100}"
Expand Down Expand Up @@ -63,6 +65,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.JsonPatch
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.PhoneValidators.Tests", "tests\Tingle.Extensions.PhoneValidators.Tests\Tingle.Extensions.PhoneValidators.Tests.csproj", "{526B37AD-256A-445A-9A42-E5C53989B11E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tingle.Extensions.Primitives.Tests", "tests\Tingle.Extensions.Primitives.Tests\Tingle.Extensions.Primitives.Tests.csproj", "{33E2AC82-DC46-42CA-A41B-0D8D97019402}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.Processing.Tests", "tests\Tingle.Extensions.Processing.Tests\Tingle.Extensions.Processing.Tests.csproj", "{978023EA-2ED5-4A28-96AD-4BB914EF2BE5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.PushNotifications.Tests", "tests\Tingle.Extensions.PushNotifications.Tests\Tingle.Extensions.PushNotifications.Tests.csproj", "{A8AB6597-DEBB-4828-AE1B-A11FD818E428}"
Expand All @@ -88,6 +92,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SerilogSample", "samples\Se
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TokensSample", "samples\TokensSample\TokensSample.csproj", "{AC04C113-8F75-43BA-8FEE-987475A87C58}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "generator", "generator", "{9071B0C9-DE4D-411D-A9D3-CB7326CBBD80}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StaticDataGenerator", "generator\StaticDataGenerator.csproj", "{009C5985-9DD4-45A8-A31E-4E6B7FE5EE78}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -234,6 +242,18 @@ Global
{AC04C113-8F75-43BA-8FEE-987475A87C58}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC04C113-8F75-43BA-8FEE-987475A87C58}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC04C113-8F75-43BA-8FEE-987475A87C58}.Release|Any CPU.Build.0 = Release|Any CPU
{3012AF4E-270B-4293-9C7B-51003F00A8D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3012AF4E-270B-4293-9C7B-51003F00A8D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3012AF4E-270B-4293-9C7B-51003F00A8D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3012AF4E-270B-4293-9C7B-51003F00A8D6}.Release|Any CPU.Build.0 = Release|Any CPU
{33E2AC82-DC46-42CA-A41B-0D8D97019402}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33E2AC82-DC46-42CA-A41B-0D8D97019402}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33E2AC82-DC46-42CA-A41B-0D8D97019402}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33E2AC82-DC46-42CA-A41B-0D8D97019402}.Release|Any CPU.Build.0 = Release|Any CPU
{009C5985-9DD4-45A8-A31E-4E6B7FE5EE78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{009C5985-9DD4-45A8-A31E-4E6B7FE5EE78}.Debug|Any CPU.Build.0 = Debug|Any CPU
{009C5985-9DD4-45A8-A31E-4E6B7FE5EE78}.Release|Any CPU.ActiveCfg = Release|Any CPU
{009C5985-9DD4-45A8-A31E-4E6B7FE5EE78}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -274,6 +294,9 @@ Global
{37ED98F0-3129-49B8-BF60-3BDEBBB34822} = {CE7124E9-01B3-4AD6-8B8F-FB302E60FB7F}
{CFEE0754-9DAF-4AA6-98F3-477F065D7DC4} = {CE7124E9-01B3-4AD6-8B8F-FB302E60FB7F}
{AC04C113-8F75-43BA-8FEE-987475A87C58} = {CE7124E9-01B3-4AD6-8B8F-FB302E60FB7F}
{3012AF4E-270B-4293-9C7B-51003F00A8D6} = {9546186D-D4E1-4EDB-956E-1F81C7F4DB72}
{33E2AC82-DC46-42CA-A41B-0D8D97019402} = {815F0941-3B70-4705-A583-AF627559595C}
{009C5985-9DD4-45A8-A31E-4E6B7FE5EE78} = {9071B0C9-DE4D-411D-A9D3-CB7326CBBD80}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B9323FCA-8E8B-4176-A463-87D202EC4552}
Expand Down
115 changes: 115 additions & 0 deletions generator/AbstractGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System.CodeDom.Compiler;
using System.Text;
using System.Text.Json;

namespace StaticDataGenerator;

public interface IGenerator
{
public string OutputFilePath { get; }
public string DataFileName { get; }
public string? OutputFilePathJson { get; }
Task GenerateAsync(CancellationToken cancellationToken = default);
}

public abstract class AbstractGenerator<T> : IGenerator where T : class, new()
{
private const string Header = @"//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by the StaticDataGenerator source generator
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
#nullable enable
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
";

private static readonly JsonSerializerOptions serializerOptions = new(JsonSerializerDefaults.Web);

private readonly IHostEnvironment environment;
private readonly FileDownloader downloader;

protected AbstractGenerator(IHostEnvironment environment, FileDownloader downloader, string dataFileName)
{
this.environment = environment ?? throw new ArgumentNullException(nameof(environment));
this.downloader = downloader ?? throw new ArgumentNullException(nameof(downloader));
DataFileName = dataFileName ?? throw new ArgumentNullException(nameof(dataFileName));
ClassName = GetType().Name.Replace("CodeGenerator", "").Replace("Generator", "");

var targetDirectory = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), $"../src/Tingle.Extensions.Primitives/Generated"));
OutputFilePath = Path.Combine(targetDirectory, $"{ClassName}.g.cs");
OutputFilePathJson = environment.ContentRootFileProvider.GetFileInfo($"{ClassName.ToLower()}.json").PhysicalPath;
}

public string ClassName { get; }

public string OutputFilePath { get; }

public string DataFileName { get; }

public string? OutputFilePathJson { get; }

public async Task GenerateAsync(CancellationToken cancellationToken = default)
{
var data = (await ParseAsync(cancellationToken)) ?? throw new InvalidOperationException("Deserialization failed");

var sb = new StringBuilder();
var writer = new IndentedTextWriter(new StringWriter(sb));

writer.WriteLine(Header);

writer.WriteLine("using System.Collections;");
writer.WriteLine("using System.Collections.Generic;");

// write the namespace
writer.WriteLine();
writer.WriteLine("namespace Tingle.Extensions.Primitives;");
writer.WriteLine();

// begin the class
writer.WriteLine($"internal partial class {ClassName}");
writer.WriteLine("{");

// write the data
writer.Indent++;
WriteCode(writer, data);
writer.Indent--;

// end the class
writer.WriteLine("}");
await writer.FlushAsync(cancellationToken);

// output to file
await File.WriteAllTextAsync(OutputFilePath, sb.ToString(), Encoding.UTF8, cancellationToken);

// generate the JSON file
if (OutputFilePathJson is not null)
{
sb.Clear();
writer = new IndentedTextWriter(new StringWriter(sb), " "); // only 2 spaces for JSON
WriteJson(writer, data);
await writer.FlushAsync(cancellationToken);
if (sb.Length > 0)
{
await File.WriteAllTextAsync(OutputFilePathJson, sb.ToString(), Encoding.UTF8, cancellationToken);
}
}
}

protected virtual async ValueTask<T?> ParseAsync(CancellationToken cancellationToken = default)
{
var fi = environment.ContentRootFileProvider.GetFileInfo(Path.Combine("Files", DataFileName));
using var dataStream = fi.CreateReadStream();
return await ParseAsync(dataStream, cancellationToken);
}

protected virtual ValueTask<T?> ParseAsync(Stream dataStream, CancellationToken cancellationToken = default)
=> JsonSerializer.DeserializeAsync<T>(dataStream, serializerOptions, cancellationToken);

protected abstract void WriteCode(IndentedTextWriter writer, T data);
protected virtual void WriteJson(IndentedTextWriter writer, T data) { }
}
86 changes: 86 additions & 0 deletions generator/CountriesGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System.CodeDom.Compiler;
using System.Text.Json.Serialization;

namespace StaticDataGenerator;

internal class CountriesGenerator(IHostEnvironment environment, FileDownloader downloader)
: AbstractGenerator<List<CountriesGenerator.CountryImpl>>(environment, downloader, "countries-20200124.json")
{
internal struct CountryImpl
{
[JsonPropertyName("name")]
public string Name { get; set; }

[JsonPropertyName("name_safe")]
public string NameSafe { get; set; }

[JsonPropertyName("continent")]
public string Continent { get; set; }

[JsonPropertyName("code_numeric")]
public string CodeNumeric { get; set; }

[JsonPropertyName("code_2")]
public string Code2 { get; set; }

[JsonPropertyName("code_3")]
public string Code3 { get; set; }

[JsonPropertyName("flag_url")]
public string FlagUrl { get; set; }

[JsonPropertyName("currency")]
public string Currency { get; set; }
}

protected override void WriteCode(IndentedTextWriter writer, List<CountryImpl> data)
{
// remove entries without all the information
data.RemoveAll(impl =>
{
return impl.Name is null
|| impl.Continent is null
|| impl.CodeNumeric is null
|| impl.Code2 is null
|| impl.Code3 is null
|| impl.FlagUrl is null
|| impl.Currency is null;
});


writer.WriteLine("// The currencies");
foreach (var impl in data)
{
writer.Write($"public static readonly Country {impl.NameSafe}");
writer.WriteLine($" = new(\"{impl.Name}\", \"{impl.Continent}\", \"{impl.CodeNumeric}\", \"{impl.Code2}\", \"{impl.Code3}\", \"{impl.FlagUrl}\", \"{impl.Currency}\");");
}
writer.Indent--;

// numeric code mapping
writer.WriteLine();
writer.WriteLine("// The array/list");
writer.WriteLine("internal static readonly IReadOnlyList<Country> All = new List<Country>");
writer.WriteLine("{");
writer.Indent++;
foreach (var impl in data)
{
writer.WriteLine($"{ClassName}.{impl.NameSafe},");
}
writer.Indent--;
writer.WriteLine("};");
writer.Indent--;
}

protected override void WriteJson(IndentedTextWriter writer, List<CountryImpl> data)
{
writer.WriteLine("[");
writer.Indent++;
foreach (var impl in data)
{
writer.Write($"{{ \"name\": \"{impl.Name}\", \"codeAlpha2\": \"{impl.Code2}\", \"codeAlpha3\": \"{impl.Code3}\"}}");
writer.WriteLine(data.IndexOf(impl) == data.Count - 1 ? "" : ",");
}
writer.Indent--;
writer.WriteLine("]");
}
}
84 changes: 84 additions & 0 deletions generator/CurrenciesGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System.CodeDom.Compiler;
using System.Text.Json.Serialization;

namespace StaticDataGenerator;

internal class CurrenciesGenerator(IHostEnvironment environment, FileDownloader downloader)
: AbstractGenerator<Dictionary<string, CurrenciesGenerator.CurrencyImpl>>(environment, downloader, "iso-4217.json")
{
internal struct CurrencyImpl
{
[JsonPropertyName("symbol")]
public string Symbol { get; set; }

[JsonPropertyName("code")]
public string Code { get; set; }

[JsonPropertyName("name")]
public string Name { get; set; }

[JsonPropertyName("symbolNative")]
public string SymbolNative { get; set; }

[JsonPropertyName("decimalDigits")]
public int? DecimalDigits { get; set; }

[JsonPropertyName("namePlural")]
public string NamePlural { get; set; }
}

protected override void WriteCode(IndentedTextWriter writer, Dictionary<string, CurrencyImpl> data)
{
// remove entries without all the information
foreach (var kvp in data.ToList())
{
var impl = kvp.Value;
if (impl.Symbol is null
|| impl.Code is null
|| impl.Name is null
|| impl.SymbolNative is null
|| impl.DecimalDigits is null
|| impl.NamePlural is null)
{
data.Remove(kvp.Key);
}
}

writer.WriteLine("// The currencies");
foreach (var kvp in data)
{
var impl = kvp.Value;
writer.Write($"public static readonly Currency {impl.Code}");
writer.WriteLine($" = new(\"{impl.Code}\", \"{impl.Symbol}\", \"{impl.SymbolNative}\", \"{impl.Name}\", \"{impl.NamePlural}\", \"{impl.DecimalDigits}\");");
}
writer.Indent--;

writer.WriteLine();
writer.Indent++;
writer.WriteLine("// The array/list");
writer.WriteLine("internal static readonly IReadOnlyList<Currency> All = new List<Currency>");
writer.WriteLine("{");
writer.Indent++;
foreach (var kvp in data)
{
var impl = kvp.Value;
writer.WriteLine($"{ClassName}.{impl.Code},");
}
writer.Indent--;
writer.WriteLine("};");
}

protected override void WriteJson(IndentedTextWriter writer, Dictionary<string, CurrencyImpl> data)
{
var impls = data.Values.ToList();
writer.WriteLine("[");
writer.Indent++;
foreach (var impl in impls)
{
writer.Write($"{{ \"name\": \"{impl.Name}\", \"code\": \"{impl.Code}\"}}");
writer.WriteLine(impls.IndexOf(impl) == data.Count - 1 ? "" : ",");
}
writer.Indent--;
writer.WriteLine("]");
}
}
7 changes: 7 additions & 0 deletions generator/FileDownloader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma warning disable CS9113 // Parameter is unread.

namespace StaticDataGenerator;

public class FileDownloader(HttpClient client)
{
}
Loading

0 comments on commit 326640b

Please sign in to comment.