From 45f741f7fc8fdb1e58d95dee9a5660fdd7f6950a Mon Sep 17 00:00:00 2001 From: Daniel Santa Rosa Date: Sun, 5 Feb 2023 21:20:37 -0300 Subject: [PATCH] add feature to intercept property binding (#20) Add a functionally to intercept the property bind and convert it to primitive value or do something else. --- .github/workflows/ci-benchmark.yml | 12 +- .github/workflows/ci-documentation.yml | 2 +- .github/workflows/ci.yml | 8 +- .gitignore | 12 +- README.md | 74 +- .../Attributes/ObjectMapperTest.cs | 4 +- .../Attributes/ReadPropertyTest.cs | 6 +- .../Attributes/WritePropertyTest.cs | 4 +- app/Spinner.Test/Cache/ParserTypeCacheTest.cs | 26 + .../Extensions/SpanExtensionsTest.cs | 6 +- app/Spinner.Test/Helper/FileInspect.cs | 2 +- app/Spinner.Test/Helper/Parses/CacheParser.cs | 12 + .../Helper/Parses/DecimalParser.cs | 14 + app/Spinner.Test/Models/NothingDecimal.cs | 32 + .../Models/NothingDecimalReader.cs | 28 + .../Models/NothingLeftNoObjectMapper.cs | 12 +- app/Spinner.Test/Models/NothingNoAttibute.cs | 14 +- .../{NothingLeft.cs => NothingPadLeft.cs} | 18 +- .../{NothingRight.cs => NothingPadRight.cs} | 18 +- app/Spinner.Test/Models/NothingReader.cs | 10 +- app/Spinner.Test/Spinner.Test.csproj | 6 +- app/Spinner.Test/SpinnerReadTest.cs | 104 ++- app/Spinner.Test/SpinnerWriteTest.cs | 187 ++--- app/Spinner.Test/coverage.opencover.xml | 697 ++++++++++++++++++ app/Spinner.sln | 23 +- .../Attributes/ObjectMapperAttribute.cs | 2 +- .../Attributes/ReadPropertyAttribute.cs | 18 +- .../Attributes/WritePropertyAttribute.cs | 10 +- app/Spinner/Cache/ParserTypeCache.cs | 33 + app/Spinner/Enums/PaddingType.cs | 2 +- .../Exceptions/PropertyNotMappedException.cs | 2 +- app/Spinner/Extensions/SpanExtensions.cs | 2 +- app/Spinner/Guards/Guard.cs | 30 + app/Spinner/Parsers/ITypeParse.cs | 7 + app/Spinner/Spinner.cs | 148 ++-- app/Spinner/Spinner.csproj | 2 +- app/makefile | 20 +- .../MethodToBenchmark.cs} | 9 + .../ObjectBench.cs | 0 .../ObjectBenchWithParser.cs | 53 ++ bench/Spinner.Benchmark/ParserAdress.cs | 12 + .../Program.cs | 0 .../Spinner.Benchmark.csproj} | 0 docs/docs/intro.md | 4 +- docs/docs/mapping-object-in-string.md | 8 +- docs/docs/mapping-string-in-object.md | 4 +- docs/docusaurus.config.js | 14 +- docs/package.json | 6 +- 48 files changed, 1343 insertions(+), 374 deletions(-) create mode 100644 app/Spinner.Test/Cache/ParserTypeCacheTest.cs create mode 100644 app/Spinner.Test/Helper/Parses/CacheParser.cs create mode 100644 app/Spinner.Test/Helper/Parses/DecimalParser.cs create mode 100644 app/Spinner.Test/Models/NothingDecimal.cs create mode 100644 app/Spinner.Test/Models/NothingDecimalReader.cs rename app/Spinner.Test/Models/{NothingLeft.cs => NothingPadLeft.cs} (55%) rename app/Spinner.Test/Models/{NothingRight.cs => NothingPadRight.cs} (57%) create mode 100644 app/Spinner.Test/coverage.opencover.xml create mode 100644 app/Spinner/Cache/ParserTypeCache.cs create mode 100644 app/Spinner/Guards/Guard.cs create mode 100644 app/Spinner/Parsers/ITypeParse.cs rename bench/{Writer.Benchmark/WriterBench.cs => Spinner.Benchmark/MethodToBenchmark.cs} (76%) rename bench/{Writer.Benchmark => Spinner.Benchmark}/ObjectBench.cs (100%) create mode 100644 bench/Spinner.Benchmark/ObjectBenchWithParser.cs create mode 100644 bench/Spinner.Benchmark/ParserAdress.cs rename bench/{Writer.Benchmark => Spinner.Benchmark}/Program.cs (100%) rename bench/{Writer.Benchmark/Writer.Benchmark.csproj => Spinner.Benchmark/Spinner.Benchmark.csproj} (100%) diff --git a/.github/workflows/ci-benchmark.yml b/.github/workflows/ci-benchmark.yml index 01846c71..f80462a4 100644 --- a/.github/workflows/ci-benchmark.yml +++ b/.github/workflows/ci-benchmark.yml @@ -13,7 +13,7 @@ jobs: env: APP: "${{ github.workspace }}/app" PROJECT_BIN5: "./app/Spinner/bin/Release/net5.0" - PROJECT_BENCH_WRITER: "./bench/Writer.Benchmark" + PROJECT_BENCH: "./bench/Spinner.Benchmark" runs-on: ubuntu-latest @@ -52,20 +52,20 @@ jobs: dotnet-version: 7.0.100 - name: Clean Dependencies 📦 - run: dotnet clean ${{env.PROJECT_BENCH_WRITER }} + run: dotnet clean ${{env.PROJECT_BENCH }} - name: Restore Dependencies 📦 - run: dotnet restore ${{env.PROJECT_BENCH_WRITER }} + run: dotnet restore ${{env.PROJECT_BENCH }} - name: Build ⚙️ - run: dotnet build ${{env.PROJECT_BENCH_WRITER }} -c Release -p:Version=$BUILD_VERSION --no-restore + run: dotnet build ${{env.PROJECT_BENCH }} -c Release -p:Version=$BUILD_VERSION --no-restore - name: Publish 📮 - run: dotnet publish ${{env.PROJECT_BENCH_WRITER }} -f net7.0 -c Release --verbosity normal -o ./publish.benchmarks/ + run: dotnet publish ${{env.PROJECT_BENCH }} -f net6.0 -c Release --verbosity normal -o ./publish.benchmarks/ - name: Run 🏋️‍♂️ if: github.event_name == 'pull_request' - run: dotnet ./publish.benchmarks/Writer.Benchmark.dll -f "Writer.Benchmark.*" + run: dotnet ./publish.benchmarks/Spinner.Benchmark.dll -f "Spinner.Benchmark.*" - name: Upload Results 📂 if: github.event_name == 'pull_request' diff --git a/.github/workflows/ci-documentation.yml b/.github/workflows/ci-documentation.yml index 83367c03..4dfed9ec 100644 --- a/.github/workflows/ci-documentation.yml +++ b/.github/workflows/ci-documentation.yml @@ -24,7 +24,7 @@ jobs: - name: Setup Node 🔧 uses: actions/setup-node@v1 with: - node-version: "15.x" + node-version: "16.x" cache: "npm" cache-dependency-path: ./docs/package-lock.json path: $DOC_FOLDER diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f597c9b..150e9b92 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,9 +43,12 @@ jobs: with: dotnet-version: ${{ matrix.dotnet-version }} - - name: Setup Stryker 📦 + - name: Setup Stryker 🔧 run: dotnet tool install -g dotnet-stryker + - name: Install dotnet-format tool 🔧 + run: dotnet tool install -g dotnet-format + - name: Clean Dependencies 📦 run: dotnet clean ${{env.PROJECT_SLN }} @@ -55,6 +58,9 @@ jobs: - name: Build ⚙️ run: dotnet build ${{env.PROJECT_SLN }} -c Release -p:Version=$BUILD_VERSION --no-restore + - name: Format ♻️ + run: dotnet format ${{env.APP }} -v diag --severity error --no-restore + - name: Test 🧪 run: dotnet test ${{env.PROJECT_SLN }} --configuration Release --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover diff --git a/.gitignore b/.gitignore index 14a815f6..c5fc3480 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,14 @@ node_modules BenchmarkDotNet.Artifacts BenchmarkDotNet.Artifacts/* -*/BenchmarkDotNet.Artifacts/* \ No newline at end of file +*/BenchmarkDotNet.Artifacts/* + +.sarif +.sarif/* +*/.sarif +*/.sarif/* + +infer-out/* +*/infer-out + +diagram.html diff --git a/README.md b/README.md index 9b333e2a..7522626e 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,25 @@
- +
# - - - - - - - - build status - - - build status - - - License - - - Nuget - - - views - - - Release - - - Repo Size - - - - - - Coverity Scan Build Status - - - Coverity Scan Build Status - - - - - -
- -### Introduction +[![Contributors](https://img.shields.io/github/contributors/Daniel-iel/Spinner)](https://www.nuget.org/packages/Spinner/) +[![Activity](https://img.shields.io/github/commit-activity/m/Daniel-iel/Spinner)](https://www.nuget.org/packages/Spinner/) +[![ci](https://github.com/Daniel-iel/Spinner/actions/workflows/ci.yml/badge.svg)](https://github.com/Daniel-iel/Spinner/actions/workflows/ci.yml/badge.svg/) +[![documentation](https://github.com/Daniel-iel/Spinner/actions/workflows/ci-documentation.yml/badge.svg)](https://github.com/Daniel-iel/Spinner/actions/workflows/ci-documentation.yml/badge.svg/) +[![ci](https://github.com/Daniel-iel/Spinner/actions/workflows/ci-benchmark.yml/badge.svg)](https://github.com/Daniel-iel/Spinner/actions/workflows/ci-benchmark.yml/badge.svg/) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.md) +[![Downloads](https://img.shields.io/nuget/dt/Spinner)](https://www.nuget.org/packages/Spinner/) +[![Visitors](https://visitor-badge-reloaded.herokuapp.com/badge?page_id=https://github.com/Daniel-iel/Spinner&logo=github) +[![Release](https://img.shields.io/nuget/v/spinner)](https://www.nuget.org/packages/Spinner/) +[![Repo Size](https://img.shields.io/github/repo-size/Daniel-iel/spinner)](https://www.nuget.org/packages/Spinner/) +[![Code Factor](https://www.codefactor.io/repository/github/Daniel-iel/spinner/badge)](https://www.codefactor.io/repository/github/Daniel-iel/spinner) +[![coverity](https://img.shields.io/coverity/scan/24116.svg)](https://scan.coverity.com/projects/spinner) +[![Code Factor](https://api.meercode.io/badge/Daniel-iel/Spinner?type=ci-score&lastDay=31)](https://scan.coverity.com/projects/spinner) +[![Snyk](https://img.shields.io/snyk/vulnerabilities/github/Daniel-iel/Spinner)](https://scan.coverity.com/projects/spinner) + +## Introduction Spinner is a simple object mapper, it’s useful to communicate to any system that uses a positional string as communication. @@ -71,17 +43,17 @@ dotnet add package Spinner [ObjectMapper(length: 50)] public struct Nothing { - public Nothing(string name, string adress) + public Nothing(string name, string webSite) { this.Name = name; - this.Adress = adress; + this.WebSite = webSite; } [WriteProperty(length: 20, order: 1, paddingChar: ' ')] public string Name { get; private set; } [WriteProperty(length: 30, order: 2, paddingChar: ' ')] - public string Adress { get; private set; } + public string WebSite { get; private set; } } var nothing = new Nothing("spinner", "www.spinner.com.br"); @@ -96,9 +68,7 @@ See Learn: Getting Started for setting up your project [here](https://spinnerfra ## Contributors - - - +[![Daniel-Profile](https://github.com/Daniel-iel.png?size=40)](https://github.com/Daniel-iel) ## Support @@ -110,4 +80,4 @@ Our code and framework are licensed under the MIT license. Please see the licens # Stats -Repobeats analytics image +[![Repobeats analytics image](https://repobeats.axiom.co/api/embed/c3f5ed375e6e703c23a90745aaee5bca46ebd0fd.svg)] diff --git a/app/Spinner.Test/Attributes/ObjectMapperTest.cs b/app/Spinner.Test/Attributes/ObjectMapperTest.cs index 807bf687..254a8982 100644 --- a/app/Spinner.Test/Attributes/ObjectMapperTest.cs +++ b/app/Spinner.Test/Attributes/ObjectMapperTest.cs @@ -1,8 +1,8 @@ using Spinner.Attribute; using Spinner.Test.Helper; -using Xunit; using System; using System.Reflection; +using Xunit; namespace Spinner.Test.Attributes { @@ -48,4 +48,4 @@ public void Should_ValidateHowManyAttributesExistsInObjectMapperAttributeFile() Assert.Equal(AttributeTargets.Struct | AttributeTargets.Class, attributeUsage.ValidOn); } } -} +} \ No newline at end of file diff --git a/app/Spinner.Test/Attributes/ReadPropertyTest.cs b/app/Spinner.Test/Attributes/ReadPropertyTest.cs index 5c31a693..91ebdc42 100644 --- a/app/Spinner.Test/Attributes/ReadPropertyTest.cs +++ b/app/Spinner.Test/Attributes/ReadPropertyTest.cs @@ -1,8 +1,8 @@ using Spinner.Attribute; using Spinner.Test.Helper; -using Xunit; using System; using System.Reflection; +using Xunit; namespace Spinner.Test.Attributes { @@ -15,7 +15,7 @@ public void Should_ValidateHowManyContructorsExistsInReadPropertyAttributeFile() ConstructorInfo[] constructors = FileInspect.GetConstructors(); // Assert - Assert.Single(constructors); + Assert.Equal(2, constructors.Length); } [Fact] @@ -51,4 +51,4 @@ public void Should_ValidateHowManyAttributesExistsInReadPropertyAttributeFile() Assert.Equal(AttributeTargets.Property, attributeUsage.ValidOn); } } -} +} \ No newline at end of file diff --git a/app/Spinner.Test/Attributes/WritePropertyTest.cs b/app/Spinner.Test/Attributes/WritePropertyTest.cs index 3dc53cae..d6a8ac47 100644 --- a/app/Spinner.Test/Attributes/WritePropertyTest.cs +++ b/app/Spinner.Test/Attributes/WritePropertyTest.cs @@ -1,8 +1,8 @@ using Spinner.Attribute; using Spinner.Test.Helper; -using Xunit; using System; using System.Reflection; +using Xunit; namespace Spinner.Test.Attributes { @@ -77,4 +77,4 @@ public void Should_ValidateHowManyAttributesExistsInWritePropertyFile() Assert.Equal(AttributeTargets.Property, attributeUsage.ValidOn); } } -} +} \ No newline at end of file diff --git a/app/Spinner.Test/Cache/ParserTypeCacheTest.cs b/app/Spinner.Test/Cache/ParserTypeCacheTest.cs new file mode 100644 index 00000000..61063a81 --- /dev/null +++ b/app/Spinner.Test/Cache/ParserTypeCacheTest.cs @@ -0,0 +1,26 @@ +using Spinner.Cache; +using Spinner.Parsers; +using Spinner.Test.Helper.Parses; +using Xunit; + +namespace Spinner.Test.Cache +{ + public class ParserTypeCacheTest + { + [Fact] + public void TryGet_WhenCalled_ShoudReturnParsedTypeFromCache() + { + // Arrange + const string key = "key"; + ITypeParse parser = new CacheParser(); + + ParserTypeCache.Add(key, parser); + // Act + var typeCached = ParserTypeCache.TryGet(key, out var typeInCache); + + // Assert + Assert.True(typeCached); + Assert.IsType(typeInCache); + } + } +} \ No newline at end of file diff --git a/app/Spinner.Test/Extensions/SpanExtensionsTest.cs b/app/Spinner.Test/Extensions/SpanExtensionsTest.cs index 81c54119..b455556b 100644 --- a/app/Spinner.Test/Extensions/SpanExtensionsTest.cs +++ b/app/Spinner.Test/Extensions/SpanExtensionsTest.cs @@ -1,6 +1,6 @@ -using System; +using Spinner.Extensions; +using System; using Xunit; -using Spinner.Extensions; namespace Spinner.Test.Extensions { @@ -74,4 +74,4 @@ public void ShouldNot_PadRight(string value, string expected, ushort numberOfPad Assert.Equal(expected, result.ToString()); } } -} +} \ No newline at end of file diff --git a/app/Spinner.Test/Helper/FileInspect.cs b/app/Spinner.Test/Helper/FileInspect.cs index 099f074c..3fb02b39 100644 --- a/app/Spinner.Test/Helper/FileInspect.cs +++ b/app/Spinner.Test/Helper/FileInspect.cs @@ -17,4 +17,4 @@ public static ConstructorInfo[] GetConstructors() return Type.GetConstructors(); } } -} +} \ No newline at end of file diff --git a/app/Spinner.Test/Helper/Parses/CacheParser.cs b/app/Spinner.Test/Helper/Parses/CacheParser.cs new file mode 100644 index 00000000..e6556f95 --- /dev/null +++ b/app/Spinner.Test/Helper/Parses/CacheParser.cs @@ -0,0 +1,12 @@ +using Spinner.Parsers; + +namespace Spinner.Test.Helper.Parses +{ + internal sealed class CacheParser : ITypeParse + { + public object Parser(object obj) + { + return obj; + } + } +} \ No newline at end of file diff --git a/app/Spinner.Test/Helper/Parses/DecimalParser.cs b/app/Spinner.Test/Helper/Parses/DecimalParser.cs new file mode 100644 index 00000000..116b9144 --- /dev/null +++ b/app/Spinner.Test/Helper/Parses/DecimalParser.cs @@ -0,0 +1,14 @@ +using Spinner.Parsers; +using System.Globalization; + +namespace Spinner.Test.Helper.Parses +{ + internal sealed class DecimalParser : ITypeParse + { + public object Parser(object obj) + { + string value = obj.ToString().Insert(2, "."); + return decimal.Parse(value, new CultureInfo("en-US")); + } + } +} \ No newline at end of file diff --git a/app/Spinner.Test/Models/NothingDecimal.cs b/app/Spinner.Test/Models/NothingDecimal.cs new file mode 100644 index 00000000..c8d0ec5b --- /dev/null +++ b/app/Spinner.Test/Models/NothingDecimal.cs @@ -0,0 +1,32 @@ +using Spinner.Attribute; +using System; + +namespace Spinner.Test.Models +{ + [ObjectMapper(length: 4)] + internal struct NothingDecimal : IEquatable + { + public NothingDecimal(string value) + { + Value = value; + } + + [WriteProperty(length: 4, order: 0, paddingChar: ' ')] + public string Value { get; set; } + + public bool Equals(NothingDecimal other) + { + return Value == other.Value; + } + + public override int GetHashCode() + { + return HashCode.Combine(Value); + } + + public override bool Equals(object obj) + { + return obj is NothingDecimal nothingDecimal && Equals(nothingDecimal); + } + } +} \ No newline at end of file diff --git a/app/Spinner.Test/Models/NothingDecimalReader.cs b/app/Spinner.Test/Models/NothingDecimalReader.cs new file mode 100644 index 00000000..76f8ba44 --- /dev/null +++ b/app/Spinner.Test/Models/NothingDecimalReader.cs @@ -0,0 +1,28 @@ +using Spinner.Attribute; +using Spinner.Test.Helper.Parses; +using System; + +namespace Spinner.Test.Models +{ + [ObjectMapper(length: 4)] + internal sealed class NothingDecimalReader : IEquatable + { + [ReadProperty(start: 0, length: 4, type: typeof(DecimalParser))] + public decimal Value { get; set; } + + public bool Equals(NothingDecimalReader other) + { + return Value == other.Value; + } + + public override int GetHashCode() + { + return HashCode.Combine(Value); + } + + public override bool Equals(object obj) + { + return obj is NothingDecimalReader nothingDecimalReader && Equals(nothingDecimalReader); + } + } +} \ No newline at end of file diff --git a/app/Spinner.Test/Models/NothingLeftNoObjectMapper.cs b/app/Spinner.Test/Models/NothingLeftNoObjectMapper.cs index 864ab6bb..925fb0c4 100644 --- a/app/Spinner.Test/Models/NothingLeftNoObjectMapper.cs +++ b/app/Spinner.Test/Models/NothingLeftNoObjectMapper.cs @@ -5,27 +5,27 @@ namespace Spinner.Test.Models { internal struct NothingLeftNoObjectMapper : IEquatable { - public NothingLeftNoObjectMapper(string name, string adress) + public NothingLeftNoObjectMapper(string name, string webSite) { Name = name; - Adress = adress; + WebSite = webSite; } [WriteProperty(length: 20, order: 1, paddingChar: ' ')] public string Name { get; set; } [WriteProperty(length: 30, order: 2, paddingChar: ' ')] - public string Adress { get; set; } + public string WebSite { get; set; } public bool Equals(NothingLeftNoObjectMapper other) { return Name == other.Name && - Adress == other.Adress; + WebSite == other.WebSite; } public override int GetHashCode() { - return HashCode.Combine(Name, Adress); + return HashCode.Combine(Name, WebSite); } public override bool Equals(object obj) @@ -33,4 +33,4 @@ public override bool Equals(object obj) return obj is NothingLeftNoObjectMapper nothingLeftNoObjectMapper && Equals(nothingLeftNoObjectMapper); } } -} +} \ No newline at end of file diff --git a/app/Spinner.Test/Models/NothingNoAttibute.cs b/app/Spinner.Test/Models/NothingNoAttibute.cs index b10735fb..4f493de3 100644 --- a/app/Spinner.Test/Models/NothingNoAttibute.cs +++ b/app/Spinner.Test/Models/NothingNoAttibute.cs @@ -6,30 +6,30 @@ namespace Spinner.Test.Models [ObjectMapper(length: 50)] internal struct NothingNoAttibute : IEquatable { - public NothingNoAttibute(string name, string adress) + public NothingNoAttibute(string name, string webSite) { Name = name; - Adress = adress; + WebSite = webSite; } public string Name { get; set; } - public string Adress { get; set; } + public string WebSite { get; set; } public bool Equals(NothingNoAttibute other) { return Name == other.Name && - Adress == other.Adress; + WebSite == other.WebSite; } public override int GetHashCode() { - return HashCode.Combine(Name, Adress); + return HashCode.Combine(Name, WebSite); } public override bool Equals(object obj) { - return obj is NothingNoAttibute nothingLeft && Equals(nothingLeft); + return obj is NothingNoAttibute nothingNoAttibute && Equals(nothingNoAttibute); } } -} +} \ No newline at end of file diff --git a/app/Spinner.Test/Models/NothingLeft.cs b/app/Spinner.Test/Models/NothingPadLeft.cs similarity index 55% rename from app/Spinner.Test/Models/NothingLeft.cs rename to app/Spinner.Test/Models/NothingPadLeft.cs index ac09e8c8..fd75c662 100644 --- a/app/Spinner.Test/Models/NothingLeft.cs +++ b/app/Spinner.Test/Models/NothingPadLeft.cs @@ -4,34 +4,34 @@ namespace Spinner.Test.Models { [ObjectMapper(length: 50)] - internal struct NothingLeft : IEquatable + internal struct NothingPadLeft : IEquatable { - public NothingLeft(string name, string adress) + public NothingPadLeft(string name, string webSite) { Name = name; - Adress = adress; + WebSite = webSite; } [WriteProperty(length: 30, order: 2, paddingChar: ' ')] - public string Adress { get; set; } + public string WebSite { get; set; } [WriteProperty(length: 20, order: 1, paddingChar: ' ')] public string Name { get; set; } - public bool Equals(NothingLeft other) + public bool Equals(NothingPadLeft other) { return Name == other.Name && - Adress == other.Adress; + WebSite == other.WebSite; } public override int GetHashCode() { - return HashCode.Combine(Name, Adress); + return HashCode.Combine(Name, WebSite); } public override bool Equals(object obj) { - return obj is NothingLeft nothingLeft && Equals(nothingLeft); + return obj is NothingPadLeft nothingLeft && Equals(nothingLeft); } } -} +} \ No newline at end of file diff --git a/app/Spinner.Test/Models/NothingRight.cs b/app/Spinner.Test/Models/NothingPadRight.cs similarity index 57% rename from app/Spinner.Test/Models/NothingRight.cs rename to app/Spinner.Test/Models/NothingPadRight.cs index 38476acd..f0d0d0af 100644 --- a/app/Spinner.Test/Models/NothingRight.cs +++ b/app/Spinner.Test/Models/NothingPadRight.cs @@ -5,34 +5,34 @@ namespace Spinner.Test.Models { [ObjectMapper(length: 50)] - internal struct NothingRight : IEquatable + internal struct NothingPadRight : IEquatable { - public NothingRight(string name, string adress) + public NothingPadRight(string name, string webSite) { Name = name; - Adress = adress; + WebSite = webSite; } [WriteProperty(length: 20, order: 1, paddingChar: ' ', PaddingType.Right)] public string Name { get; } [WriteProperty(length: 30, order: 2, paddingChar: ' ', PaddingType.Right)] - public string Adress { get; } + public string WebSite { get; } - public bool Equals(NothingRight other) + public bool Equals(NothingPadRight other) { return Name == other.Name && - Adress == other.Adress; + WebSite == other.WebSite; } public override int GetHashCode() { - return HashCode.Combine(Name, Adress); + return HashCode.Combine(Name, WebSite); } public override bool Equals(object obj) { - return obj is NothingRight nothingRight && Equals(nothingRight); + return obj is NothingPadRight nothingPadRight && Equals(nothingPadRight); } } -} +} \ No newline at end of file diff --git a/app/Spinner.Test/Models/NothingReader.cs b/app/Spinner.Test/Models/NothingReader.cs index 3fd8de57..cf75cc83 100644 --- a/app/Spinner.Test/Models/NothingReader.cs +++ b/app/Spinner.Test/Models/NothingReader.cs @@ -4,23 +4,23 @@ namespace Spinner.Test.Models { [ObjectMapper(length: 50)] - internal class NothingReader : IEquatable + internal sealed class NothingReader : IEquatable { [ReadProperty(start: 1, length: 19)] public string Name { get; set; } [ReadProperty(start: 20, length: 30)] - public string Adress { get; set; } + public string WebSite { get; set; } public bool Equals(NothingReader other) { return Name == other.Name && - Adress == other.Adress; + WebSite == other.WebSite; } public override int GetHashCode() { - return HashCode.Combine(Name, Adress); + return HashCode.Combine(Name, WebSite); } public override bool Equals(object obj) @@ -28,4 +28,4 @@ public override bool Equals(object obj) return obj is NothingReader nothingReader && Equals(nothingReader); } } -} +} \ No newline at end of file diff --git a/app/Spinner.Test/Spinner.Test.csproj b/app/Spinner.Test/Spinner.Test.csproj index 05d7c275..a6f38607 100644 --- a/app/Spinner.Test/Spinner.Test.csproj +++ b/app/Spinner.Test/Spinner.Test.csproj @@ -7,10 +7,10 @@ - - + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/app/Spinner.Test/SpinnerReadTest.cs b/app/Spinner.Test/SpinnerReadTest.cs index acd717e3..b8fdd9e9 100644 --- a/app/Spinner.Test/SpinnerReadTest.cs +++ b/app/Spinner.Test/SpinnerReadTest.cs @@ -1,10 +1,12 @@ -using Xunit; -using System.Linq; +using Spinner.Cache; +using Spinner.Exceptions; +using Spinner.Test.Helper.Parses; using Spinner.Test.Models; using System; -using Spinner.Exceptions; using System.Collections.Generic; +using System.Linq; using System.Reflection; +using Xunit; namespace Spinner.Test { @@ -14,53 +16,87 @@ public class SpinnerReadTest public void ReadFromString_WhenCalled_ShoudReturnObjectMappedFromString() { // Arrange - NothingLeft nothingLeft = new NothingLeft("spinner", "www.spinner.com.br"); - Spinner spinnerWriter = new Spinner(nothingLeft); + NothingPadLeft nothing = new NothingPadLeft("spinner", "www.spinner.com.br"); + Spinner spinnerWriter = new Spinner(nothing); Spinner spinnerReader = new Spinner(); // Act - string stringResponse = spinnerWriter.WriteAsString(); - NothingReader nothingReader = spinnerReader.ReadFromString(stringResponse); + string positionalString = spinnerWriter.WriteAsString(); + NothingReader nothingReader = spinnerReader.ReadFromString(positionalString); + + // Assert + Assert.True(nothing.GetHashCode() == nothingReader.GetHashCode()); + } + + [Fact] + public void ReadFromString_WhenCalled_ShoudParsePropertyToDecimal() + { + // Arrange + NothingDecimal nothing = new NothingDecimal("0001"); + Spinner spinnerWriter = new Spinner(nothing); + Spinner spinnerReader = new Spinner(); + + // Act + string positionalString = spinnerWriter.WriteAsString(); + NothingDecimalReader nothingReader = spinnerReader.ReadFromString(positionalString); + + // Assert + Assert.Equal(00.01M, nothingReader.Value); + } + + [Fact] + public void ReadFromString_WhenCalled_ShoudValidateIfPropertyParseIsChached() + { + // Arrange + NothingDecimal nothing = new NothingDecimal("0001"); + Spinner spinnerWriter = new Spinner(nothing); + Spinner spinnerReader = new Spinner(); + + string positionalString = spinnerWriter.WriteAsString(); + NothingDecimalReader nothingDecimalReader = spinnerReader.ReadFromString(positionalString); + + // Act + bool decimalParserWasCached = ParserTypeCache.Parses.Any(c => c.GetType() == typeof(DecimalParser)); // Assert - Assert.True(nothingLeft.GetHashCode() == nothingReader.GetHashCode()); + Assert.True(decimalParserWasCached); } [Fact] public void ReadFromSpan_WhenCalled_ShoudReturnObjectMappedAsSpan() { // Arrange - NothingLeft nothingLeft = new NothingLeft("spinner", "www.spinner.com.br"); - Spinner spinnerWriter = new Spinner(nothingLeft); + NothingPadLeft nothing = new NothingPadLeft("spinner", "www.spinner.com.br"); + Spinner spinnerWriter = new Spinner(nothing); Spinner spinnerReader = new Spinner(); // Act - System.ReadOnlySpan stringResponse = spinnerWriter.WriteAsSpan(); - NothingReader nothingReader = spinnerReader.ReadFromSpan(stringResponse); + ReadOnlySpan positionalString = spinnerWriter.WriteAsSpan(); + NothingReader nothingReader = spinnerReader.ReadFromSpan(positionalString); // Assert - Assert.True(nothingLeft.GetHashCode() == nothingReader.GetHashCode()); + Assert.True(nothing.GetHashCode() == nothingReader.GetHashCode()); } [Fact] public void ReadFromString_WhenCalled_ShouldValidateIfTwoResponsesAreDiferents() { // Arrange - NothingLeft nothingLeftFirst = new NothingLeft("spinnerFirst", "www.spinner.com.br"); - NothingLeft nothingLeftSecond = new NothingLeft("spinnerSecond", "www.spinner.com.br"); + NothingPadLeft nothingFirst = new NothingPadLeft("spinnerFirst", "www.spinner.com.br"); + NothingPadLeft nothingSecond = new NothingPadLeft("spinnerSecond", "www.spinner.com.br"); - Spinner spinnerWriterFirst = new Spinner(nothingLeftFirst); - Spinner spinnerWriterSecond = new Spinner(nothingLeftSecond); + Spinner spinnerWriterFirst = new Spinner(nothingFirst); + Spinner spinnerWriterSecond = new Spinner(nothingSecond); Spinner spinnerReaderFirst = new Spinner(); Spinner spinnerReaderSecond = new Spinner(); // Act - string stringResponseFirst = spinnerWriterFirst.WriteAsString(); - string stringResponseSecond = spinnerWriterSecond.WriteAsString(); + string positionalStringFirst = spinnerWriterFirst.WriteAsString(); + string positionalStringSecond = spinnerWriterSecond.WriteAsString(); - NothingReader nothingReaderFirst = spinnerReaderFirst.ReadFromString(stringResponseFirst); - NothingReader nothingReaderSecond = spinnerReaderSecond.ReadFromString(stringResponseSecond); + NothingReader nothingReaderFirst = spinnerReaderFirst.ReadFromString(positionalStringFirst); + NothingReader nothingReaderSecond = spinnerReaderSecond.ReadFromString(positionalStringSecond); // Assert Assert.True(nothingReaderFirst.GetHashCode() != nothingReaderSecond.GetHashCode()); @@ -70,36 +106,36 @@ public void ReadFromString_WhenCalled_ShouldValidateIfTwoResponsesAreDiferents() public void ReadFromSpan_WhenCalled_ShouldValidateIfTwoResponsesAreDiferents() { // Arrange - NothingLeft nothingLeftFirst = new NothingLeft("spinnerFirst", "www.spinner.com.br"); - NothingLeft nothingLeftSecond = new NothingLeft("spinnerSecond", "www.spinner.com.br"); + NothingPadLeft nothingFirst = new NothingPadLeft("spinnerFirst", "www.spinner.com.br"); + NothingPadLeft nothingSecond = new NothingPadLeft("spinnerSecond", "www.spinner.com.br"); - Spinner spinnerWriterFirst = new Spinner(nothingLeftFirst); - Spinner spinnerWriterSecond = new Spinner(nothingLeftSecond); + Spinner spinnerWriterFirst = new Spinner(nothingFirst); + Spinner spinnerWriterSecond = new Spinner(nothingSecond); Spinner spinnerReaderFirst = new Spinner(); Spinner spinnerReaderSecond = new Spinner(); // Act - System.ReadOnlySpan stringResponseFirst = spinnerWriterFirst.WriteAsSpan(); - System.ReadOnlySpan stringResponseSecond = spinnerWriterSecond.WriteAsSpan(); + ReadOnlySpan positionalStringFirst = spinnerWriterFirst.WriteAsSpan(); + ReadOnlySpan positionalStringSecond = spinnerWriterSecond.WriteAsSpan(); - NothingReader nothingReaderFirst = spinnerReaderFirst.ReadFromSpan(stringResponseFirst); - NothingReader nothingReaderSecond = spinnerReaderSecond.ReadFromSpan(stringResponseSecond); + NothingReader nothingReaderFirst = spinnerReaderFirst.ReadFromSpan(positionalStringFirst); + NothingReader nothingReaderSecond = spinnerReaderSecond.ReadFromSpan(positionalStringSecond); // Assert Assert.True(nothingReaderFirst.GetHashCode() != nothingReaderSecond.GetHashCode()); } [Fact] - public void GetWriteProperties_WhenCaller_ShouldValidadeHowManyPropertiesWasMapped() + public void GetReadProperties_WhenCaller_ShouldValidadeHowManyPropertiesWasMapped() { - Spinner spinnerFirst = new Spinner(); + Spinner spinner = new Spinner(); - IEnumerable props = spinnerFirst.GetReadProperties; + IEnumerable props = spinner.GetReadProperties; Assert.Equal(2, props.Count()); Assert.Equal("Name", props.First().Name); - Assert.Equal("Adress", props.Last().Name); + Assert.Equal("WebSite", props.Last().Name); } [Fact] @@ -136,4 +172,4 @@ public void ReadFromSpan_WhenCalled_ShouldThrowExceptionIfNotExistsAnyProperties Assert.Equal("Property Name should have ReadProperty configured.", ex.Message); } } -} +} \ No newline at end of file diff --git a/app/Spinner.Test/SpinnerWriteTest.cs b/app/Spinner.Test/SpinnerWriteTest.cs index e87451b2..75b364f4 100644 --- a/app/Spinner.Test/SpinnerWriteTest.cs +++ b/app/Spinner.Test/SpinnerWriteTest.cs @@ -1,10 +1,11 @@ -using System; -using Xunit; -using System.Linq; -using Spinner.Test.Models; +using Spinner.Attribute; using Spinner.Exceptions; +using Spinner.Test.Models; +using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; +using Xunit; namespace Spinner.Test { @@ -14,49 +15,49 @@ public class SpinnerWriteTest public void WriteAsString_WhenCalled_ShoudReturnObjectMappedAsStringWithPadLeft() { // Arrange - NothingLeft nothing = new NothingLeft("spinner", "www.spinner.com.br"); - Spinner spinner = new Spinner(nothing); + NothingPadLeft nothing = new NothingPadLeft("spinner", "www.spinner.com.br"); + Spinner spinner = new Spinner(nothing); const string expected = " spinner www.spinner.com.br"; // Act - string stringResponse = spinner.WriteAsString(); + string positionalString = spinner.WriteAsString(); // Assert - Assert.Equal(50, stringResponse.Length); - Assert.Equal(expected, stringResponse); + Assert.Equal(50, positionalString.Length); + Assert.Equal(expected, positionalString); } [Fact] public void WriteAsString_WhenCalled_ShoudReturnObjectMappedAsStringInOrderOfConfiguratedProperty() { // Arrange - NothingLeft nothing = new NothingLeft("spinner", "www.spinner.com.br"); - Spinner spinner = new Spinner(nothing); + NothingPadLeft nothing = new NothingPadLeft("spinner", "www.spinner.com.br"); + Spinner spinner = new Spinner(nothing); const string expected = " www.spinner.com.br spinner"; // Act - string stringResponse = spinner.WriteAsString(); + string positionalString = spinner.WriteAsString(); // Assert - Assert.Equal(50, stringResponse.Length); - Assert.NotEqual(expected, stringResponse); + Assert.Equal(50, positionalString.Length); + Assert.NotEqual(expected, positionalString); } [Fact] public void WriteAsString_WhenCalled_ShouldValidateIfConfigurationLengthIsEqualToLengthStringThatWasMappedWithPadLeft() { // Arrange - NothingLeft nothing = new NothingLeft(" spinner", " www.spinner.com.br"); - Spinner spinner = new Spinner(nothing); + NothingPadLeft nothing = new NothingPadLeft(" spinner", " www.spinner.com.br"); + Spinner spinner = new Spinner(nothing); const string expected = " spinner www.spinner.com.b"; // Act - Attribute.ObjectMapperAttribute conf = spinner.GetObjectMapper; - string stringResponse = spinner.WriteAsString(); + ObjectMapperAttribute conf = spinner.GetObjectMapper; + string positionalString = spinner.WriteAsString(); // Assert - Assert.Equal(conf.Length, stringResponse.Length); - Assert.Equal(expected, stringResponse); + Assert.Equal(conf.Length, positionalString.Length); + Assert.Equal(expected, positionalString); } [Fact] @@ -68,45 +69,45 @@ public void WriteAsString_WhenCalled_ShouldValidateIfNoObjectMapperIsUsedWithPad const string expected = " spinner www.spinner.com.br"; // Act - Attribute.ObjectMapperAttribute conf = spinner.GetObjectMapper; - string stringResponse = spinner.WriteAsString(); + ObjectMapperAttribute conf = spinner.GetObjectMapper; + string positionalString = spinner.WriteAsString(); // Assert Assert.Null(conf); - Assert.Equal(expected, stringResponse); + Assert.Equal(expected, positionalString); } [Fact] public void WriteAsSpan_WhenCalled_ShoudReturnObjectMappedAsSpanWithPadLeft() { // Arrange - NothingLeft nothing = new NothingLeft("spinner", "www.spinner.com.br"); - Spinner spinner = new Spinner(nothing); + NothingPadLeft nothing = new NothingPadLeft("spinner", "www.spinner.com.br"); + Spinner spinner = new Spinner(nothing); ReadOnlySpan expected = new ReadOnlySpan(" spinner www.spinner.com.br".ToCharArray()); // Act - ReadOnlySpan stringResponseAsSpan = spinner.WriteAsSpan(); + ReadOnlySpan positionalString = spinner.WriteAsSpan(); // Assert - Assert.Equal(50, stringResponseAsSpan.Length); - Assert.Equal(expected.ToString(), stringResponseAsSpan.ToString()); + Assert.Equal(50, positionalString.Length); + Assert.Equal(expected.ToString(), positionalString.ToString()); } [Fact] public void WriteAsSpan_WhenCalled_ShouldValidateIfConfigurationLengthIsEqualToLengthSpanThatWasMappedWithPadLeft() { // Arrange - NothingLeft nothing = new NothingLeft("spinner", "www.spinner.com.br"); - Spinner spinner = new Spinner(nothing); + NothingPadLeft nothing = new NothingPadLeft("spinner", "www.spinner.com.br"); + Spinner spinner = new Spinner(nothing); ReadOnlySpan expected = new ReadOnlySpan(" spinner www.spinner.com.br".ToCharArray()); // Act - Attribute.ObjectMapperAttribute conf = spinner.GetObjectMapper; - ReadOnlySpan stringResponseAsSpan = spinner.WriteAsSpan(); + ObjectMapperAttribute objectMapper = spinner.GetObjectMapper; + ReadOnlySpan positionalString = spinner.WriteAsSpan(); // Assert - Assert.Equal(conf.Length, stringResponseAsSpan.Length); - Assert.Equal(expected.ToString(), stringResponseAsSpan.ToString()); + Assert.Equal(objectMapper.Length, positionalString.Length); + Assert.Equal(expected.ToString(), positionalString.ToString()); } [Fact] @@ -118,167 +119,167 @@ public void WriteAsSpan_WhenCalled_ShouldValidateIfNoObjectMapperIsUsedWithPadLe ReadOnlySpan expected = new ReadOnlySpan(" spinner www.spinner.com.br".ToCharArray()); // Act - Attribute.ObjectMapperAttribute conf = spinner.GetObjectMapper; - ReadOnlySpan stringResponseAsSpan = spinner.WriteAsSpan(); + ObjectMapperAttribute objectMapper = spinner.GetObjectMapper; + ReadOnlySpan positionalString = spinner.WriteAsSpan(); // Assert - Assert.Null(conf); - Assert.Equal(expected.ToString(), stringResponseAsSpan.ToString()); + Assert.Null(objectMapper); + Assert.Equal(expected.ToString(), positionalString.ToString()); } [Fact] public void WriteAsString_WhenCalled_ShouldValidateIfTwoResponseIsDiferentWithPadLeft() { // Arrange - NothingLeft nothingFirst = new NothingLeft("spinnerFirst", "www.spinner.com.br"); - NothingLeft nothingSecond = new NothingLeft("spinnerSecond", "www.spinner.com.br"); + NothingPadLeft nothingFirst = new NothingPadLeft("spinnerFirst", "www.spinner.com.br"); + NothingPadLeft nothingSecond = new NothingPadLeft("spinnerSecond", "www.spinner.com.br"); - Spinner spinnerFirst = new Spinner(nothingFirst); - Spinner spinnerSecond = new Spinner(nothingSecond); + Spinner spinnerFirst = new Spinner(nothingFirst); + Spinner spinnerSecond = new Spinner(nothingSecond); // Act - string stringResponseFirst = spinnerFirst.WriteAsString(); - string stringResponseSecond = spinnerSecond.WriteAsString(); + string positionalStringFirst = spinnerFirst.WriteAsString(); + string positionalStringSecond = spinnerSecond.WriteAsString(); // Assert - Assert.True(stringResponseFirst != stringResponseSecond); + Assert.True(positionalStringFirst != positionalStringSecond); } [Fact] public void WriteAsSpan_WhenCalled_ShouldValidateIfTwoResponseIsDiferentWithPadLeft() { // Arrange - NothingLeft nothingFirst = new NothingLeft("spinnerFirst", "www.spinner.com.br"); - NothingLeft nothingSecond = new NothingLeft("spinnerSecond", "www.spinner.com.br"); + NothingPadLeft nothingFirst = new NothingPadLeft("spinnerFirst", "www.spinner.com.br"); + NothingPadLeft nothingSecond = new NothingPadLeft("spinnerSecond", "www.spinner.com.br"); - Spinner spinnerFirst = new Spinner(nothingFirst); - Spinner spinnerSecond = new Spinner(nothingSecond); + Spinner spinnerFirst = new Spinner(nothingFirst); + Spinner spinnerSecond = new Spinner(nothingSecond); // Act - ReadOnlySpan stringResponseFirst = spinnerFirst.WriteAsSpan(); - ReadOnlySpan stringResponseSecond = spinnerSecond.WriteAsSpan(); + ReadOnlySpan positionalStringFirst = spinnerFirst.WriteAsSpan(); + ReadOnlySpan positionalStringSecond = spinnerSecond.WriteAsSpan(); // Assert - Assert.True(stringResponseFirst != stringResponseSecond); + Assert.True(positionalStringFirst != positionalStringSecond); } [Fact] public void WriteAsString_WhenCalled_ShoudReturnObjectMappedAsStringWithPadRight() { // Arrange - NothingRight nothing = new NothingRight("spinner", "www.spinner.com.br"); - Spinner spinner = new Spinner(nothing); + NothingPadRight nothing = new NothingPadRight("spinner", "www.spinner.com.br"); + Spinner spinner = new Spinner(nothing); const string expected = "spinner www.spinner.com.br "; // Act - string stringResponse = spinner.WriteAsString(); + string positionalString = spinner.WriteAsString(); // Assert - Assert.Equal(50, stringResponse.Length); - Assert.Equal(expected, stringResponse); + Assert.Equal(50, positionalString.Length); + Assert.Equal(expected, positionalString); } [Fact] public void WriteAsString_WhenCalled_ShouldValidateIfConfigurationLengthIsEqualToLengthStringThatWasMappedWithPadRight() { // Arrange - NothingRight nothing = new NothingRight("spinner", "www.spinner.com.br"); - Spinner spinner = new Spinner(nothing); + NothingPadRight nothing = new NothingPadRight("spinner", "www.spinner.com.br"); + Spinner spinner = new Spinner(nothing); const string expected = "spinner www.spinner.com.br "; // Act - Attribute.ObjectMapperAttribute conf = spinner.GetObjectMapper; - string stringResponse = spinner.WriteAsString(); + ObjectMapperAttribute objectMapper = spinner.GetObjectMapper; + string positionalString = spinner.WriteAsString(); // Assert - Assert.Equal(conf.Length, stringResponse.Length); - Assert.Equal(expected, stringResponse); + Assert.Equal(objectMapper.Length, positionalString.Length); + Assert.Equal(expected, positionalString); } [Fact] public void WriteAsSpan_WhenCalled_ShoudReturnObjectMappedAsSpanWithPadRight() { // Arrange - NothingRight nothing = new NothingRight("spinner", "www.spinner.com.br"); - Spinner spinner = new Spinner(nothing); + NothingPadRight nothing = new NothingPadRight("spinner", "www.spinner.com.br"); + Spinner spinner = new Spinner(nothing); ReadOnlySpan expected = new ReadOnlySpan("spinner www.spinner.com.br ".ToCharArray()); // Act - ReadOnlySpan stringResponseAsSpan = spinner.WriteAsSpan(); + ReadOnlySpan positionalString = spinner.WriteAsSpan(); // Assert - Assert.Equal(50, stringResponseAsSpan.Length); - Assert.Equal(expected.ToString(), stringResponseAsSpan.ToString()); + Assert.Equal(50, positionalString.Length); + Assert.Equal(expected.ToString(), positionalString.ToString()); } [Fact] public void WriteAsSpan_WhenCalled_ShouldValidateIfConfigurationLengthIsEqualToLengthSpanThatWasMappedWithPadRight() { // Arrange - NothingRight nothing = new NothingRight("spinner", "www.spinner.com.br"); - Spinner spinner = new Spinner(nothing); + NothingPadRight nothing = new NothingPadRight("spinner", "www.spinner.com.br"); + Spinner spinner = new Spinner(nothing); ReadOnlySpan expected = new ReadOnlySpan("spinner www.spinner.com.br ".ToCharArray()); // Act - Attribute.ObjectMapperAttribute conf = spinner.GetObjectMapper; - ReadOnlySpan stringResponseAsSpan = spinner.WriteAsSpan(); + ObjectMapperAttribute objectMapper = spinner.GetObjectMapper; + ReadOnlySpan positionalString = spinner.WriteAsSpan(); // Assert - Assert.Equal(conf.Length, stringResponseAsSpan.Length); - Assert.Equal(expected.ToString(), stringResponseAsSpan.ToString()); + Assert.Equal(objectMapper.Length, positionalString.Length); + Assert.Equal(expected.ToString(), positionalString.ToString()); } [Fact] public void WriteAsString_WhenCalled_ShouldValidateIfTwoResponseIsDiferentWithPadRight() { // Arrange - NothingRight nothingFirst = new NothingRight("spinnerFirst", "www.spinner.com.br"); - NothingRight nothingSecond = new NothingRight("spinnerSecond", "www.spinner.com.br"); + NothingPadRight nothingFirst = new NothingPadRight("spinnerFirst", "www.spinner.com.br"); + NothingPadRight nothingSecond = new NothingPadRight("spinnerSecond", "www.spinner.com.br"); - Spinner spinnerFirst = new Spinner(nothingFirst); - Spinner spinnerSecond = new Spinner(nothingSecond); + Spinner spinnerFirst = new Spinner(nothingFirst); + Spinner spinnerSecond = new Spinner(nothingSecond); // Act - string stringResponseFirst = spinnerFirst.WriteAsString(); - string stringResponseSecond = spinnerSecond.WriteAsString(); + string positionalStringFirst = spinnerFirst.WriteAsString(); + string positionalStringSecond = spinnerSecond.WriteAsString(); // Assert - Assert.True(stringResponseFirst != stringResponseSecond); + Assert.True(positionalStringFirst != positionalStringSecond); } [Fact] public void WriteAsSpan_WhenCalled_ShouldValidateIfTwoResponseIsDiferentWithPadRight() { // Arrange - NothingRight nothingFirst = new NothingRight("spinnerFirst", "www.spinner.com.br"); - NothingRight nothingSecond = new NothingRight("spinnerSecond", "www.spinner.com.br"); + NothingPadRight nothingFirst = new NothingPadRight("spinnerFirst", "www.spinner.com.br"); + NothingPadRight nothingSecond = new NothingPadRight("spinnerSecond", "www.spinner.com.br"); - Spinner spinnerFirst = new Spinner(nothingFirst); - Spinner spinnerSecond = new Spinner(nothingSecond); + Spinner spinnerFirst = new Spinner(nothingFirst); + Spinner spinnerSecond = new Spinner(nothingSecond); // Act - ReadOnlySpan stringResponseFirst = spinnerFirst.WriteAsSpan(); - ReadOnlySpan stringResponseSecond = spinnerSecond.WriteAsSpan(); + ReadOnlySpan positionalStringFirst = spinnerFirst.WriteAsSpan(); + ReadOnlySpan positionalStringSecond = spinnerSecond.WriteAsSpan(); // Assert - Assert.True(stringResponseFirst != stringResponseSecond); + Assert.True(positionalStringFirst != positionalStringSecond); } [Fact] public void GetWriteProperties_WhenCaller_ShouldValidadeHowManyPropertiesWasMapped() { // Arrange - NothingRight nothing = new NothingRight("spinnerFirst", "www.spinner.com.br"); + NothingPadRight nothing = new NothingPadRight("spinnerFirst", "www.spinner.com.br"); - Spinner spinnerFirst = new Spinner(nothing); + Spinner spinnerFirst = new Spinner(nothing); // Act - IEnumerable props = spinnerFirst.GetWriteProperties; + IEnumerable props = spinnerFirst.GetWriteProperties; // Assert Assert.Equal(2, props.Count()); Assert.Equal("Name", props.First().Name); - Assert.Equal("Adress", props.Last().Name); + Assert.Equal("WebSite", props.Last().Name); } [Fact] @@ -319,4 +320,4 @@ public void WriteAsSpan_WhenCalled_ShouldThrowExceptionIfNotExistsAnyPropertiesW Assert.Equal("Property Name should have WriteProperty configured.", ex.Message); } } -} +} \ No newline at end of file diff --git a/app/Spinner.Test/coverage.opencover.xml b/app/Spinner.Test/coverage.opencover.xml new file mode 100644 index 00000000..9f81b56e --- /dev/null +++ b/app/Spinner.Test/coverage.opencover.xml @@ -0,0 +1,697 @@ + + + + + + Spinner.dll + 2023-02-05T06:05:12 + Spinner + + + + + + + + + + + + + Spinner.Spinner`1 + + + + + Spinner.Attribute.ObjectMapperAttribute Spinner.Spinner`1::get_GetObjectMapper() + + + + + + + + + + + System.Collections.Generic.IEnumerable`1<System.Reflection.PropertyInfo> Spinner.Spinner`1::get_GetWriteProperties() + + + + + + + + + + + System.Collections.Generic.IEnumerable`1<System.Reflection.PropertyInfo> Spinner.Spinner`1::get_GetReadProperties() + + + + + + + + + + + System.String Spinner.Spinner`1::WriteAsString() + + + + + + + + + + + + + + + + + + + System.ReadOnlySpan`1<System.Char> Spinner.Spinner`1::WriteAsSpan() + + + + + + + + + + + + + + + + + + + + T Spinner.Spinner`1::ReadFromString(System.String) + + + + + + + + + + + + + + T Spinner.Spinner`1::ReadFromSpan(System.ReadOnlySpan`1<System.Char>) + + + + + + + + + + + + + + System.Void Spinner.Spinner`1::ReadPositionalString(System.ReadOnlySpan`1<System.Char>) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System.Void Spinner.Spinner`1::WritePositionaString() + + + + + + + + + + + + + + + + + + + + + + + + + + System.ReadOnlySpan`1<System.Char> Spinner.Spinner`1::FormatValue(System.ReadOnlySpan`1<System.Char>,Spinner.Attribute.WritePropertyAttribute) + + + + + + + + + + + + + + + + + + Spinner.Attribute.WritePropertyAttribute Spinner.Spinner`1::GetWriteProperty(System.Reflection.PropertyInfo) + + + + + + + + + + + + + + Spinner.Attribute.ReadPropertyAttribute Spinner.Spinner`1::GetReaderProperty(System.Reflection.PropertyInfo) + + + + + + + + + + + + + + System.Func`2<System.Reflection.PropertyInfo,System.Boolean> Spinner.Spinner`1::PredicateForWriteProperty() + + + + + + + + + + + + + + + + + + + + + + System.Func`2<System.Reflection.PropertyInfo,System.UInt16> Spinner.Spinner`1::PrecicateForOrderByWriteProperty() + + + + + + + + + + + + + + + + + + System.Func`2<System.Reflection.PropertyInfo,System.Boolean> Spinner.Spinner`1::PredicateForReadProperty() + + + + + + + + + + + + + + + + + + + + + + System.Void Spinner.Spinner`1::.ctor(T) + + + + + + + + + + + + + + System.Void Spinner.Spinner`1::.ctor() + + + + + + + + + + + + + System.Void Spinner.Spinner`1::.cctor() + + + + + + + + + + + + + + + + + + + + + + + + + + + Spinner.Extensions.SpanExtensions + + + + + System.ReadOnlySpan`1<System.Char> Spinner.Extensions.SpanExtensions::PadLeft(System.ReadOnlySpan`1<System.Char>,System.Int32,System.Char) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System.ReadOnlySpan`1<System.Char> Spinner.Extensions.SpanExtensions::PadRight(System.ReadOnlySpan`1<System.Char>,System.Int32,System.Char) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spinner.Exceptions.PropertyNotMappedException + + + + + System.Void Spinner.Exceptions.PropertyNotMappedException::.ctor(System.String) + + + + + + + + + + + + + + Spinner.Cache.ParserTypeCache + + + + + System.Collections.Generic.IEnumerable`1<Spinner.Parsers.ITypeParse> Spinner.Cache.ParserTypeCache::get_Parses() + + + + + + + + + + + System.Boolean Spinner.Cache.ParserTypeCache::Add(System.String,Spinner.Parsers.ITypeParse) + + + + + + + + + + + + + System.Boolean Spinner.Cache.ParserTypeCache::TryGet(System.String,Spinner.Parsers.ITypeParse&) + + + + + + + + + + + + + + + + + + + + + System.Void Spinner.Cache.ParserTypeCache::.cctor() + + + + + + + + + + + + Spinner.Attribute.ObjectMapperAttribute + + + + + System.UInt16 Spinner.Attribute.ObjectMapperAttribute::get_Length() + + + + + + + + + + + System.Void Spinner.Attribute.ObjectMapperAttribute::.ctor(System.UInt16) + + + + + + + + + + + + + + + Spinner.Attribute.ReadPropertyAttribute + + + + + System.UInt16 Spinner.Attribute.ReadPropertyAttribute::get_Start() + + + + + + + + + + + System.UInt16 Spinner.Attribute.ReadPropertyAttribute::get_Length() + + + + + + + + + + + System.Type Spinner.Attribute.ReadPropertyAttribute::get_Type() + + + + + + + + + + + System.Void Spinner.Attribute.ReadPropertyAttribute::.ctor(System.UInt16,System.UInt16) + + + + + + + + + + + + + + + System.Void Spinner.Attribute.ReadPropertyAttribute::.ctor(System.UInt16,System.UInt16,System.Type) + + + + + + + + + + + + + + + + + Spinner.Attribute.WritePropertyAttribute + + + + + System.UInt16 Spinner.Attribute.WritePropertyAttribute::get_Length() + + + + + + + + + + + System.UInt16 Spinner.Attribute.WritePropertyAttribute::get_Order() + + + + + + + + + + + System.Char Spinner.Attribute.WritePropertyAttribute::get_PaddingChar() + + + + + + + + + + + Spinner.Enums.PaddingType Spinner.Attribute.WritePropertyAttribute::get_Padding() + + + + + + + + + + + System.Void Spinner.Attribute.WritePropertyAttribute::.ctor(System.UInt16,System.UInt16,System.Char) + + + + + + + + + + + + + System.Void Spinner.Attribute.WritePropertyAttribute::.ctor(System.UInt16,System.UInt16,System.Char,Spinner.Enums.PaddingType) + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/Spinner.sln b/app/Spinner.sln index 46e9a88c..77d403e6 100644 --- a/app/Spinner.sln +++ b/app/Spinner.sln @@ -10,13 +10,12 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EEC9F328-3E41-4446-9DE0-41352A5320C1}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - ..\.lgmt.yml = ..\.lgmt.yml global.json = global.json makefile = makefile ..\README.md = ..\README.md EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Writer.Benchmark", "..\bench\Writer.Benchmark\Writer.Benchmark.csproj", "{057C345D-E1D1-4C1D-B4F3-09B67FE47ED2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spinner.Benchmark", "..\bench\Spinner.Benchmark\Spinner.Benchmark.csproj", "{1659BF3A-422C-4754-9325-B58C2B979901}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -26,28 +25,16 @@ Global GlobalSection(ProjectConfigurationPlatforms) = postSolution {B73DFF16-E66B-4731-B94D-C655DE6B03B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B73DFF16-E66B-4731-B94D-C655DE6B03B0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B73DFF16-E66B-4731-B94D-C655DE6B03B0}.Debug|x64.ActiveCfg = Debug|x64 - {B73DFF16-E66B-4731-B94D-C655DE6B03B0}.Debug|x64.Build.0 = Debug|x64 {B73DFF16-E66B-4731-B94D-C655DE6B03B0}.Release|Any CPU.ActiveCfg = Release|Any CPU {B73DFF16-E66B-4731-B94D-C655DE6B03B0}.Release|Any CPU.Build.0 = Release|Any CPU - {B73DFF16-E66B-4731-B94D-C655DE6B03B0}.Release|x64.ActiveCfg = Release|x64 - {B73DFF16-E66B-4731-B94D-C655DE6B03B0}.Release|x64.Build.0 = Release|x64 {D7AA1C12-0909-4B74-A57F-6BBE48ED7355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7AA1C12-0909-4B74-A57F-6BBE48ED7355}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D7AA1C12-0909-4B74-A57F-6BBE48ED7355}.Debug|x64.ActiveCfg = Debug|Any CPU - {D7AA1C12-0909-4B74-A57F-6BBE48ED7355}.Debug|x64.Build.0 = Debug|Any CPU {D7AA1C12-0909-4B74-A57F-6BBE48ED7355}.Release|Any CPU.ActiveCfg = Release|Any CPU {D7AA1C12-0909-4B74-A57F-6BBE48ED7355}.Release|Any CPU.Build.0 = Release|Any CPU - {D7AA1C12-0909-4B74-A57F-6BBE48ED7355}.Release|x64.ActiveCfg = Release|Any CPU - {D7AA1C12-0909-4B74-A57F-6BBE48ED7355}.Release|x64.Build.0 = Release|Any CPU - {057C345D-E1D1-4C1D-B4F3-09B67FE47ED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {057C345D-E1D1-4C1D-B4F3-09B67FE47ED2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {057C345D-E1D1-4C1D-B4F3-09B67FE47ED2}.Debug|x64.ActiveCfg = Debug|Any CPU - {057C345D-E1D1-4C1D-B4F3-09B67FE47ED2}.Debug|x64.Build.0 = Debug|Any CPU - {057C345D-E1D1-4C1D-B4F3-09B67FE47ED2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {057C345D-E1D1-4C1D-B4F3-09B67FE47ED2}.Release|Any CPU.Build.0 = Release|Any CPU - {057C345D-E1D1-4C1D-B4F3-09B67FE47ED2}.Release|x64.ActiveCfg = Release|Any CPU - {057C345D-E1D1-4C1D-B4F3-09B67FE47ED2}.Release|x64.Build.0 = Release|Any CPU + {1659BF3A-422C-4754-9325-B58C2B979901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1659BF3A-422C-4754-9325-B58C2B979901}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1659BF3A-422C-4754-9325-B58C2B979901}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1659BF3A-422C-4754-9325-B58C2B979901}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/app/Spinner/Attributes/ObjectMapperAttribute.cs b/app/Spinner/Attributes/ObjectMapperAttribute.cs index 681e5fbd..f194de2e 100644 --- a/app/Spinner/Attributes/ObjectMapperAttribute.cs +++ b/app/Spinner/Attributes/ObjectMapperAttribute.cs @@ -22,4 +22,4 @@ public ObjectMapperAttribute(ushort length) /// public ushort Length { get; } } -} +} \ No newline at end of file diff --git a/app/Spinner/Attributes/ReadPropertyAttribute.cs b/app/Spinner/Attributes/ReadPropertyAttribute.cs index 945bf1a1..78ed63bb 100644 --- a/app/Spinner/Attributes/ReadPropertyAttribute.cs +++ b/app/Spinner/Attributes/ReadPropertyAttribute.cs @@ -11,7 +11,7 @@ public sealed class ReadPropertyAttribute : System.Attribute /// /// Constructor to configure the initial position and total characters of a field. /// - /// Inicial position. + /// Initial position. /// Total characters. public ReadPropertyAttribute(ushort start, ushort length) { @@ -19,8 +19,15 @@ public ReadPropertyAttribute(ushort start, ushort length) this.Length = length; } + public ReadPropertyAttribute(ushort start, ushort length, Type type) + { + this.Start = start; + this.Length = length; + this.Type = type; + } + /// - /// Inicial position. + /// Initial position. /// public ushort Start { get; } @@ -28,5 +35,10 @@ public ReadPropertyAttribute(ushort start, ushort length) /// Total characters. /// public ushort Length { get; } + + /// + /// Parser property type + /// + public Type Type { get; } } -} +} \ No newline at end of file diff --git a/app/Spinner/Attributes/WritePropertyAttribute.cs b/app/Spinner/Attributes/WritePropertyAttribute.cs index 363c8f0c..af971055 100644 --- a/app/Spinner/Attributes/WritePropertyAttribute.cs +++ b/app/Spinner/Attributes/WritePropertyAttribute.cs @@ -10,7 +10,7 @@ namespace Spinner.Attribute public sealed class WritePropertyAttribute : System.Attribute { /// - /// Constructor to configure the lenght, order and char padding of a field. + /// Constructor to configure the length, order and char padding of a field. /// /// Total characters. /// Field order within string. @@ -21,12 +21,12 @@ public WritePropertyAttribute(ushort length, ushort order, char paddingChar) } /// - /// Constructor to configure the lenght, order and char padding of a field. + /// Constructor to configure the length, order and char padding of a field. /// /// Total characters. /// Field order within string. /// Type of characters to padding. - /// Type of pading Left or Right. + /// Type of padding Left or Right. public WritePropertyAttribute(ushort length, ushort order, char paddingChar, PaddingType padding) { this.Length = length; @@ -51,8 +51,8 @@ public WritePropertyAttribute(ushort length, ushort order, char paddingChar, Pad public char PaddingChar { get; } /// - /// Type of pading Left or Right. + /// Type of padding Left or Right. /// public PaddingType Padding { get; } } -} +} \ No newline at end of file diff --git a/app/Spinner/Cache/ParserTypeCache.cs b/app/Spinner/Cache/ParserTypeCache.cs new file mode 100644 index 00000000..daf89bbd --- /dev/null +++ b/app/Spinner/Cache/ParserTypeCache.cs @@ -0,0 +1,33 @@ +using Spinner.Parsers; +using System.Collections.Generic; +using System.Linq; + +namespace Spinner.Cache +{ + internal static class ParserTypeCache + { + private static readonly Dictionary cache = new Dictionary(); + + public static IEnumerable Parses + { + get => cache.Values.ToList(); + } + + public static bool Add(string key, ITypeParse value) + { + return cache.TryAdd(key, value); + } + + public static bool TryGet(string key, out ITypeParse output) + { + if (cache.TryGetValue(key, out ITypeParse value)) + { + output = value; + return true; + } + + output = default; + return false; + } + } +} \ No newline at end of file diff --git a/app/Spinner/Enums/PaddingType.cs b/app/Spinner/Enums/PaddingType.cs index 92197625..c681f8e9 100644 --- a/app/Spinner/Enums/PaddingType.cs +++ b/app/Spinner/Enums/PaddingType.cs @@ -15,4 +15,4 @@ public enum PaddingType /// Right = 2 } -} +} \ No newline at end of file diff --git a/app/Spinner/Exceptions/PropertyNotMappedException.cs b/app/Spinner/Exceptions/PropertyNotMappedException.cs index 8fabe715..338dc5e7 100644 --- a/app/Spinner/Exceptions/PropertyNotMappedException.cs +++ b/app/Spinner/Exceptions/PropertyNotMappedException.cs @@ -15,4 +15,4 @@ public PropertyNotMappedException(string message) : base(message) { } } -} +} \ No newline at end of file diff --git a/app/Spinner/Extensions/SpanExtensions.cs b/app/Spinner/Extensions/SpanExtensions.cs index 413e9a22..df143ce4 100644 --- a/app/Spinner/Extensions/SpanExtensions.cs +++ b/app/Spinner/Extensions/SpanExtensions.cs @@ -65,4 +65,4 @@ internal static unsafe ReadOnlySpan PadRight(this ReadOnlySpan @this return new ReadOnlySpan(tmp.ToArray()); } } -} +} \ No newline at end of file diff --git a/app/Spinner/Guards/Guard.cs b/app/Spinner/Guards/Guard.cs new file mode 100644 index 00000000..cda56a03 --- /dev/null +++ b/app/Spinner/Guards/Guard.cs @@ -0,0 +1,30 @@ +using Spinner.Exceptions; +using System.Reflection; + +namespace Spinner.Guards +{ + internal static class Guard + { + public static class ReadProperty + { + public static void NotMapped(PropertyInfo property, Attribute.ReadPropertyAttribute attribute) + { + if (attribute is null) + { + throw new PropertyNotMappedException($"Property {property.Name} should have ReadProperty configured."); + } + } + } + + public static class WriteProperty + { + public static void NotMapped(PropertyInfo property, Attribute.WritePropertyAttribute attribute) + { + if (attribute is null) + { + throw new PropertyNotMappedException($"Property {property.Name} should have WriteProperty configured."); + } + } + } + } +} diff --git a/app/Spinner/Parsers/ITypeParse.cs b/app/Spinner/Parsers/ITypeParse.cs new file mode 100644 index 00000000..04f373b4 --- /dev/null +++ b/app/Spinner/Parsers/ITypeParse.cs @@ -0,0 +1,7 @@ +namespace Spinner.Parsers +{ + public interface ITypeParse + { + object Parser(object obj); + } +} \ No newline at end of file diff --git a/app/Spinner/Spinner.cs b/app/Spinner/Spinner.cs index ec25417a..2e984b2e 100644 --- a/app/Spinner/Spinner.cs +++ b/app/Spinner/Spinner.cs @@ -1,23 +1,25 @@ using Microsoft.VisualStudio.Utilities; +using Spinner.Attribute; +using Spinner.Cache; +using Spinner.Enums; +using Spinner.Extensions; +using Spinner.Guards; +using Spinner.Parsers; using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; namespace Spinner { - using Spinner.Enums; - using Spinner.Attribute; - using Spinner.Extensions; - using System.Linq; - using Spinner.Exceptions; - /// /// Spinner object that abstract all rule to read or write an string. /// /// The type of object to write or read. public ref struct Spinner where T : new() { - private readonly T obj; + private readonly PooledStringBuilder _sb = PooledStringBuilder.GetInstance(); + private readonly T _obj; /// /// T is the object that can be mapped using the attributes WriteProperty, ReadProperty and ContextProperty. @@ -25,7 +27,7 @@ namespace Spinner /// Object of T that will be used to map. public Spinner(T obj) { - this.obj = obj; + _obj = obj; } /// @@ -33,7 +35,7 @@ public Spinner(T obj) /// public Spinner() { - this.obj = new T(); + _obj = new T(); } /// @@ -57,27 +59,11 @@ public Spinner() /// Return a string mapped of T. public string WriteAsString() { - PooledStringBuilder sb = PooledStringBuilder.GetInstance(); - - for (int i = 0; i < WriteProperties.Length; i++) - { - WritePropertyAttribute attribute = GetWriteProperty(WriteProperties[i]); - - if (attribute == null) - { - throw new PropertyNotMappedException($"Property {WriteProperties[i].Name} should have WriteProperty configured."); - } + WritePositionalString(); - sb.Builder.Append( - FormatValue( - (WriteProperties[i].GetValue(this.obj) as string).AsSpan(), - attribute - )); - } - - return GetObjectMapper != null ? - sb.ToStringAndFree(0, GetObjectMapper.Length) : - sb.ToStringAndFree(); + return GetObjectMapper is not null ? + _sb.ToStringAndFree(0, GetObjectMapper.Length) : + _sb.ToStringAndFree(); } /// @@ -86,79 +72,75 @@ public string WriteAsString() /// Return an string mapped of T as span. public ReadOnlySpan WriteAsSpan() { - PooledStringBuilder sb = PooledStringBuilder.GetInstance(); - - for (int i = 0; i < WriteProperties.Length; i++) - { - WritePropertyAttribute attribute = GetWriteProperty(WriteProperties[i]); - - if (attribute == null) - { - throw new PropertyNotMappedException($"Property {WriteProperties[i].Name} should have WriteProperty configured."); - } - - sb.Builder.Append( - FormatValue( - (WriteProperties[i].GetValue(this.obj) as string).AsSpan(), - attribute - )); - } + WritePositionalString(); return new ReadOnlySpan( - GetObjectMapper != null ? - sb.ToStringAndFree(0, GetObjectMapper.Length).ToCharArray() : - sb.ToStringAndFree().ToCharArray() - ); + GetObjectMapper is not null ? + _sb.ToStringAndFree(0, GetObjectMapper.Length).ToCharArray() : + _sb.ToStringAndFree().ToCharArray()); } /// /// Convert string in an object. /// /// Positional string to map in an object. - /// + /// returns instance of T. public T ReadFromString(string value) { - ReadOnlySpan valuesToSlice = new ReadOnlySpan(value.ToCharArray()); - - for (int i = 0; i < ReadProperties.Length; i++) - { - ReadPropertyAttribute attribute = GetReaderProperty(ReadProperties[i]); + ReadPositionalString(value); - if (attribute == null) - { - throw new PropertyNotMappedException($"Property {ReadProperties[i].Name} should have ReadProperty configured."); - } - - ReadProperties[i].SetValue( - this.obj, - new string(valuesToSlice.Slice(attribute.Start, attribute.Length).Trim())); - } - - return this.obj; + return _obj; } /// /// Convert string in an object. /// /// Span with data to map an object. - /// + /// returns instance of T. public T ReadFromSpan(ReadOnlySpan value) + { + ReadPositionalString(value); + + return _obj; + } + + private void ReadPositionalString(ReadOnlySpan text) { for (int i = 0; i < ReadProperties.Length; i++) { - ReadPropertyAttribute attribute = GetReaderProperty(ReadProperties[i]); + ref PropertyInfo property = ref ReadProperties[i]; + ReadPropertyAttribute attribute = GetReaderProperty(property); + + Guard.ReadProperty.NotMapped(property, attribute); - if (attribute == null) + if (attribute.Type is not null) { - throw new PropertyNotMappedException($"Property {ReadProperties[i].Name} should have ReadProperty configured."); + if (!ParserTypeCache.TryGet(attribute.Type.Name, out ITypeParse typeParser)) + { + typeParser = (ITypeParse)Activator.CreateInstance(attribute.Type); + ParserTypeCache.Add(attribute.Type.Name, typeParser); + } + + property.SetValue(_obj, typeParser.Parser(new string(text.Slice(attribute.Start, attribute.Length).Trim()))); + + continue; } - ReadProperties[i].SetValue( - this.obj, - new string(value.Slice(attribute.Start, attribute.Length).Trim())); + property.SetValue(_obj, new string(text.Slice(attribute.Start, attribute.Length).Trim())); } + } - return this.obj; + private void WritePositionalString() + { + for (int i = 0; i < WriteProperties.Length; i++) + { + ref PropertyInfo property = ref WriteProperties[i]; + WritePropertyAttribute attribute = GetWriteProperty(property); + + Guard.WriteProperty.NotMapped(property, attribute); + + _sb.Builder.Append(FormatValue((property.GetValue(_obj) as string).AsSpan(), attribute)); + } } private static ReadOnlySpan FormatValue(ReadOnlySpan value, WritePropertyAttribute property) @@ -190,7 +172,7 @@ private static ReadPropertyAttribute GetReaderProperty(PropertyInfo info) => typeof(T) .GetProperties() .Where(PredicateForWriteProperty()) - .OrderBy(PrecicateForOrderByWriteProperty()) + .OrderBy(PredicateForOrderByWriteProperty()) .ToArray(); private static readonly PropertyInfo[] ReadProperties = @@ -204,15 +186,15 @@ private static Func PredicateForWriteProperty() return (prop) => { return prop.GetCustomAttributes(typeof(WritePropertyAttribute), false) - .All(a => a.GetType() == typeof(WritePropertyAttribute)); + .All(attribute => attribute.GetType() == typeof(WritePropertyAttribute)); }; } - private static Func PrecicateForOrderByWriteProperty() + private static Func PredicateForOrderByWriteProperty() { - return (prop) => ((WritePropertyAttribute)prop.GetCustomAttributes(false) - .Where(x => x.GetType() == typeof(WritePropertyAttribute)) - .FirstOrDefault())?.Order ?? default; + return (prop) => ((WritePropertyAttribute)prop + .GetCustomAttributes(typeof(WritePropertyAttribute), false) + .FirstOrDefault())?.Order ?? default; } private static Func PredicateForReadProperty() @@ -220,8 +202,8 @@ private static Func PredicateForReadProperty() return (prop) => { return prop.GetCustomAttributes(typeof(ReadPropertyAttribute), false) - .All(a => a.GetType() == typeof(ReadPropertyAttribute)); + .All(attribute => attribute.GetType() == typeof(ReadPropertyAttribute)); }; } } -} +} \ No newline at end of file diff --git a/app/Spinner/Spinner.csproj b/app/Spinner/Spinner.csproj index 31c902e4..3a049acb 100644 --- a/app/Spinner/Spinner.csproj +++ b/app/Spinner/Spinner.csproj @@ -5,7 +5,7 @@ 11.0 Daniel Oliveira MIT - Integration; Positional String; Comunication; String Reader; String Writer + integration;string-mapper;string-protocol;convert-string-object https://spinnerframework.com/ logo.png Spinner Framework diff --git a/app/makefile b/app/makefile index dd66b30b..e82549f6 100644 --- a/app/makefile +++ b/app/makefile @@ -2,13 +2,25 @@ restore: dotnet restore build: - dotnet build -c Release + dotnet build -c Release test: - dotnet test --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover + dotnet test --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover test-nocoverage: - dotnet test --no-build --verbosity normal /p:CollectCoverage=false + dotnet test --no-build --verbosity normal /p:CollectCoverage=false test-mutant: - cd Spinner.Test && dotnet stryker --reporter "html" \ No newline at end of file + cd Spinner.Test && dotnet stryker --reporter "html" + +format: + dotnet format '.\' -v diag --severity error --verbosity --no-restore + +dependense-diagram: + dependensee -S "src" -O "src/diagram.html" + +analyze-package-usage: + snitch + +run-ci: + act "on: pull_request" -C ../ \ No newline at end of file diff --git a/bench/Writer.Benchmark/WriterBench.cs b/bench/Spinner.Benchmark/MethodToBenchmark.cs similarity index 76% rename from bench/Writer.Benchmark/WriterBench.cs rename to bench/Spinner.Benchmark/MethodToBenchmark.cs index 559b63ea..4b8b567f 100644 --- a/bench/Writer.Benchmark/WriterBench.cs +++ b/bench/Spinner.Benchmark/MethodToBenchmark.cs @@ -14,11 +14,13 @@ namespace Writer.Benchmark public class WriterBench { private ObjectBench instance; + private ObjectBenchWithParser instanceWithParser; [GlobalSetup] public void Setup() { instance = new ObjectBench("Bench Test", "www.bench.com"); + instanceWithParser = new ObjectBenchWithParser("Bench Test", "www.bench.com"); } [Benchmark] @@ -35,6 +37,13 @@ public void WriteAsSpan() spinner.WriteAsSpan(); } + [Benchmark] + public void ReadFromStringWithParser() + { + Spinner spinner = new Spinner(instanceWithParser); + spinner.ReadFromString(" "); + } + [Benchmark] public void ReadFromString() { diff --git a/bench/Writer.Benchmark/ObjectBench.cs b/bench/Spinner.Benchmark/ObjectBench.cs similarity index 100% rename from bench/Writer.Benchmark/ObjectBench.cs rename to bench/Spinner.Benchmark/ObjectBench.cs diff --git a/bench/Spinner.Benchmark/ObjectBenchWithParser.cs b/bench/Spinner.Benchmark/ObjectBenchWithParser.cs new file mode 100644 index 00000000..db28e242 --- /dev/null +++ b/bench/Spinner.Benchmark/ObjectBenchWithParser.cs @@ -0,0 +1,53 @@ +using Spinner.Attribute; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Writer.Benchmark +{ + [ObjectMapper(length: 50)] + internal struct ObjectBenchWithParser : IEquatable, IEqualityComparer + { + public ObjectBenchWithParser(string name, string adress) + { + Name = name; + Adress = adress; + } + + [WriteProperty(length: 20, order: 1, paddingChar: ' ')] + [ReadProperty(start: 0, length: 19)] + public string Name { get; set; } + + [WriteProperty(length: 30, order: 2, paddingChar: ' ')] + [ReadProperty(start: 19, length: 30, type: typeof(ParserWebSite))] + public string Adress { get; set; } + + public bool Equals(ObjectBenchWithParser other) + { + return Name == other.Name && + Adress == other.Adress; + } + + public override bool Equals(object obj) + { + ObjectBenchWithParser other = (ObjectBenchWithParser)obj; + + return other.Equals(this); + } + + public bool Equals(ObjectBenchWithParser x, ObjectBenchWithParser y) + { + return x.Equals(y); + } + + public override int GetHashCode() + { + return HashCode.Combine(Name, Adress); + } + + public int GetHashCode([DisallowNull] ObjectBenchWithParser obj) + { + return HashCode.Combine(obj.Name, obj.Adress); + } + } +} diff --git a/bench/Spinner.Benchmark/ParserAdress.cs b/bench/Spinner.Benchmark/ParserAdress.cs new file mode 100644 index 00000000..9043417f --- /dev/null +++ b/bench/Spinner.Benchmark/ParserAdress.cs @@ -0,0 +1,12 @@ +using Spinner.Parsers; + +namespace Writer.Benchmark +{ + internal class ParserWebSite : ITypeParse + { + public object Parser(object propertyValue) + { + return $"WebSite: {propertyValue.ToString()}"; + } + } +} diff --git a/bench/Writer.Benchmark/Program.cs b/bench/Spinner.Benchmark/Program.cs similarity index 100% rename from bench/Writer.Benchmark/Program.cs rename to bench/Spinner.Benchmark/Program.cs diff --git a/bench/Writer.Benchmark/Writer.Benchmark.csproj b/bench/Spinner.Benchmark/Spinner.Benchmark.csproj similarity index 100% rename from bench/Writer.Benchmark/Writer.Benchmark.csproj rename to bench/Spinner.Benchmark/Spinner.Benchmark.csproj diff --git a/docs/docs/intro.md b/docs/docs/intro.md index 19ac64f8..e12a2816 100644 --- a/docs/docs/intro.md +++ b/docs/docs/intro.md @@ -41,12 +41,12 @@ Run **WriteAsString** to get mapped object as string or call **WriteAsSpan** to var nothing = new Nothing("spinner", "www.spinner.com.br"); var spinner = new Spinner(nothing); var stringResponse = spinner.WriteAsString(); - //stringresponse = " spinner www.spinner.com.br" + //output: " spinner www.spinner.com.br" ``` ```csharp var nothing = new Nothing("spinner", "www.spinner.com.br"); var spinner = new Spinner(nothing); var spanResponse = spinner.WriteAsSpan(); - //spanResponse = " spinner www.spinner.com.br" + //output: " spinner www.spinner.com.br" ``` diff --git a/docs/docs/mapping-object-in-string.md b/docs/docs/mapping-object-in-string.md index 2ab8fb34..c11b703e 100644 --- a/docs/docs/mapping-object-in-string.md +++ b/docs/docs/mapping-object-in-string.md @@ -12,17 +12,17 @@ To configure an object, you should use ` ObjectMapper ` and ` WriteProperty ` pr [ObjectMapper(length: 50)] public struct Nothing { - public Nothing(string name, string adress) + public Nothing(string name, string webSite) { this.Name = name; - this.Adress = adress; + this.WebSite = webSite; } [WriteProperty(length: 20, order: 1, paddingChar: ' ')] public string Name { get; private set; } [WriteProperty(length: 30, order: 2, paddingChar: ' ')] - public string Adress { get; private set; } + public string WebSite { get; private set; } } ``` @@ -32,7 +32,7 @@ The sum `length` of all mapped property should not be more than ObjectMapper `le ::: -## Instanciate +## Instantiate To map you object as string, you need instantiate ``` Spinner ``` passing the object type in T and an instance of the object in the constructor. diff --git a/docs/docs/mapping-string-in-object.md b/docs/docs/mapping-string-in-object.md index b75a2f7f..a833c06d 100644 --- a/docs/docs/mapping-string-in-object.md +++ b/docs/docs/mapping-string-in-object.md @@ -16,11 +16,11 @@ To configure an object, you should use ` ObjectMapper ` and ` ReadProperty ` pro public string Name { get; private set; } [ReadProperty(start: 20, length: 30)] - public string Adress { get; private set; } + public string WebSite { get; private set; } } ``` -## Instanciate +## Instantiate To map you object as string, you need instantiate ``` Spinner ``` passing the object type in T. diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index d65b6ec2..4f311846 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -23,8 +23,12 @@ const config = { }, theme: { customCss: require.resolve('./src/css/custom.css'), - } - }), + }, + gtag: { + trackingID: 'G-QK1XCXPTY5', + anonymizeIP: true, + } + }), ], ], @@ -95,11 +99,7 @@ const config = { prism: { theme: lightCodeTheme, darkTheme: darkCodeTheme, - }, - gtag: { - trackingID: 'G-QK1XCXPTY5', - anonymizeIP: true, - } + } }), }; diff --git a/docs/package.json b/docs/package.json index 851ff1bc..800d44ed 100644 --- a/docs/package.json +++ b/docs/package.json @@ -14,9 +14,9 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@docusaurus/core": "2.0.0-beta.9", - "@docusaurus/plugin-google-gtag": "^2.0.0-beta.9", - "@docusaurus/preset-classic": "2.0.0-beta.9", + "@docusaurus/core": "^2.2.0", + "@docusaurus/plugin-google-gtag": "^2.2.0", + "@docusaurus/preset-classic": "^2.2.0", "@mdx-js/react": "^1.6.21", "@svgr/webpack": "^5.5.0", "clsx": "^1.1.1",