Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: target netstandard #148

Closed
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@

# SA1623: Property summary documentation should match accessors
dotnet_diagnostic.SA1623.severity = suggestion

# SA1101: Prefix local calls with this
dotnet_diagnostic.SA1101.severity = none
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets remove this.
We use stylecop, which enforces prefixing with 'this'

3 changes: 2 additions & 1 deletion AsyncAPI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DE167614-5BCB-4046-BD4C-ABB70E9F3462}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
Common.Build.props = Common.Build.props
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LEGO.AsyncAPI.Bindings", "src\LEGO.AsyncAPI.Bindings\LEGO.AsyncAPI.Bindings.csproj", "{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LEGO.AsyncAPI.Bindings", "src\LEGO.AsyncAPI.Bindings\LEGO.AsyncAPI.Bindings.csproj", "{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
13 changes: 13 additions & 0 deletions Common.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project>
<!-- Common Properties used by all assemblies -->
<PropertyGroup>
VisualBean marked this conversation as resolved.
Show resolved Hide resolved
<LangVersion>10</LangVersion>
<TargetFramework>netstandard2.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Company>The LEGO Group</Company>
<PackageProjectUrl>https://github.com/LEGO/AsyncAPI.NET</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/LEGO/AsyncAPI.NET</RepositoryUrl>
<PackageTags>asyncapi .net openapi documentation</PackageTags>
</PropertyGroup>
</Project>
11 changes: 9 additions & 2 deletions src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,15 @@ public static TCollection Add<TCollection, TItem>(
IEnumerable<TItem> source)
where TCollection : ICollection<TItem>
{
ArgumentNullException.ThrowIfNull(destination);
ArgumentNullException.ThrowIfNull(source);
VisualBean marked this conversation as resolved.
Show resolved Hide resolved
if (destination == null)
{
throw new ArgumentNullException(nameof(destination));
}

if (source == null)
{
throw new ArgumentNullException(nameof(source));
}

if (destination is List<TItem> list)
{
Expand Down
21 changes: 6 additions & 15 deletions src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../Common.Build.props"/>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Company>The LEGO Group</Company>
<PackageProjectUrl>https://github.com/LEGO/AsyncAPI.NET</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Description>AsyncAPI.NET Bindings</Description>
<PackageTags>asyncapi .net openapi documentation</PackageTags>
<PackageId>AsyncAPI.NET.Bindings</PackageId>
<AssemblyName>LEGO.AsyncAPI.Bindings</AssemblyName>
<RootNamespace>LEGO.AsyncAPI.Bindings</RootNamespace>
<RepositoryUrl>https://github.com/LEGO/AsyncAPI.NET</RepositoryUrl>
<Description>AsyncAPI.NET Bindings</Description>
<PackageId>AsyncAPI.NET.Bindings</PackageId>
<AssemblyName>LEGO.AsyncAPI.Bindings</AssemblyName>
<RootNamespace>LEGO.AsyncAPI.Bindings</RootNamespace>
</PropertyGroup>


<ItemGroup>
<None Remove="stylecop.json" />
</ItemGroup>
Expand Down
40 changes: 37 additions & 3 deletions src/LEGO.AsyncAPI.Readers/JsonHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,49 @@ namespace LEGO.AsyncAPI.Readers
{
using System;
using System.Globalization;
using System.IO;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using LEGO.AsyncAPI.Exceptions;

/// <summary>
/// Contains helper methods for working with Json
/// </summary>
internal static class JsonHelper
{
public static string GetScalarValue(this JsonNode node)
private static readonly JsonWriterOptions WriterOptions;

static JsonHelper()
{
WriterOptions = new JsonWriterOptions()
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
Indented = false,
MaxDepth = 1,
SkipValidation = true,
};
}

/// <summary>
/// Takes a <see cref="JsonValue"/> and converts it into a string value.
/// </summary>
/// <param name="jsonValue">The node to convert.</param>
/// <returns>The string value.</returns>
public static string GetScalarValue(this JsonValue jsonValue)
{
var scalarNode = node is JsonValue value ? value : throw new AsyncApiException($"Expected scalar value");
return Convert.ToString(scalarNode.GetValue<object>(), CultureInfo.InvariantCulture);
using (MemoryStream memoryStream = new MemoryStream())
using (Utf8JsonWriter writer = new Utf8JsonWriter(memoryStream, WriterOptions))
{
jsonValue.WriteTo(writer);
writer.Flush();
memoryStream.Position = 0;
using (StreamReader reader = new StreamReader(memoryStream))
{
string value = reader.ReadToEnd();
return value.Trim('"');
Copy link
Contributor Author

@ByronMayne ByronMayne Mar 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old method would not handle conversation of DateTime's correctly which was causing it to fail. Instead now we use the built in logic to serialization of values and then just remove the contain quotes. There are new unit tests that cover this logic

}
}
}

public static JsonNode ParseJsonString(string jsonString)
Expand Down
17 changes: 5 additions & 12 deletions src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<Import Project="../../Common.Build.props"/>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
<Company>The LEGO Group</Company>
<PackageProjectUrl>https://github.com/LEGO/AsyncAPI.NET</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Description>AsyncAPI.NET Readers for JSON and YAML documents</Description>
<PackageTags>asyncapi .net openapi documentation</PackageTags>
<PackageId>AsyncAPI.NET.Readers</PackageId>
<AssemblyName>LEGO.AsyncAPI.Readers</AssemblyName>
<RootNamespace>LEGO.AsyncAPI.Readers</RootNamespace>
<RepositoryUrl>https://github.com/LEGO/AsyncAPI.NET</RepositoryUrl>
<Description>AsyncAPI.NET Readers for JSON and YAML documents</Description>
<PackageId>AsyncAPI.NET.Readers</PackageId>
<AssemblyName>LEGO.AsyncAPI.Readers</AssemblyName>
<RootNamespace>LEGO.AsyncAPI.Readers</RootNamespace>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public string GetReferencePointer()
return null;
}

return refNode.GetScalarValue();
return refNode.AsValue().GetScalarValue();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This AsValue already has a properly worded exception if it's the wrong type. Before inside of GetScalerValue it would throw a rather vague exception. I changed GetScalerValue to only work with NodeValue.

}

public string GetScalarValue(ValueNode key)
Expand Down
2 changes: 1 addition & 1 deletion src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public override string GetScalarValue()
{
if (this.cachedScalarValue == null)
{
this.cachedScalarValue = this.node.GetScalarValue();
this.cachedScalarValue = this.node.AsValue().GetScalarValue();
}

return this.cachedScalarValue;
Expand Down
33 changes: 26 additions & 7 deletions src/LEGO.AsyncAPI.Readers/YamlConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,41 @@ public static JsonNode ToJsonNode(this YamlNode yaml)
};
}

private static JsonValue ToJsonValue(this YamlScalarNode yaml)
public static JsonValue ToJsonValue(this YamlScalarNode yaml)
{
string value = yaml.Value;

switch (yaml.Style)
{
case ScalarStyle.Plain:
return decimal.TryParse(yaml.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var d)
Copy link
Contributor Author

@ByronMayne ByronMayne Mar 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

decimal was a weird choice for capturing all number, is most serializers I have worked with they will default to a double. This code now correctly supports parsing to int, double, DateTime, bool, and `string. Totally feel free to push back on anything, after all it's your repository :)

Copy link
Collaborator

@VisualBean VisualBean Mar 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decimal was ultimately chosen as double looses precision.
And since decimal (and double for that matter) can house simple integers, we never try to convert to regular ol' int.

double looses precision with certain conversions.
A few examples
1.2345678901234567890123456789E25
12345678901234567890.0123456789

? JsonValue.Create(d)
: bool.TryParse(yaml.Value, out var b)
? JsonValue.Create(b)
: JsonValue.Create(yaml.Value)!;
// We need to guess the types just based on it's format, so that means parsing
if (int.TryParse(value, out int intValue))
{
return JsonValue.Create<int>(intValue);
}

if (double.TryParse(value, out double doubleValue))
{
return JsonValue.Create<double>(doubleValue);
}

if (DateTime.TryParse(value, out DateTime dateTimeValue))
{
return JsonValue.Create<DateTime>(dateTimeValue);
}

if (bool.TryParse(value, out bool boolValue))
{
return JsonValue.Create<bool>(boolValue);
}

return JsonValue.Create<string>(value);
case ScalarStyle.SingleQuoted:
case ScalarStyle.DoubleQuoted:
case ScalarStyle.Literal:
case ScalarStyle.Folded:
case ScalarStyle.Any:
return JsonValue.Create(yaml.Value);
return JsonValue.Create<string>(yaml.Value);
default:
throw new ArgumentOutOfRangeException();
}
Expand Down
19 changes: 6 additions & 13 deletions src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<Import Project="../../Common.Build.props"/>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Company>The LEGO Group</Company>
<PackageProjectUrl>https://github.com/LEGO/AsyncAPI.NET</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Description>AsyncAPI.NET models</Description>
<PackageTags>asyncapi .net openapi documentation</PackageTags>
<PackageId>AsyncAPI.NET</PackageId>
<AssemblyName>LEGO.AsyncAPI</AssemblyName>
<RootNamespace>LEGO.AsyncAPI</RootNamespace>
<RepositoryUrl>https://github.com/LEGO/AsyncAPI.NET</RepositoryUrl>
<Description>AsyncAPI.NET models</Description>
<PackageId>AsyncAPI.NET</PackageId>
<AssemblyName>LEGO.AsyncAPI</AssemblyName>
<RootNamespace>LEGO.AsyncAPI</RootNamespace>
</PropertyGroup>

<ItemGroup>
<None Remove="stylecop.json" />
</ItemGroup>
Expand All @@ -27,6 +19,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="8.0.2" />
VisualBean marked this conversation as resolved.
Show resolved Hide resolved

<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>$(MSBuildProjectName).Tests</_Parameter1>
Expand Down
38 changes: 30 additions & 8 deletions src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ namespace LEGO.AsyncAPI.Writers
using System;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;

public static class SpecialCharacterStringExtensions
{
private static readonly Regex numberRegex = new Regex("^[+-]?[0-9]*\\.?[0-9]*$", RegexOptions.Compiled, TimeSpan.FromSeconds(1));

// Plain style strings cannot start with indicators.
// http://www.yaml.org/spec/1.2/spec.html#indicator//
private static readonly char[] yamlIndicators =
Expand Down Expand Up @@ -100,10 +103,15 @@ public static class SpecialCharacterStringExtensions
/// Escapes all special characters and put the string in quotes if necessary to
/// get a YAML-compatible string.
/// </summary>
internal static string GetYamlCompatibleString(this string input)
internal static string GetYamlCompatibleString(this string? input)
{
if (input == null)
{
return "null";
}

// If string is an empty string, wrap it in quote to ensure it is not recognized as null.
if (input == "")
if (input.Length == 0)
{
return "''";
}
Expand Down Expand Up @@ -163,7 +171,7 @@ internal static string GetYamlCompatibleString(this string input)
input = input.Replace("\x1e", "\\x1e");
input = input.Replace("\x1f", "\\x1f");

return $"\"{input}\"";
return $"'{input}'";
}

// If string
Expand All @@ -183,11 +191,25 @@ internal static string GetYamlCompatibleString(this string input)
return $"'{input}'";
}

// If string can be mistaken as a number, a boolean, or a timestamp,
// wrap it in quote to indicate that this is indeed a string, not a number, a boolean, or a timestamp
if (decimal.TryParse(input, NumberStyles.Float, CultureInfo.InvariantCulture, out var _) ||
bool.TryParse(input, out var _) ||
DateTime.TryParse(input, out var _))
// Handle lexemes that can be intperated as as string
// https://yaml.org/spec/1.2-old/spec.html#id2761292
switch (input.ToLower())
{
// Example 2.20. Floating Point
case "-.inf":
case ".inf":
case ".nan":
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These cases were all missed

Copy link
Collaborator

@VisualBean VisualBean Mar 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good find!
I think the datetime case has gone missing however.
The simplest case would be to simply always quote them.

cases

December 31, 2022
2022-12-31 23:59:59
12/31/2022 23:59:59
2022-12-31
2022/12/31
2022-12-31T23:59:59Z

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might get caught by the forbidden combinations though. - dont remember off the top of my head.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be covered by the regex however just because I am not 100% sure let me add a test to explicitly check for that.

// Example 2.21. Miscellaneous
case "null":

// Booleans
case "true":
case "false":
return $"'{input}'";
}

// Handle numbers
if (numberRegex.IsMatch(input))
{
return $"'{input}'";
}
Expand Down
Loading
Loading