├── .editorconfig
├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── .idea
└── .idea.Typely
│ └── .idea
│ ├── .gitignore
│ ├── encodings.xml
│ ├── indexLayout.xml
│ └── vcs.xml
├── Directory.Build.props
├── HowToPublishNugets.md
├── LICENSE
├── README.md
├── Typely.sln
├── assets
└── logo-300.png
├── benchmarks
└── Typely.Benchmarks
│ ├── ClassVsStructBenchmark.cs
│ ├── EqualsBenchmark.cs
│ ├── ErrorBenchmark.cs
│ ├── Program.cs
│ ├── README.md
│ ├── RandomString.cs
│ ├── ReadonlyStructBenchmark.cs
│ ├── Typely.Benchmarks.csproj
│ └── ValueObjectLibrariesBenchmark.cs
├── sample
├── README.md
├── Sample.Api
│ ├── ContactsApi.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Sample.Api.csproj
│ ├── TestsController.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
├── Sample.Domain
│ ├── .idea
│ │ └── .idea.Sample.dir
│ │ │ └── .idea
│ │ │ └── encodings.xml
│ ├── ContactAggregate
│ │ ├── Address.cs
│ │ ├── Contact.cs
│ │ ├── PhoneType.cs
│ │ └── TypelySpecification.cs
│ └── Sample.Domain.csproj
└── Sample.Infrastructure
│ ├── AppDbContext.cs
│ ├── ContactConfiguration.cs
│ ├── Migrations
│ ├── 20230423224612_InitialCreate.Designer.cs
│ ├── 20230423224612_InitialCreate.cs
│ ├── 20230428170636_MaxLength.Designer.cs
│ ├── 20230428170636_MaxLength.cs
│ ├── 20230430140013_TableAddress.Designer.cs
│ ├── 20230430140013_TableAddress.cs
│ ├── 20240924122024_InitialData.Designer.cs
│ ├── 20240924122024_InitialData.cs
│ └── AppDbContextModelSnapshot.cs
│ └── Sample.Infrastructure.csproj
├── src
├── .vscode
│ ├── launch.json
│ └── tasks.json
├── Typely.AspNetCore.Swashbuckle
│ ├── SwaggerGenOptionsExtensions.cs
│ ├── Typely.AspNetCore.Swashbuckle.csproj
│ ├── TypelyValueSchemaFilter.cs
│ └── nuget-readme.md
├── Typely.AspNetCore
│ ├── Http
│ │ ├── ApplicationBuilderExtensions.cs
│ │ ├── HttpValidationTemplatedProblemDetails.cs
│ │ ├── TemplatedProblemDetail.cs
│ │ └── TypelyValidationResultMiddleware.cs
│ ├── Mvc
│ │ ├── ModelBinding
│ │ │ ├── TypelyValueModelBinder.cs
│ │ │ └── TypelyValueModelBinderProvider.cs
│ │ └── MvcOptionsExtensions.cs
│ ├── Typely.AspNetCore.csproj
│ └── nuget-readme.md
├── Typely.Core
│ ├── Builders
│ │ ├── IComparableRuleBuilder.cs
│ │ ├── ITypelyBuilder.cs
│ │ ├── ITypelyBuilderOfBool.cs
│ │ ├── ITypelyBuilderOfByte.cs
│ │ ├── ITypelyBuilderOfChar.cs
│ │ ├── ITypelyBuilderOfDateOnly.cs
│ │ ├── ITypelyBuilderOfDateTime.cs
│ │ ├── ITypelyBuilderOfDateTimeOffset.cs
│ │ ├── ITypelyBuilderOfDecimal.cs
│ │ ├── ITypelyBuilderOfDouble.cs
│ │ ├── ITypelyBuilderOfFloat.cs
│ │ ├── ITypelyBuilderOfGuid.cs
│ │ ├── ITypelyBuilderOfInt.cs
│ │ ├── ITypelyBuilderOfLong.cs
│ │ ├── ITypelyBuilderOfSByte.cs
│ │ ├── ITypelyBuilderOfShort.cs
│ │ ├── ITypelyBuilderOfString.cs
│ │ ├── ITypelyBuilderOfTimeOnly.cs
│ │ ├── ITypelyBuilderOfTimeSpan.cs
│ │ ├── ITypelyBuilderOfUInt.cs
│ │ ├── ITypelyBuilderOfULong.cs
│ │ └── ITypelyBuilderOfUShort.cs
│ ├── Converters
│ │ ├── TypelyJsonConverter.cs
│ │ └── TypelyTypeConverter.cs
│ ├── ErrorMessages.Designer.cs
│ ├── ErrorMessages.resx
│ ├── Extensions
│ │ └── TypeExtensions.cs
│ ├── IMaxLength.cs
│ ├── ITypelySpecification.cs
│ ├── ITypelyValue.cs
│ ├── Typely.Core.csproj
│ ├── TypelyOptions.cs
│ ├── TypelyValue.cs
│ ├── ValidationError.cs
│ ├── ValidationErrorFactory.cs
│ ├── ValidationException.cs
│ ├── ValidationPlaceholders.cs
│ └── nuget-readme.md
├── Typely.EfCore
│ ├── Conventions
│ │ ├── ConventionModelBuilderExtensions.cs
│ │ ├── ConventionSetBuilderExtensions.cs
│ │ ├── TypelyConversionConvention.cs
│ │ └── TypelyMaxLengthConvention.cs
│ ├── Typely.EfCore.csproj
│ ├── TypelyValueConverter.cs
│ └── nuget-readme.md
└── Typely.Generators
│ ├── Analysers
│ └── TypelySpecificationAnalyser.cs
│ ├── Comparers
│ ├── DictionaryComparer.cs
│ └── ImmutableArrayComparer.cs
│ ├── Extensions
│ └── ClassDeclarationSyntaxExtensions.cs
│ ├── Infrastructure
│ └── SymbolExtensions.cs
│ ├── Typely.Generators.csproj
│ ├── Typely
│ ├── DiagnosticDescriptors.cs
│ ├── Emitting
│ │ ├── Emitter.Class.cs
│ │ ├── Emitter.Struct.cs
│ │ └── Emitter.cs
│ ├── ErrorCodes.cs
│ ├── ErrorMessageResource.cs
│ ├── Parsing
│ │ ├── ConstructTypeKind.cs
│ │ ├── EmittableRule.cs
│ │ ├── EmittableRuleBuilder.cs
│ │ ├── EmittableType.cs
│ │ ├── EmittableTypeBuilder.cs
│ │ ├── EmittableTypeOrDiagnostic.cs
│ │ ├── NamespaceResolver.cs
│ │ ├── ParsedInvocation.cs
│ │ ├── ParsedInvocationExtensions.cs
│ │ ├── ParsedStatement.cs
│ │ ├── Parser.cs
│ │ ├── TypeBuilders
│ │ │ ├── EmittableTypeBuilderBase.cs
│ │ │ ├── EmittableTypeBuilderFactory.cs
│ │ │ ├── EmittableTypeBuilderOfBool.cs
│ │ │ ├── EmittableTypeBuilderOfByte.cs
│ │ │ ├── EmittableTypeBuilderOfChar.cs
│ │ │ ├── EmittableTypeBuilderOfDateOnly.cs
│ │ │ ├── EmittableTypeBuilderOfDateTime.cs
│ │ │ ├── EmittableTypeBuilderOfDateTimeOffset.cs
│ │ │ ├── EmittableTypeBuilderOfDecimal.cs
│ │ │ ├── EmittableTypeBuilderOfDouble.cs
│ │ │ ├── EmittableTypeBuilderOfFloat.cs
│ │ │ ├── EmittableTypeBuilderOfGuid.cs
│ │ │ ├── EmittableTypeBuilderOfInt.cs
│ │ │ ├── EmittableTypeBuilderOfLong.cs
│ │ │ ├── EmittableTypeBuilderOfSByte.cs
│ │ │ ├── EmittableTypeBuilderOfShort.cs
│ │ │ ├── EmittableTypeBuilderOfString.cs
│ │ │ ├── EmittableTypeBuilderOfTimeOnly.cs
│ │ │ ├── EmittableTypeBuilderOfTimeSpan.cs
│ │ │ ├── EmittableTypeBuilderOfULong.cs
│ │ │ ├── EmittableTypeBuilderOfUShort.cs
│ │ │ ├── EmittableTypeBuilderOfUint.cs
│ │ │ └── IEmittableTypeBuilder.cs
│ │ └── TypeProperties.cs
│ ├── SupportedMembers.cs
│ ├── SymbolNames.cs
│ ├── TypelyBuilder.cs
│ ├── TypelyBuilderOf.cs
│ ├── TypelyGenerator.cs
│ └── ValidationPlaceholder.cs
│ └── nuget-readme.md
└── tests
├── Typely.AspNetCore.Swashbuckle.Tests
├── BaseFixture.cs
├── SchemaFilterContextFixture.cs
├── SwaggerGenOptionsExtensionsTests.cs
├── Typely.AspNetCore.Swashbuckle.Tests.csproj
├── TypelySpecification.cs
├── TypelyValueSchemaFilterTests.cs
└── Usings.cs
├── Typely.AspNetCore.Tests
├── Http
│ ├── TestServerFixture.cs
│ ├── TypelyValidationResultMiddlewareTests.Middleware_ShouldReturn_ErrorResponse.verified.txt
│ └── TypelyValidationResultMiddlewareTests.cs
├── Mvc
│ ├── ModelBinding
│ │ ├── ModelBinderFixture.cs
│ │ ├── ModelBinderProviderContextFixture.cs
│ │ ├── ModelBindingContextFixture.cs
│ │ ├── TestModelBinderProviderContext.cs
│ │ ├── TypelySpecification.cs
│ │ ├── TypelyValueModelBinderProviderTests.cs
│ │ └── TypelyValueModelBinderTests.cs
│ └── MvcOptionsTests.cs
├── Typely.AspNetCore.Tests.csproj
└── Usings.cs
├── Typely.EfCore.Tests
├── Common
│ ├── MyDbContext.cs
│ ├── Person.cs
│ └── TypelySpecificationOfPerson.cs
├── Conventions
│ └── ConventionTests.cs
├── Typely.EfCore.Tests.csproj
├── TypelyValueConverterTests.cs
└── Usings.cs
├── Typely.Generators.Tests
├── Analysers
│ ├── TypelySpecificationAnalyserFixture.cs
│ └── TypelySpecificationAnalyserTests.cs
├── BaseFixture.cs
├── Comparers
│ ├── DictionaryComparerTests.cs
│ └── ImmutableArrayComparerTests.cs
├── CompilationFixture.cs
├── CompilationWithAnalysersFixture.cs
├── Extensions
│ └── ClassDeclarationSyntaxExtensionsTests.cs
├── ModuleInitializer.cs
├── TestExtensions.cs
├── Typely.Generators.Tests.csproj
├── Typely
│ ├── Parsing
│ │ ├── EmittableRuleTests.cs
│ │ ├── EmittableTypeBuilderFactoryTests.cs
│ │ ├── EmittableTypeTests.cs
│ │ ├── ParenthesizedDeclarationSpecification.cs
│ │ ├── ParsedInvocationExtensionsTests.cs
│ │ ├── ParserContext.cs
│ │ ├── ParserContextFixture.cs
│ │ ├── ParserTests.cs
│ │ └── TypePropertiesTests.cs
│ ├── Snapshots
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfBool_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfByte_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfChar_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfDateOnly_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfDateTimeOffset_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfDateTime_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfDecimal_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfDouble_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfFloat_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfGuid_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfInt_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfLong_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfSByte_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfShort_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfString_Should_EmitTypes#Typely.Generators.Tests.Typely.Specifications.Code.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfString_Should_EmitTypes#UserAggregate.UserId.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfTimeOnly_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfTimeSpan_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfUInt_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfULong_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.AllSupportedScenariosOfUShort_Should_EmitTypes#Election.Votes.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.MissingName_Should_OutputDiagnostic.verified.txt
│ │ ├── TypelyGeneratorSnapshotTests.MissingTypeName_Should_Continue.verified.txt
│ │ ├── TypelyGeneratorSnapshotTests.MultipleConfigurations_Should_EmitTypes#Typely.Generators.Tests.Typely.Specifications.A.MultiConfigDiffNamespace.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.MultipleConfigurations_Should_EmitTypes#Typely.Generators.Tests.Typely.Specifications.B.MultiConfigDiffNamespace.g.verified.cs
│ │ ├── TypelyGeneratorSnapshotTests.UnsupportedSpecification_Should_OutputDiagnostics#Typely.Generators.Tests.Typely.Specifications.ShouldGenerate.g.verified.cs
│ │ └── TypelyGeneratorSnapshotTests.UnsupportedSpecification_Should_OutputDiagnostics.verified.txt
│ ├── Specifications
│ │ ├── A
│ │ │ ├── CustomLocalization.Designer.cs
│ │ │ ├── CustomLocalization.resx
│ │ │ └── MultipleSpecificationA.cs
│ │ ├── B
│ │ │ └── MultipleSpecificationB.cs
│ │ ├── BoolSpecification.cs
│ │ ├── ByteSpecification.cs
│ │ ├── CharSpecification.cs
│ │ ├── DateOnlySpecification.cs
│ │ ├── DateTimeOffsetSpecification.cs
│ │ ├── DateTimeSpecification.cs
│ │ ├── DecimalSpecification.cs
│ │ ├── Diagnostics
│ │ │ ├── UnsupportedFieldsSpecification.cs
│ │ │ ├── UnsupportedMethodsSpecification.cs
│ │ │ ├── UnsupportedPropertiesSpecification.cs
│ │ │ ├── UnsupportedTypesSpecification.cs
│ │ │ └── UnsupportedVariablesSpecification.cs
│ │ ├── DiagnosticsSpecification.cs
│ │ ├── DoubleSpecification.cs
│ │ ├── EmptySpecification.cs
│ │ ├── FloatSpecification.cs
│ │ ├── GuidSpecification.cs
│ │ ├── IntSpecification.cs
│ │ ├── LocalizedMessages.Designer.cs
│ │ ├── LocalizedMessages.resx
│ │ ├── LocalizedNames.Designer.cs
│ │ ├── LocalizedNames.resx
│ │ ├── LongSpecification.cs
│ │ ├── NamespaceSpecification.cs
│ │ ├── NoNamespaceSpecification.cs
│ │ ├── SByteSpecification.cs
│ │ ├── ShortSpecification.cs
│ │ ├── StringSpecification.cs
│ │ ├── TimeOnlySpecification.cs
│ │ ├── TimeSpanSpecification.cs
│ │ ├── UIntSpecification.cs
│ │ ├── ULongSpecification.cs
│ │ ├── UShortSpecification.cs
│ │ └── WrappedNamespaceSpecification.cs
│ ├── TypelyGeneratorDriver.cs
│ ├── TypelyGeneratorDriverFixture.cs
│ └── TypelyGeneratorSnapshotTests.cs
└── Usings.cs
├── Typely.Isolated.Tests
├── Typely.Isolated.Tests.csproj
├── Usings.cs
└── ValidationErrorFactory.Tests.cs
└── Typely.Tests
├── Asserts.cs
├── CompleteTypelySpecification.cs
├── Converters
├── TypelyJsonConverterTests.cs
├── TypelySpecification.cs
└── TypelyTypeConverterTests.cs
├── TypeGeneration
├── BoolType
│ ├── BoolSpecification.cs
│ └── BoolTests.cs
├── ByteType
│ ├── ByteSpecification.cs
│ └── ByteTests.cs
├── CharType
│ ├── CharConfiguration.cs
│ └── CharTests.cs
├── DateOnlyType
│ ├── DateOnlyConfiguration.cs
│ └── DateOnlyTests.cs
├── DateTimeOffsetType
│ ├── DateTimeOffsetConfiguration.cs
│ └── DateTimeOffsetTests.cs
├── DateTimeType
│ ├── DateTimeConfiguration.cs
│ └── DateTimeTests.cs
├── DecimalType
│ ├── DecimalConfiguration.cs
│ └── DecimalTests.cs
├── DoubleType
│ ├── DoubleConfiguration.cs
│ └── DoubleTests.cs
├── FloatType
│ ├── FloatConfiguration.cs
│ └── FloatTests.cs
├── GuidType
│ ├── GuidConfiguration.cs
│ └── GuidTests.cs
├── IntType
│ ├── IntSpecification.cs
│ └── IntTests.cs
├── LongType
│ ├── LongConfiguration.cs
│ └── LongTests.cs
├── SByteType
│ ├── SByteConfiguration.cs
│ └── SByteTests.cs
├── ShortType
│ ├── ShortConfiguration.cs
│ └── ShortTests.cs
├── StringType
│ ├── StringSpecification.cs
│ └── StringTests.cs
├── TimeOnlyType
│ ├── TimeOnlyConfiguration.cs
│ └── TimeOnlyTests.cs
├── TimeSpanType
│ ├── TimeSpanConfiguration.cs
│ └── TimeSpanTests.cs
├── UIntType
│ ├── UIntConfiguration.cs
│ └── UIntTests.cs
├── ULongType
│ ├── ULongConfiguration.cs
│ └── ULongTests.cs
└── UShortType
│ ├── UShortConfiguration.cs
│ └── UShortTests.cs
├── Typely.Tests.csproj
├── TypelyOptionsTests.cs
├── Usings.cs
├── ValidationErrorTests.Validate_ShouldReturn_ValidationError.verified.txt
└── ValidationErrorTests.cs
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: pipeline
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 | strategy:
10 | matrix:
11 | dotnet-version: ['8.0']
12 |
13 | steps:
14 | - uses: actions/checkout@v3
15 |
16 | - name: Setup .NET Core SDK ${{ matrix.dotnet-version }}
17 | uses: actions/setup-dotnet@v3
18 | with:
19 | dotnet-version: ${{ matrix.dotnet-version }}
20 |
21 | - name: Restore dependencies
22 | run: dotnet restore ./Typely.sln
23 |
24 | - name: Build
25 | run: dotnet build --configuration Release ./Typely.sln
26 |
27 | - name: Test
28 | run: dotnet test --no-restore ./Typely.sln /p:CollectCoverage=true /p:CoverletOutput="../Coverage/" /p:MergeWith="../Coverage/coverage.json" /p:CoverletOutputFormat=\"cobertura,json\" /maxcpucount:1
29 |
30 | - name: Upload coverage reports to Codecov
31 | uses: codecov/codecov-action@v3
32 |
33 | - name: Create nuget packages
34 | run: |
35 | dotnet pack --configuration Release ./src/Typely.Core/Typely.Core.csproj
36 | dotnet pack --configuration Release ./src/Typely.Generators/Typely.Generators.csproj
37 | dotnet pack --configuration Release ./src/Typely.EfCore/Typely.EfCore.csproj
38 | dotnet pack --configuration Release ./src/Typely.AspNetCore/Typely.AspNetCore.csproj
39 | dotnet pack --configuration Release ./src/Typely.AspNetCore.Swashbuckle/Typely.AspNetCore.Swashbuckle.csproj
40 |
41 | - name: Publish packages
42 | run: dotnet nuget push ./artifacts/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
43 |
--------------------------------------------------------------------------------
/.idea/.idea.Typely/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Rider ignored files
5 | /contentModel.xml
6 | /modules.xml
7 | /projectSettingsUpdater.xml
8 | /.idea.Typely.iml
9 | # Editor-based HTTP Client requests
10 | /httpRequests/
11 | # Datasource local storage ignored files
12 | /dataSources/
13 | /dataSources.local.xml
14 |
--------------------------------------------------------------------------------
/.idea/.idea.Typely/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/.idea.Typely/.idea/indexLayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | .
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/.idea.Typely/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/HowToPublishNugets.md:
--------------------------------------------------------------------------------
1 | # How to publish packages
2 |
3 | 1. Increment `Typely.Core`
4 | 1. Commit
5 | 1. Wait for nuget to index the package
6 | 1. Increment `Typely.AspNetCore`, `Typely.AspNetCore.Swashbuckle` and `Typely.EfCore`
7 | 1. Update references to `Typely.Core` in csprojs.
8 | 1. Commit
9 | 1. Merge if all successful
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Adam Paquette
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/assets/logo-300.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/typely-io/Typely/1ca6b01b5f8499eaa6faf2f196b5231cb2251473/assets/logo-300.png
--------------------------------------------------------------------------------
/benchmarks/Typely.Benchmarks/ClassVsStructBenchmark.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 |
3 | namespace Typely.Benchmarks;
4 |
5 | [MemoryDiagnoser]
6 | public class ClassVsStructBenchmark
7 | {
8 | private string _text = string.Empty;
9 |
10 | [Params(10, 100)]
11 | public int N;
12 |
13 | [GlobalSetup]
14 | public void Setup()
15 | {
16 | var randomString = new RandomString();
17 | _text = randomString.Next(N);
18 | }
19 |
20 | [Benchmark]
21 | public void Struct() => new NameStruct(_text);
22 |
23 | [Benchmark]
24 | public void Class() => new NameClass(_text);
25 | }
26 |
27 | public class NameClass
28 | {
29 | public string Value { get; }
30 |
31 | public NameClass(string value)
32 | {
33 | Value = value;
34 | }
35 | }
36 |
37 | public struct NameStruct
38 | {
39 | public string Value { get; }
40 |
41 | public NameStruct(string value)
42 | {
43 | Value = value;
44 | }
45 | }
--------------------------------------------------------------------------------
/benchmarks/Typely.Benchmarks/ErrorBenchmark.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 |
3 | namespace Typely.Benchmarks;
4 |
5 | [MemoryDiagnoser]
6 | public class ErrorBenchmark
7 | {
8 | readonly string _val = new Random().Next().ToString();
9 |
10 | [Benchmark]
11 | public void ReturnException()
12 | {
13 | try
14 | {
15 | throw new ArgumentOutOfRangeException("Param", _val, "Parameter must not be empty.");
16 | }
17 | catch (Exception)
18 | {
19 | }
20 | }
21 |
22 | [Benchmark]
23 | public void ReturnValidationError() => new ValidationErrorTest("ERR001", "Parameter must not be empty.", _val, "Param");
24 | }
25 |
26 | public class ValidationErrorTest
27 | {
28 | public string ErrorCode { get; init; }
29 | public string ErrorMessage { get; init; }
30 | public object AttemptedValue { get; init; }
31 | public object Source { get; init; }
32 | public ValidationErrorTest(string errorCode, string errorMessage, object attemptedValue, object source)
33 | {
34 | ErrorCode = errorCode;
35 | ErrorMessage = errorMessage;
36 | AttemptedValue = attemptedValue;
37 | Source = source;
38 | }
39 | }
--------------------------------------------------------------------------------
/benchmarks/Typely.Benchmarks/Program.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Running;
2 | using Typely.Benchmarks;
3 |
4 | BenchmarkRunner.Run();
5 | //BenchmarkRunner.Run();
6 | //BenchmarkRunner.Run();
7 | //BenchmarkRunner.Run();
8 | //BenchmarkRunner.Run();
9 | //BenchmarkRunner.Run();
10 | //BenchmarkRunner.Run(typeof(Program).Assembly);
--------------------------------------------------------------------------------
/benchmarks/Typely.Benchmarks/README.md:
--------------------------------------------------------------------------------
1 | # ClassVsStructBenchmark
2 |
3 | | Method | N | Mean | Error | StdDev | Median | Gen0 | Allocated |
4 | |------- |---- |----------:|----------:|----------:|----------:|-------:|----------:|
5 | | Struct | 10 | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns | - | - |
6 | | Class | 10 | 2.0950 ns | 0.0106 ns | 0.0088 ns | 2.0922 ns | 0.0015 | 24 B |
7 | | Struct | 100 | 0.0011 ns | 0.0011 ns | 0.0010 ns | 0.0008 ns | - | - |
8 | | Class | 100 | 2.1348 ns | 0.0534 ns | 0.0473 ns | 2.1205 ns | 0.0015 | 24 B |
9 |
--------------------------------------------------------------------------------
/benchmarks/Typely.Benchmarks/RandomString.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Benchmarks;
2 |
3 | public class RandomString
4 | {
5 | private readonly Random _random = new();
6 |
7 | public string Next(int length)
8 | {
9 | const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
10 | var stringChars = new char[length];
11 |
12 | for (int i = 0; i < stringChars.Length; i++)
13 | {
14 | stringChars[i] = chars[_random.Next(chars.Length)];
15 | }
16 |
17 | return new String(stringChars);
18 | }
19 | }
--------------------------------------------------------------------------------
/benchmarks/Typely.Benchmarks/ReadonlyStructBenchmark.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 |
3 | namespace Typely.Benchmarks;
4 |
5 | [MemoryDiagnoser]
6 | public class ReadonlyStructBenchmark
7 | {
8 | private struct NumberStruct(int value)
9 | {
10 | public int Value { get; } = value;
11 |
12 | public override string ToString() => Value.ToString();
13 | }
14 |
15 | private readonly struct NumberReadonlyStruct(int value)
16 | {
17 | public int Value { get; } = value;
18 |
19 | public override string ToString() => Value.ToString();
20 | }
21 |
22 | private readonly NumberStruct _numberStruct = new(1);
23 | private readonly NumberReadonlyStruct _numberReadonlyStruct = new(1);
24 |
25 | [Benchmark]
26 | public string Struct()
27 | {
28 | // Making a defensive copy of the value
29 | return _numberStruct.ToString();
30 | }
31 |
32 | [Benchmark]
33 | public string ReadOnlyStruct()
34 | {
35 | return _numberReadonlyStruct.ToString();
36 | }
37 | }
--------------------------------------------------------------------------------
/benchmarks/Typely.Benchmarks/Typely.Benchmarks.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | Exe
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/sample/README.md:
--------------------------------------------------------------------------------
1 | # Migrations
2 |
3 | cd sample
4 | dotnet ef migrations add InitialCreate --project Sample.Infrastructure --startup-project Sample.Api
--------------------------------------------------------------------------------
/sample/Sample.Api/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:36490",
8 | "sslPort": 44371
9 | }
10 | },
11 | "profiles": {
12 | "http": {
13 | "commandName": "Project",
14 | "dotnetRunMessages": true,
15 | "launchBrowser": true,
16 | "launchUrl": "swagger",
17 | "applicationUrl": "http://localhost:5230",
18 | "environmentVariables": {
19 | "ASPNETCORE_ENVIRONMENT": "Development"
20 | }
21 | },
22 | "https": {
23 | "commandName": "Project",
24 | "dotnetRunMessages": true,
25 | "launchBrowser": true,
26 | "launchUrl": "swagger",
27 | "applicationUrl": "https://localhost:7087;http://localhost:5230",
28 | "environmentVariables": {
29 | "ASPNETCORE_ENVIRONMENT": "Development"
30 | }
31 | },
32 | "IIS Express": {
33 | "commandName": "IISExpress",
34 | "launchBrowser": true,
35 | "launchUrl": "swagger",
36 | "environmentVariables": {
37 | "ASPNETCORE_ENVIRONMENT": "Development"
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/sample/Sample.Api/Sample.Api.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 | all
13 | runtime; build; native; contentfiles; analyzers; buildtransitive
14 |
15 |
16 |
17 |
18 |
19 |
20 | appsettings.json
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/sample/Sample.Api/TestsController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Sample.Domain.ContactAggregate;
3 | using Typely.Core;
4 |
5 | namespace Sample.Api;
6 |
7 | [Route("tests/[controller]")]
8 | [ApiController]
9 | public class TestsController : ControllerBase
10 | {
11 | [HttpGet("string")]
12 | public string Foo(FirstName firstName) => $"Value: {firstName}";
13 |
14 | [HttpGet("int")]
15 | public string Foo(ContactId contactId) => $"Value: {contactId}";
16 |
17 | [HttpPost("validation-problem")]
18 | public IActionResult ValidationProblem(Contact contact)
19 | {
20 | var error = new ValidationError("A", "AAA", null, "type", new Dictionary());
21 |
22 | ModelState.AddModelError("FirstName", "Validation error message");
23 | return ValidationProblem();
24 | }
25 | }
--------------------------------------------------------------------------------
/sample/Sample.Api/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Information"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/sample/Sample.Api/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*"
9 | }
10 |
--------------------------------------------------------------------------------
/sample/Sample.Domain/.idea/.idea.Sample.dir/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/sample/Sample.Domain/ContactAggregate/Address.cs:
--------------------------------------------------------------------------------
1 | namespace Sample.Domain.ContactAggregate;
2 |
3 | public class Address
4 | {
5 | public AddressId Id { get; set; }
6 | public required CivicNumber CivicNumber { get; set; }
7 | public required Street Street { get; set; }
8 | public required City City { get; set; }
9 | public required State State { get; set; }
10 | public required ZipCode ZipCode { get; set; }
11 | }
--------------------------------------------------------------------------------
/sample/Sample.Domain/ContactAggregate/Contact.cs:
--------------------------------------------------------------------------------
1 | namespace Sample.Domain.ContactAggregate;
2 |
3 | public class Contact
4 | {
5 | public ContactId Id { get; set; }
6 | public required FirstName FirstName { get; set; }
7 | public required LastName LastName { get; set; }
8 | public Phone? Phone { get; set; }
9 | public PhoneType? PhoneType { get; set; }
10 |
11 | public required ICollection Addresses { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/sample/Sample.Domain/ContactAggregate/PhoneType.cs:
--------------------------------------------------------------------------------
1 | namespace Sample.Domain.ContactAggregate;
2 |
3 | public enum PhoneType
4 | {
5 | Mobile = 1,
6 | Work = 2,
7 | Home = 3
8 | }
--------------------------------------------------------------------------------
/sample/Sample.Domain/ContactAggregate/TypelySpecification.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using System.Text.RegularExpressions;
3 | using Typely.Core;
4 | using Typely.Core.Builders;
5 |
6 | namespace Sample.Domain.ContactAggregate;
7 |
8 | internal class TypelySpecification : ITypelySpecification
9 | {
10 | public void Create(ITypelyBuilder builder)
11 | {
12 | //Contact
13 | builder.OfInt().For("ContactId").GreaterThan(0);
14 |
15 | builder.OfString()
16 | .For("Phone")
17 | .MaxLength(12)
18 | .Matches(new Regex("[0-9]{3}-[0-9]{3}-[0-9]{4}"));
19 |
20 | builder.OfString()
21 | .For("ZipCode")
22 | .Matches(new Regex(@"^((\d{5}-\d{4})|(\d{5})|([A-Z|a-z]\d[A-Z|a-z]\d[A-Z|a-z]\d))$"))
23 | .Normalize(x => x.ToUpper());
24 |
25 | var title = builder.OfString()
26 | .NotEmpty()
27 | .MaxLength(100)
28 | .Normalize(x => CultureInfo.InvariantCulture.TextInfo.ToTitleCase(x));
29 |
30 | title.For("FirstName");
31 | title.For("LastName");
32 | title.For("Street");
33 | title.For("City");
34 | title.For("State");
35 |
36 | //Address
37 | builder.OfInt().For("AddressId").GreaterThan(0);
38 | builder.OfInt().For("CivicNumber").GreaterThan(0);
39 | }
40 | }
--------------------------------------------------------------------------------
/sample/Sample.Domain/Sample.Domain.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/sample/Sample.Infrastructure/AppDbContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Sample.Domain.ContactAggregate;
3 | using Typely.EfCore.Conventions;
4 |
5 | namespace Sample.Infrastructure;
6 |
7 | public class AppDbContext : DbContext
8 | {
9 | public AppDbContext(DbContextOptions options) : base(options) { }
10 |
11 | public DbSet Contacts => Set();
12 |
13 | protected override void OnModelCreating(ModelBuilder modelBuilder)
14 | {
15 | modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
16 | }
17 |
18 | protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
19 | {
20 | configurationBuilder.Conventions.AddTypelyConventions();
21 | }
22 | }
--------------------------------------------------------------------------------
/sample/Sample.Infrastructure/Migrations/20230423224612_InitialCreate.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | #nullable disable
4 |
5 | namespace Sample.Infrastructure.Migrations
6 | {
7 | ///
8 | public partial class InitialCreate : Migration
9 | {
10 | ///
11 | protected override void Up(MigrationBuilder migrationBuilder)
12 | {
13 | migrationBuilder.CreateTable(
14 | name: "Contacts",
15 | columns: table => new
16 | {
17 | Id = table.Column(type: "INTEGER", nullable: false),
18 | FirstName = table.Column(type: "TEXT", nullable: false),
19 | LastName = table.Column(type: "TEXT", nullable: false),
20 | Phone = table.Column(type: "TEXT", nullable: false),
21 | Street = table.Column(type: "TEXT", nullable: false),
22 | City = table.Column(type: "TEXT", nullable: false),
23 | State = table.Column(type: "TEXT", nullable: false),
24 | ZipCode = table.Column(type: "TEXT", nullable: false)
25 | },
26 | constraints: table =>
27 | {
28 | table.PrimaryKey("PK_Contacts", x => x.Id);
29 | });
30 | }
31 |
32 | ///
33 | protected override void Down(MigrationBuilder migrationBuilder)
34 | {
35 | migrationBuilder.DropTable(
36 | name: "Contacts");
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/sample/Sample.Infrastructure/Migrations/20230428170636_MaxLength.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | #nullable disable
4 |
5 | namespace Sample.Infrastructure.Migrations
6 | {
7 | ///
8 | public partial class MaxLength : Migration
9 | {
10 | ///
11 | protected override void Up(MigrationBuilder migrationBuilder)
12 | {
13 |
14 | }
15 |
16 | ///
17 | protected override void Down(MigrationBuilder migrationBuilder)
18 | {
19 |
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/sample/Sample.Infrastructure/Sample.Infrastructure.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": ".NET Core Launch (console)",
9 | "type": "coreclr",
10 | "request": "launch",
11 | "preLaunchTask": "build",
12 | "program": "${workspaceFolder}/Typely.Generators.Tests/bin/Debug/net7.0/Typely.Generators.Tests.dll",
13 | "args": [],
14 | "cwd": "${workspaceFolder}/Typely.Generators.Tests",
15 | "console": "internalConsole",
16 | "stopAtEntry": false
17 | },
18 | {
19 | "name": ".NET Core Attach",
20 | "type": "coreclr",
21 | "request": "attach"
22 | }
23 | ]
24 | }
--------------------------------------------------------------------------------
/src/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "build",
6 | "command": "dotnet",
7 | "type": "process",
8 | "args": [
9 | "build",
10 | "${workspaceFolder}/Typely.Generators.Tests/Typely.Generators.Tests.csproj",
11 | "/property:GenerateFullPaths=true",
12 | "/consoleloggerparameters:NoSummary"
13 | ],
14 | "problemMatcher": "$msCompile"
15 | },
16 | {
17 | "label": "publish",
18 | "command": "dotnet",
19 | "type": "process",
20 | "args": [
21 | "publish",
22 | "${workspaceFolder}/Typely.Generators.Tests/Typely.Generators.Tests.csproj",
23 | "/property:GenerateFullPaths=true",
24 | "/consoleloggerparameters:NoSummary"
25 | ],
26 | "problemMatcher": "$msCompile"
27 | },
28 | {
29 | "label": "watch",
30 | "command": "dotnet",
31 | "type": "process",
32 | "args": [
33 | "watch",
34 | "run",
35 | "--project",
36 | "${workspaceFolder}/Typely.Generators.Tests/Typely.Generators.Tests.csproj"
37 | ],
38 | "problemMatcher": "$msCompile"
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/src/Typely.AspNetCore.Swashbuckle/SwaggerGenOptionsExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Swashbuckle.AspNetCore.SwaggerGen;
3 |
4 | namespace Typely.AspNetCore.Swashbuckle;
5 |
6 | ///
7 | /// SwaggerGen options extensions.
8 | ///
9 | public static class SwaggerGenOptionsExtensions
10 | {
11 | ///
12 | /// Adds the .
13 | ///
14 | /// The options.
15 | /// The options.
16 | public static SwaggerGenOptions UseTypelySchemaFilter(this SwaggerGenOptions options)
17 | {
18 | options.SchemaFilter();
19 | return options;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Typely.AspNetCore.Swashbuckle/Typely.AspNetCore.Swashbuckle.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | 8.0.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/Typely.AspNetCore.Swashbuckle/TypelyValueSchemaFilter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.OpenApi.Models;
2 | using Swashbuckle.AspNetCore.SwaggerGen;
3 | using Typely.Core;
4 |
5 | namespace Typely.AspNetCore.Swashbuckle;
6 |
7 | public class TypelyValueSchemaFilter : ISchemaFilter
8 | {
9 | public void Apply(OpenApiSchema schema, SchemaFilterContext context)
10 | {
11 | var typelyValueType = GetTypelyValueTypeOrDefault(context.Type);
12 | if (typelyValueType is null)
13 | {
14 | return;
15 | }
16 |
17 | var valueType = typelyValueType.GenericTypeArguments[0];
18 | if (!context.SchemaRepository.TryLookupByType(valueType, out var valueSchema))
19 | {
20 | valueSchema = context.SchemaGenerator.GenerateSchema(valueType, context.SchemaRepository);
21 | }
22 |
23 | schema.Type = valueSchema.Type;
24 | schema.Format = valueSchema.Format;
25 | schema.Default = valueSchema.Default;
26 | schema.Example = valueSchema.Example;
27 | schema.Enum = valueSchema.Enum;
28 | schema.Minimum = valueSchema.Minimum;
29 | schema.Maximum = valueSchema.Maximum;
30 | schema.Pattern = valueSchema.Pattern;
31 | foreach (var (key, prop) in valueSchema.Properties)
32 | {
33 | schema.Properties.Add(key, prop);
34 | }
35 | }
36 |
37 | private static Type? GetTypelyValueTypeOrDefault(Type type) =>
38 | type.GetInterfaces()
39 | .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypelyValue<,>));
40 | }
--------------------------------------------------------------------------------
/src/Typely.AspNetCore.Swashbuckle/nuget-readme.md:
--------------------------------------------------------------------------------
1 | Typely: Unleashing the power of value object creation with a fluent Api.
2 |
3 | This library lets you use Typely in ASP.NET Core by generating OpenAPI documentation with Swashbuckle.
4 |
5 | # Documentation
6 |
7 | - https://docs.typely.net/
8 |
9 | # Usage
10 | ```csharp
11 | var builder = WebApplication.CreateBuilder(args);
12 | builder.Services.AddSwaggerGen(options => options.UseTypelySchemaFilter());
13 | ```
--------------------------------------------------------------------------------
/src/Typely.AspNetCore/Http/ApplicationBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Http;
3 | using Typely.Core;
4 |
5 | namespace Typely.AspNetCore.Http;
6 |
7 | ///
8 | /// Application builder extensions.
9 | ///
10 | public static class ApplicationBuilderExtensions
11 | {
12 | ///
13 | /// Adds a middleware to format Typely's as JSON compatible with .
14 | ///
15 | /// The instance this method extends.
16 | /// The .
17 | public static IApplicationBuilder UseTypelyValidationResult(this IApplicationBuilder app) =>
18 | app.UseMiddleware();
19 | }
--------------------------------------------------------------------------------
/src/Typely.AspNetCore/Http/TemplatedProblemDetail.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.AspNetCore.Http;
2 |
3 | ///
4 | /// A problem detail with placeholders.
5 | ///
6 | public record TemplatedProblemDetail
7 | {
8 | ///
9 | /// A unique identifier for the error.
10 | ///
11 | /// The value is used for translations.
12 | public required string Code { get; init; }
13 |
14 | ///
15 | /// The error message localized with placeholders.
16 | ///
17 | public required string Message { get; init; }
18 |
19 | ///
20 | /// The value that caused the error.
21 | ///
22 | public string? AttemptedValue { get; init; }
23 |
24 | ///
25 | /// Type's name that generated the error.
26 | ///
27 | public required string TypeName { get; init; }
28 |
29 | ///
30 | /// List of placeholders with their values.
31 | ///
32 | public required Dictionary PlaceholderValues { get; init; }
33 | }
--------------------------------------------------------------------------------
/src/Typely.AspNetCore/Http/TypelyValidationResultMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using System.Net;
3 | using System.Text.Json;
4 | using System.Text.Json.Serialization;
5 | using Typely.Core;
6 |
7 | namespace Typely.AspNetCore.Http;
8 |
9 | ///
10 | /// Middleware that catches Typely's and returns an serialized.
11 | ///
12 | public class TypelyValidationResultMiddleware
13 | {
14 | private readonly RequestDelegate _next;
15 | private readonly JsonSerializerOptions _serializerOptions;
16 |
17 | public TypelyValidationResultMiddleware(RequestDelegate next)
18 | {
19 | _next = next;
20 | _serializerOptions =
21 | new JsonSerializerOptions(JsonSerializerDefaults.Web)
22 | {
23 | DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
24 | };
25 | }
26 |
27 | public async Task InvokeAsync(HttpContext context)
28 | {
29 | try
30 | {
31 | await _next(context);
32 | }
33 | catch (ValidationException ex)
34 | {
35 | await HandleExceptionAsync(context, ex);
36 | }
37 | }
38 |
39 | private Task HandleExceptionAsync(HttpContext context, ValidationException validationException)
40 | {
41 | context.Response.ContentType = "application/json";
42 | context.Response.StatusCode = (int)HttpStatusCode.UnprocessableEntity;
43 |
44 | var problemDetails = HttpValidationTemplatedProblemDetails.From(validationException);
45 | var errorJson = JsonSerializer.Serialize(problemDetails, _serializerOptions);
46 | return context.Response.WriteAsync(errorJson);
47 | }
48 | }
--------------------------------------------------------------------------------
/src/Typely.AspNetCore/Mvc/ModelBinding/TypelyValueModelBinderProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc.ModelBinding;
2 | using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
3 | using Typely.Core;
4 |
5 | namespace Typely.AspNetCore.Mvc.ModelBinding;
6 |
7 | ///
8 | /// Provides bindings for .
9 | ///
10 | public class TypelyValueModelBinderProvider : IModelBinderProvider
11 | {
12 | public IModelBinder? GetBinder(ModelBinderProviderContext context)
13 | {
14 | if (context == null)
15 | {
16 | throw new ArgumentNullException(nameof(context));
17 | }
18 |
19 | var implementedInterface = context.Metadata.ModelType.GetInterface(typeof(ITypelyValue<,>).FullName!);
20 | if (implementedInterface == null)
21 | {
22 | return null;
23 | }
24 |
25 | var binderType = typeof(TypelyValueModelBinder<,>).MakeGenericType(implementedInterface.GenericTypeArguments);
26 | return new BinderTypeModelBinder(binderType);
27 | }
28 | }
--------------------------------------------------------------------------------
/src/Typely.AspNetCore/Mvc/MvcOptionsExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Typely.AspNetCore.Mvc.ModelBinding;
3 |
4 | namespace Typely.AspNetCore.Mvc;
5 |
6 | ///
7 | /// MVC options extensions.
8 | ///
9 | public static class MvcOptionsExtensions
10 | {
11 | ///
12 | /// Support Typely validations through model binding.
13 | ///
14 | /// The options.
15 | /// The options.
16 | public static MvcOptions UseTypelyModelBinderProvider(this MvcOptions options)
17 | {
18 | options.ModelBinderProviders.Insert(0, new TypelyValueModelBinderProvider());
19 | return options;
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Typely.AspNetCore/Typely.AspNetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | 8.0.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/Typely.AspNetCore/nuget-readme.md:
--------------------------------------------------------------------------------
1 | Typely: Unleashing the power of value object creation with a fluent Api.
2 |
3 | This library lets you use Typely in ASP.NET Core projects by providing validation exception handling and model binding.
4 |
5 | # Documentation
6 |
7 | - https://docs.typely.net/
8 |
9 | # Usage
10 |
11 | This middleware will catch any `ValidationException` thrown by Typely and return a JSON response with the validation errors.
12 | ```csharp
13 | var builder = WebApplication.CreateBuilder(args);
14 | var app = builder.Build();
15 |
16 | // Register this middleware before other middleware components
17 | app.UseTypelyValidationResult();
18 | ```
19 | Example of response:
20 | ````
21 | {
22 | "templates": {
23 | "ZipCode": [
24 | {
25 | "code": "Matches",
26 | "message": "'{Name}' is not in the correct format. Expected format '{RegularExpression}'.",
27 | "typeName": "ZipCode",
28 | "placeholderValues": {
29 | "RegularExpression": "^((\\d{5}-\\d{4})|(\\d{5})|([A-Z|a-z]\\d[A-Z|a-z]\\d[A-Z|a-z]\\d))$",
30 | "Name": "ZipCode",
31 | "ActualLength": "7"
32 | }
33 | }
34 | ]
35 | },
36 | "errors": {
37 | "ZipCode": [
38 | "'ZipCode' is not in the correct format. Expected format '^((\\d{5}-\\d{4})|(\\d{5})|([A-Z|a-z]\\d[A-Z|a-z]\\d[A-Z|a-z]\\d))$'."
39 | ]
40 | },
41 | "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
42 | "title": "One or more validation errors occurred.",
43 | "status": 400
44 | }
45 | ````
46 |
47 | If you want to add validation errors into the model state of MVC during the binding phase of the request, configure the below option:
48 | ```csharp
49 | var builder = WebApplication.CreateBuilder(args);
50 | builder.Services.AddControllers(options => options.UseTypelyModelBinderProvider());
51 | ```
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/IComparableRuleBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder of types.
5 | ///
6 | /// Type of the inheriting rule builder.
7 | /// Underlying value's type.
8 | public interface IComparableRuleBuilder
9 | {
10 | ///
11 | /// Ensure the value object's value is less than the specified one.
12 | ///
13 | /// Value to be compared to.
14 | /// Fluent .
15 | TRuleBuilder LessThan(TValue value);
16 |
17 | ///
18 | /// Ensure the value object's value is less than or equal to the specified one.
19 | ///
20 | /// Value to be compared to.
21 | /// Fluent .
22 | TRuleBuilder LessThanOrEqualTo(TValue value);
23 |
24 | ///
25 | /// Ensure the value object's value is greater than the specified one.
26 | ///
27 | /// Value to be compared to.
28 | /// Fluent .
29 | TRuleBuilder GreaterThan(TValue value);
30 |
31 | ///
32 | /// Ensure the value object's value is greater than or equal to the specified one.
33 | ///
34 | /// Value to be compared to.
35 | /// Fluent .
36 | TRuleBuilder GreaterThanOrEqualTo(TValue value);
37 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfBool.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying bool type.
5 | ///
6 | public interface ITypelyBuilderOfBool : ITypelyBuilder
7 | {
8 | }
9 |
10 | ///
11 | /// Rule builder of bool.
12 | ///
13 | public interface IRuleBuilderOfBool :
14 | IRuleBuilder,
15 | ITypelyBuilderOfBool
16 | {
17 | }
18 |
19 | ///
20 | /// Factory for creating similar value objects of bool.
21 | ///
22 | public interface IFactoryOfBool : ITypelyBuilder
23 | {
24 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfByte.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying byte type.
5 | ///
6 | public interface ITypelyBuilderOfByte : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of byte.
13 | ///
14 | public interface IRuleBuilderOfByte :
15 | IRuleBuilder,
16 | ITypelyBuilderOfByte
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of byte.
22 | ///
23 | public interface IFactoryOfByte : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfChar.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying char type.
5 | ///
6 | public interface ITypelyBuilderOfChar : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of char.
13 | ///
14 | public interface IRuleBuilderOfChar :
15 | IRuleBuilder,
16 | ITypelyBuilderOfChar
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of char.
22 | ///
23 | public interface IFactoryOfChar : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfDateOnly.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | #if NET7_0_OR_GREATER
4 | ///
5 | /// Builder dedicated to creating a value object with the underlying DateOnly type.
6 | ///
7 | public interface ITypelyBuilderOfDateOnly : ITypelyBuilder,
8 | IComparableRuleBuilder
9 | {
10 | }
11 |
12 | ///
13 | /// Rule builder of DateOnly.
14 | ///
15 | public interface IRuleBuilderOfDateOnly :
16 | IRuleBuilder,
17 | ITypelyBuilderOfDateOnly
18 | {
19 | }
20 |
21 | ///
22 | /// Factory for creating similar value objects of DateOnly.
23 | ///
24 | public interface IFactoryOfDateOnly : ITypelyBuilder
25 | {
26 | }
27 | #endif
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfDateTime.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying DateTime type.
5 | ///
6 | public interface ITypelyBuilderOfDateTime : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of DateTime.
13 | ///
14 | public interface IRuleBuilderOfDateTime :
15 | IRuleBuilder,
16 | ITypelyBuilderOfDateTime
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of DateTime.
22 | ///
23 | public interface IFactoryOfDateTime : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfDateTimeOffset.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying DateTimeOffset type.
5 | ///
6 | public interface ITypelyBuilderOfDateTimeOffset : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of DateTimeOffset.
13 | ///
14 | public interface IRuleBuilderOfDateTimeOffset :
15 | IRuleBuilder,
16 | ITypelyBuilderOfDateTimeOffset
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of DateTimeOffset.
22 | ///
23 | public interface IFactoryOfDateTimeOffset : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfDecimal.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying decimal type.
5 | ///
6 | public interface ITypelyBuilderOfDecimal : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of decimal.
13 | ///
14 | public interface IRuleBuilderOfDecimal :
15 | IRuleBuilder,
16 | ITypelyBuilderOfDecimal
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of decimal.
22 | ///
23 | public interface IFactoryOfDecimal : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfDouble.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying double type.
5 | ///
6 | public interface ITypelyBuilderOfDouble : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of double.
13 | ///
14 | public interface IRuleBuilderOfDouble :
15 | IRuleBuilder,
16 | ITypelyBuilderOfDouble
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of double.
22 | ///
23 | public interface IFactoryOfDouble : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfFloat.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying float type.
5 | ///
6 | public interface ITypelyBuilderOfFloat : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of float.
13 | ///
14 | public interface IRuleBuilderOfFloat :
15 | IRuleBuilder,
16 | ITypelyBuilderOfFloat
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of float.
22 | ///
23 | public interface IFactoryOfFloat : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfGuid.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying Guid type.
5 | ///
6 | public interface ITypelyBuilderOfGuid : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of Guid.
13 | ///
14 | public interface IRuleBuilderOfGuid :
15 | IRuleBuilder,
16 | ITypelyBuilderOfGuid
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of Guid.
22 | ///
23 | public interface IFactoryOfGuid : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfInt.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying int type.
5 | ///
6 | public interface ITypelyBuilderOfInt : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of int.
13 | ///
14 | public interface IRuleBuilderOfInt :
15 | IRuleBuilder,
16 | ITypelyBuilderOfInt
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of int.
22 | ///
23 | public interface IFactoryOfInt : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfLong.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying long type.
5 | ///
6 | public interface ITypelyBuilderOfLong : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of long.
13 | ///
14 | public interface IRuleBuilderOfLong :
15 | IRuleBuilder,
16 | ITypelyBuilderOfLong
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of long.
22 | ///
23 | public interface IFactoryOfLong : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfSByte.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying sbyte type.
5 | ///
6 | public interface ITypelyBuilderOfSByte : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of sbyte.
13 | ///
14 | public interface IRuleBuilderOfSByte :
15 | IRuleBuilder,
16 | ITypelyBuilderOfSByte
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of sbyte.
22 | ///
23 | public interface IFactoryOfSByte : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfShort.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying short type.
5 | ///
6 | public interface ITypelyBuilderOfShort : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of short.
13 | ///
14 | public interface IRuleBuilderOfShort :
15 | IRuleBuilder,
16 | ITypelyBuilderOfShort
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of short.
22 | ///
23 | public interface IFactoryOfShort : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfString.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 |
3 | namespace Typely.Core.Builders;
4 |
5 | ///
6 | /// Builder dedicated to creating a value object with the underlying string type.
7 | ///
8 | public interface ITypelyBuilderOfString : ITypelyBuilder,
9 | IComparableRuleBuilder
10 | {
11 | IRuleBuilderOfString Length(int min, int max);
12 | IRuleBuilderOfString Length(int exactLength);
13 | IRuleBuilderOfString MinLength(int minLength);
14 | IRuleBuilderOfString MaxLength(int maxLength);
15 | IRuleBuilderOfString Matches(Regex regex);
16 | }
17 |
18 | ///
19 | /// Rule builder of string.
20 | ///
21 | public interface IRuleBuilderOfString :
22 | IRuleBuilder,
23 | ITypelyBuilderOfString
24 | {
25 | }
26 |
27 | ///
28 | /// Factory for creating similar value objects of string.
29 | ///
30 | public interface IFactoryOfString : ITypelyBuilder
31 | {
32 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfTimeOnly.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | #if NET7_0_OR_GREATER
4 | ///
5 | /// Builder dedicated to creating a value object with the underlying TimeOnly type.
6 | ///
7 | public interface ITypelyBuilderOfTimeOnly : ITypelyBuilder,
8 | IComparableRuleBuilder
9 | {
10 | }
11 |
12 | ///
13 | /// Rule builder of TimeOnly.
14 | ///
15 | public interface IRuleBuilderOfTimeOnly :
16 | IRuleBuilder,
17 | ITypelyBuilderOfTimeOnly
18 | {
19 | }
20 |
21 | ///
22 | /// Factory for creating similar value objects of TimeOnly.
23 | ///
24 | public interface IFactoryOfTimeOnly : ITypelyBuilder
25 | {
26 | }
27 | #endif
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfTimeSpan.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying TimeSpan type.
5 | ///
6 | public interface ITypelyBuilderOfTimeSpan : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of TimeSpan.
13 | ///
14 | public interface IRuleBuilderOfTimeSpan :
15 | IRuleBuilder,
16 | ITypelyBuilderOfTimeSpan
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of TimeSpan.
22 | ///
23 | public interface IFactoryOfTimeSpan : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfUInt.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying uint type.
5 | ///
6 | public interface ITypelyBuilderOfUInt : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of uint.
13 | ///
14 | public interface IRuleBuilderOfUInt :
15 | IRuleBuilder,
16 | ITypelyBuilderOfUInt
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of uint.
22 | ///
23 | public interface IFactoryOfUInt : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfULong.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying long type.
5 | ///
6 | public interface ITypelyBuilderOfULong : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of long.
13 | ///
14 | public interface IRuleBuilderOfULong :
15 | IRuleBuilder,
16 | ITypelyBuilderOfULong
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of long.
22 | ///
23 | public interface IFactoryOfULong : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Builders/ITypelyBuilderOfUShort.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Builders;
2 |
3 | ///
4 | /// Builder dedicated to creating a value object with the underlying short type.
5 | ///
6 | public interface ITypelyBuilderOfUShort : ITypelyBuilder,
7 | IComparableRuleBuilder
8 | {
9 | }
10 |
11 | ///
12 | /// Rule builder of short.
13 | ///
14 | public interface IRuleBuilderOfUShort :
15 | IRuleBuilder,
16 | ITypelyBuilderOfUShort
17 | {
18 | }
19 |
20 | ///
21 | /// Factory for creating similar value objects of short.
22 | ///
23 | public interface IFactoryOfUShort : ITypelyBuilder
24 | {
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Converters/TypelyJsonConverter.cs:
--------------------------------------------------------------------------------
1 | #if NET7_0_OR_GREATER
2 |
3 | using System.Text.Json.Serialization;
4 | using System.Text.Json;
5 |
6 | namespace Typely.Core.Converters;
7 |
8 | ///
9 | /// Json converter for typely types.
10 | ///
11 | /// The wrapped type.
12 | /// The value object type.
13 | public class TypelyJsonConverter : JsonConverter where TTypelyValue : ITypelyValue
14 | {
15 | ///
16 | public override TTypelyValue? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
17 | {
18 | var value = JsonSerializer.Deserialize(ref reader, options);
19 |
20 | return TTypelyValue.TryFrom(value!, out var typelyType, out var validationError)
21 | ? typelyType
22 | : throw new ValidationException(validationError!);
23 | }
24 |
25 | ///
26 | public override void Write(Utf8JsonWriter writer, TTypelyValue typelyType, JsonSerializerOptions options) =>
27 | JsonSerializer.Serialize(writer, typelyType.Value, options);
28 | }
29 | #endif
--------------------------------------------------------------------------------
/src/Typely.Core/Converters/TypelyTypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Globalization;
3 |
4 | namespace Typely.Core.Converters;
5 |
6 | ///
7 | /// Provides a way of converting types to their underlying value type and vice versa.
8 | ///
9 | /// The underlying type of the value object.
10 | /// Value object's type.
11 | public class TypelyTypeConverter : TypeConverter
12 | where TTypelyValue : ITypelyValue
13 | {
14 | private readonly TypeConverter _underlyingValueConverter = TypeDescriptor.GetConverter(typeof(TValue));
15 |
16 | ///
17 | public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) =>
18 | sourceType == typeof(TValue) || _underlyingValueConverter.CanConvertFrom(context, sourceType);
19 |
20 | ///
21 | public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) =>
22 | destinationType == typeof(TValue) || _underlyingValueConverter.CanConvertTo(context, destinationType);
23 |
24 | ///
25 | public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
26 | {
27 | if (value is not TValue underlyingValue)
28 | {
29 | underlyingValue = (TValue)_underlyingValueConverter.ConvertFrom(context, culture, value)!;
30 | }
31 |
32 | return TypelyValue.From(underlyingValue);
33 | }
34 |
35 | ///
36 | public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value,
37 | Type destinationType) =>
38 | value is not ITypelyValue typelyValue ? default(object?) : typelyValue.Value;
39 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Extensions/TypeExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core.Extensions;
2 |
3 | ///
4 | /// Type extensions
5 | ///
6 | public static class TypeExtensions
7 | {
8 | ///
9 | /// Determines whether is implemented by the current .
10 | ///
11 | /// The type to be verified.
12 | /// when the type implements the interface.
13 | public static bool ImplementsITypelyValue(this Type type)
14 | {
15 | var underlyingType = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)
16 | ? Nullable.GetUnderlyingType(type)!
17 | : type;
18 |
19 | var typelyType = typeof(ITypelyValue<,>);
20 | return underlyingType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typelyType);
21 | }
22 |
23 | ///
24 | /// Return the underlying type when nullable or the current type.
25 | ///
26 | /// Type.
27 | /// The wrapped type
28 | public static Type GetTypeOrUnderlyingType(this Type type)
29 | {
30 | var nullable = type.IsGenericType &&
31 | type.GetGenericTypeDefinition() == typeof(Nullable<>);
32 | return nullable ? Nullable.GetUnderlyingType(type)! : type;
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Typely.Core/IMaxLength.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core;
2 |
3 | public interface IMaxLength
4 | {
5 | static abstract int MaxLength { get; }
6 | }
--------------------------------------------------------------------------------
/src/Typely.Core/ITypelySpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core.Builders;
2 |
3 | namespace Typely.Core;
4 |
5 | ///
6 | /// Represent a specification of how to create value objects.
7 | ///
8 | ///
9 | /// The class will be parsed as text by the generator. You cannot create and use custom functions inside the class.
10 | ///
11 | public interface ITypelySpecification
12 | {
13 | ///
14 | /// Define how to create value objects.
15 | ///
16 | /// Builder used to create value objects.
17 | void Create(ITypelyBuilder builder);
18 | }
--------------------------------------------------------------------------------
/src/Typely.Core/Typely.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | Typely: Unleashing the power of value object creation with a fluent Api. This package contains interfaces that a consumer must implement.
6 | 8.0.0
7 |
8 |
9 |
10 |
11 | True
12 | True
13 | ErrorMessages.resx
14 |
15 |
16 |
17 |
18 |
19 | PublicResXFileCodeGenerator
20 | ErrorMessages.Designer.cs
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/Typely.Core/TypelyOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core;
2 |
3 | ///
4 | /// Options to modify the Typely's value object at runtime.
5 | ///
6 | public class TypelyOptions
7 | {
8 | public static TypelyOptions Instance { get; } = new();
9 |
10 | ///
11 | /// Add the actual value to the exceptions when thrown.
12 | ///
13 | /// Output the actual value when set to .
14 | /// Fluent
15 | public TypelyOptions EnableSensitiveDataLogging(bool enabled = true)
16 | {
17 | IsSensitiveDataLoggingEnabled = enabled;
18 | return this;
19 | }
20 |
21 | ///
22 | /// if the actual value being validated will be thrown in exceptions.
23 | ///
24 | public bool IsSensitiveDataLoggingEnabled { get; private set; }
25 | }
--------------------------------------------------------------------------------
/src/Typely.Core/TypelyValue.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core;
2 |
3 | ///
4 | /// Static methods for a type generated by Typely.
5 | ///
6 | public static class TypelyValue
7 | {
8 | ///
9 | /// Validates the value object and throw if in error.
10 | ///
11 | /// The underlying type of the value object.
12 | /// Value object's type.
13 | /// Value to validate.
14 | ///
15 | public static void ValidateAndThrow(TValue value) where TTypelyValue : ITypelyValue
16 | {
17 | #if NET7_0_OR_GREATER
18 | var validationError = TTypelyValue.Validate(value);
19 | if (validationError != null)
20 | {
21 | throw new ValidationException(validationError);
22 | }
23 | #else
24 | throw new NotImplementedException();
25 | #endif
26 | }
27 |
28 | ///
29 | /// Creates a if the validation succeeds.
30 | ///
31 | /// The underlying value.
32 | /// The created .
33 | /// If validations failed.
34 | public static TTypelyValue From(TValue value) where TTypelyValue : ITypelyValue
35 | {
36 | #if NET7_0_OR_GREATER
37 | return TTypelyValue.From(value);
38 | #else
39 | throw new NotImplementedException();
40 | #endif
41 | }
42 | }
--------------------------------------------------------------------------------
/src/Typely.Core/ValidationErrorFactory.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core;
2 |
3 | ///
4 | /// Factory for ValidationError
5 | ///
6 | public static class ValidationErrorFactory
7 | {
8 | ///
9 | /// Creates a ValidationError.
10 | ///
11 | /// Type of the value.
12 | /// Validated value.
13 | /// Unique code for the error.
14 | /// Error message containing placeholders in braces.
15 | /// The name of the calss or struct to create.
16 | /// List of the placeholders with their values.
17 | /// A .
18 | public static ValidationError Create(TValue value, string errorCode,
19 | string errorMessageWithPlaceholders, string typeName, Dictionary? placeholderValues = null)
20 | {
21 | object? attemptedValue = null;
22 |
23 | if (placeholderValues == null)
24 | {
25 | placeholderValues = new Dictionary();
26 | }
27 |
28 | placeholderValues.TryAdd(ValidationPlaceholders.Name, typeName);
29 |
30 | if (typeof(TValue) == typeof(string))
31 | {
32 | var actualLength = (value as string)?.Length ?? 0;
33 | placeholderValues.Add(ValidationPlaceholders.ActualLength, actualLength.ToString());
34 | }
35 |
36 | if (TypelyOptions.Instance.IsSensitiveDataLoggingEnabled)
37 | {
38 | placeholderValues.TryAdd(ValidationPlaceholders.Value, value);
39 | attemptedValue = value;
40 | }
41 |
42 | return new ValidationError(errorCode, errorMessageWithPlaceholders, attemptedValue, typeName, placeholderValues);
43 | }
44 | }
--------------------------------------------------------------------------------
/src/Typely.Core/ValidationException.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core;
2 |
3 | ///
4 | /// Exception thrown when a validation fails.
5 | ///
6 | public class ValidationException : Exception
7 | {
8 | public ValidationError ValidationError { get; }
9 |
10 | public ValidationException(ValidationError validationError) : base(validationError.ErrorMessage)
11 | {
12 | ValidationError = validationError;
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Typely.Core/ValidationPlaceholders.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Core;
2 |
3 | ///
4 | /// Keywords used in validation messages.
5 | ///
6 | public class ValidationPlaceholders
7 | {
8 | public const string Name = nameof(Name);
9 | public const string Value = nameof(Value);
10 | public const string ComparisonValue = nameof(ComparisonValue);
11 | public const string MinLength = nameof(MinLength);
12 | public const string MaxLength = nameof(MaxLength);
13 | public const string ExactLength = nameof(ExactLength);
14 | public const string ActualLength = nameof(ActualLength);
15 | public const string Min = nameof(Min);
16 | public const string Max = nameof(Max);
17 | }
--------------------------------------------------------------------------------
/src/Typely.Core/nuget-readme.md:
--------------------------------------------------------------------------------
1 | Typely: Unleashing the power of value object creation with a fluent Api.
2 |
3 | ## Example
4 |
5 | ```csharp
6 | public class TypesSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("Votes");
11 | builder.OfString().For("Code").Length(4).NotEqual("0000");
12 |
13 | builder.OfString()
14 | .For("UserId")
15 | .WithNamespace("UserAggregate")
16 | .WithName("Owner identifier")
17 | .NotEmpty()
18 | .NotEqual("0").WithMessage("{Name} cannot be equal to {ComparisonValue}.").WithErrorCode("ERR001")
19 | .MaxLength(20);
20 |
21 | builder.OfString()
22 | .For("Monday")
23 | .AsClass()
24 | .WithName(() => LocalizedNames.Moment)
25 | .MinLength(1).WithMessage(() => LocalizedMessages.MinLengthCustom)
26 | .MaxLength(20).WithMessage(() => LocalizedMessages.MaxLengthCustom);
27 | }
28 | }
29 | ```
30 |
31 | # Documentation
32 |
33 | - https://docs.typely.net/
34 |
35 | # Prerequisites
36 |
37 | - Supported .NET versions
38 | - .NET 7.0 and greater
39 |
40 | # Getting started
41 |
42 | Install packages
43 | ```
44 | dotnet add package Typely.Core
45 | dotnet add package Typely.Generators
46 | ```
47 |
48 | Create a class inheriting from `ITypelySpecification`
49 | ```csharp
50 | public class TypesSpecification : ITypelySpecification
51 | {
52 | public void Create(ITypelyBuilder builder)
53 | {
54 | builder.OfString().For("FirstName").NotEmpty();
55 | }
56 | }
57 | ```
58 |
59 | Usage
60 | ```csharp
61 | var firstName = FirstName.From("Adam");
62 | FirstName.From(""); //Throws ValidationException
63 |
64 | if(!FirstName.TryFrom("value", out FirstName instance, out ValidationError? validationError))
65 | {
66 | // Handle error
67 | }
68 | ```
--------------------------------------------------------------------------------
/src/Typely.EfCore/Conventions/ConventionModelBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Metadata;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 | using Typely.Core;
4 | using Typely.Core.Extensions;
5 |
6 | namespace Typely.EfCore.Conventions;
7 |
8 | internal static class ConventionModelBuilderExtensions
9 | {
10 | ///
11 | /// Gets all non-navigation properties implementing declared on the entity type.
12 | ///
13 | /// Convention model builder
14 | /// Declared non-navigation properties implementing .
15 | public static IEnumerable
16 | GetTypelyValueProperties(this IConventionModelBuilder modelBuilder) =>
17 | modelBuilder.Metadata
18 | .GetEntityTypes()
19 | .SelectMany(x => x.GetDeclaredProperties())
20 | .Where(x => x.ClrType.ImplementsITypelyValue());
21 | }
--------------------------------------------------------------------------------
/src/Typely.EfCore/Conventions/TypelyConversionConvention.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
2 | using Microsoft.EntityFrameworkCore.Metadata.Conventions;
3 | using Typely.Core;
4 | using Typely.Core.Extensions;
5 |
6 | namespace Typely.EfCore.Conventions;
7 |
8 | ///
9 | /// Apply the value converter as a convention
10 | /// to all properties when a model is being finalized.
11 | ///
12 | ///
13 | /// See Model building conventions for more information and examples.
14 | ///
15 | public class TypelyConversionConvention : IModelFinalizingConvention
16 | {
17 | ///
18 | /// Called when a model is being finalized.
19 | ///
20 | /// The builder for the model.
21 | /// Additional information associated with convention execution.
22 | public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder,
23 | IConventionContext context)
24 | {
25 | foreach (var property in modelBuilder.GetTypelyValueProperties())
26 | {
27 | var typelyValueType = property.ClrType.GetTypeOrUnderlyingType();
28 |
29 | var valueType = typelyValueType
30 | .GetInterfaces()
31 | .First(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ITypelyValue<,>))
32 | .GetGenericArguments()[0];
33 |
34 | var converter = typeof(TypelyValueConverter<,>).MakeGenericType(valueType, typelyValueType);
35 |
36 | property.Builder.HasConverter(converter);
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/Typely.EfCore/Conventions/TypelyMaxLengthConvention.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
2 | using Microsoft.EntityFrameworkCore.Metadata.Conventions;
3 | using Typely.Core;
4 | using Typely.Core.Extensions;
5 |
6 | namespace Typely.EfCore.Conventions;
7 |
8 | ///
9 | /// Configures the maximum length of data that can be stored in all
10 | /// properties when a model is being finalized.
11 | ///
12 | ///
13 | /// See Model building conventions for more information and examples.
14 | ///
15 | public class TypelyMaxLengthConvention : IModelFinalizingConvention
16 | {
17 | ///
18 | /// Called when a model is being finalized.
19 | ///
20 | /// The builder for the model.
21 | /// Additional information associated with convention execution.
22 | public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder,
23 | IConventionContext context)
24 | {
25 | var maxLengthType = typeof(IMaxLength);
26 |
27 | foreach (var property in modelBuilder.GetTypelyValueProperties())
28 | {
29 | var typelyValueType = property.ClrType.GetTypeOrUnderlyingType();
30 |
31 | if (maxLengthType.IsAssignableFrom(typelyValueType))
32 | {
33 | var getMaxLengthMethod = typelyValueType.GetMethod("get_" + nameof(IMaxLength.MaxLength));
34 | var maxLength = (int)getMaxLengthMethod!.Invoke(null, null)!;
35 |
36 | property.Builder.HasMaxLength(maxLength);
37 | }
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/Typely.EfCore/Typely.EfCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | Typely: Unleashing the power of value object creation with a fluent Api. This package contains integrations with EF Core.
6 | 8.0.0
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/Typely.EfCore/TypelyValueConverter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
2 | using Typely.Core;
3 |
4 | namespace Typely.EfCore;
5 |
6 | ///
7 | /// Defines conversions from an to the underlying value's type, in the store.
8 | ///
9 | ///
10 | /// See EF Core value converters for more information and examples.
11 | ///
12 | public class TypelyValueConverter : ValueConverter
13 | where TTypelyValue : ITypelyValue
14 | {
15 | ///
16 | /// Initializes a new instance of the class.
17 | ///
18 | public TypelyValueConverter()
19 | : base(
20 | v => v.Value,
21 | v => TypelyValue.From(v))
22 | {
23 | }
24 | }
--------------------------------------------------------------------------------
/src/Typely.EfCore/nuget-readme.md:
--------------------------------------------------------------------------------
1 | Typely: Unleashing the power of value object creation with a fluent Api.
2 |
3 | This library lets you use Typely with Entity Framework Core by providing a set of conventions.
4 |
5 | # Documentation
6 |
7 | - https://docs.typely.net/
8 |
9 | # Usage
10 |
11 | ```csharp
12 | protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
13 | {
14 | configurationBuilder.Conventions.AddTypelyConventions();
15 | }
16 | ```
17 |
--------------------------------------------------------------------------------
/src/Typely.Generators/Comparers/DictionaryComparer.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Generators.Comparers;
2 |
3 | public class DictionaryComparer : IEqualityComparer>
4 | where TKey : notnull
5 | {
6 | public static readonly DictionaryComparer Default = new();
7 |
8 | private readonly IEqualityComparer _valueComparer;
9 |
10 | private DictionaryComparer(IEqualityComparer? valueComparer = null)
11 | {
12 | _valueComparer = valueComparer ?? EqualityComparer.Default;
13 | }
14 |
15 | public bool Equals(IDictionary? dict1, IDictionary? dict2)
16 | {
17 | if (ReferenceEquals(dict1, dict2))
18 | {
19 | return true;
20 | }
21 |
22 | if (dict1 is null || dict2 is null)
23 | {
24 | return false;
25 | }
26 |
27 | if (dict1.Count != dict2.Count)
28 | {
29 | return false;
30 | }
31 |
32 | foreach (var keyValuePair in dict1)
33 | {
34 | if (!dict2.TryGetValue(keyValuePair.Key, out var value))
35 | {
36 | return false;
37 | }
38 |
39 | if (!_valueComparer.Equals(keyValuePair.Value, value))
40 | {
41 | return false;
42 | }
43 | }
44 |
45 | return true;
46 | }
47 |
48 | public int GetHashCode(IDictionary? obj)
49 | {
50 | if (obj == null)
51 | {
52 | return 0;
53 | }
54 |
55 | int hash = 1;
56 | int keyHash = 0;
57 | int valueHash = 0;
58 |
59 | foreach (var pair in obj)
60 | {
61 | keyHash += EqualityComparer.Default.GetHashCode(pair.Key);
62 | valueHash += _valueComparer.GetHashCode(pair.Value);
63 | }
64 |
65 | hash = hash * 31 + keyHash;
66 | hash = hash * 31 + valueHash;
67 |
68 | return hash;
69 | }
70 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Comparers/ImmutableArrayComparer.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 |
3 | namespace Typely.Generators.Comparers;
4 |
5 | public class ImmutableArrayComparer : IEqualityComparer>
6 | {
7 | public static readonly ImmutableArrayComparer Default = new();
8 |
9 | private readonly IEqualityComparer _valueComparer;
10 |
11 | private ImmutableArrayComparer(IEqualityComparer? valueComparer = null)
12 | {
13 | _valueComparer = valueComparer ?? EqualityComparer.Default;
14 | }
15 |
16 | public bool Equals(ImmutableArray arr1, ImmutableArray arr2)
17 | {
18 | if (arr1.Length != arr2.Length)
19 | {
20 | return false;
21 | }
22 |
23 | foreach (var value in arr1)
24 | {
25 | if (!arr2.Contains(value))
26 | {
27 | return false;
28 | }
29 | }
30 |
31 | return true;
32 | }
33 |
34 | public int GetHashCode(ImmutableArray arr)
35 | {
36 | int hash = 1;
37 |
38 | foreach (var value in arr)
39 | {
40 | hash = hash * 31 + _valueComparer.GetHashCode(value);
41 | }
42 |
43 | return hash;
44 | }
45 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Extensions/ClassDeclarationSyntaxExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis.CSharp.Syntax;
2 |
3 | namespace Typely.Generators.Extensions;
4 |
5 | ///
6 | /// Extensions over .
7 | ///
8 | internal static class ClassDeclarationSyntaxExtensions
9 | {
10 | ///
11 | /// Indicates whether or not the class has a specific interface.
12 | ///
13 | public static bool HasInterface(this ClassDeclarationSyntax source, string interfaceName) =>
14 | source.BaseList != null && source.BaseList.Types.Any(x => x.ToString() == interfaceName);
15 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Infrastructure/SymbolExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Infrastructure;
4 |
5 | public static class SymbolExtensions
6 | {
7 | public static bool Implements(this ITypeSymbol type, ITypeSymbol interfaceType)
8 | {
9 | foreach (var t in type.AllInterfaces)
10 | {
11 | if (SymbolEqualityComparer.Default.Equals(t, interfaceType))
12 | {
13 | return true;
14 | }
15 | }
16 | return false;
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/ErrorCodes.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Generators.Typely;
2 |
3 | ///
4 | /// Unique error codes.
5 | ///
6 | internal static class ErrorCodes
7 | {
8 | public const string NotEmpty = nameof(NotEmpty);
9 | public const string NotEqual = nameof(NotEqual);
10 | public const string Length = nameof(Length);
11 | public const string ExactLength = nameof(ExactLength);
12 | public const string MinLength = nameof(MinLength);
13 | public const string MaxLength = nameof(MaxLength);
14 | public const string LessThan = nameof(LessThan);
15 | public const string LessThanOrEqualTo = nameof(LessThanOrEqualTo);
16 | public const string GreaterThan = nameof(GreaterThan);
17 | public const string GreaterThanOrEqualTo = nameof(GreaterThanOrEqualTo);
18 | public const string InclusiveBetween = nameof(InclusiveBetween);
19 | public const string ExclusiveBetween = nameof(ExclusiveBetween);
20 | public const string Must = nameof(Must);
21 | public const string Matches = nameof(Matches);
22 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/ErrorMessageResource.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Generators.Typely;
2 |
3 | public static class ErrorMessageResource
4 | {
5 | public const string NotEmpty = "ErrorMessages.NotEmpty";
6 | public const string NotEqual = "ErrorMessages.NotEqual";
7 | public const string Length = "ErrorMessages.Length";
8 | public const string ExactLength = "ErrorMessages.ExactLength";
9 | public const string MinLength = "ErrorMessages.MinLength";
10 | public const string MaxLength = "ErrorMessages.MaxLength";
11 | public const string GreaterThan = "ErrorMessages.GreaterThan";
12 | public const string GreaterThanOrEqualTo = "ErrorMessages.GreaterThanOrEqualTo";
13 | public const string LessThan = "ErrorMessages.LessThan";
14 | public const string LessThanOrEqualTo = "ErrorMessages.LessThanOrEqualTo";
15 | public const string Matches = "ErrorMessages.Matches";
16 | public const string Must = "ErrorMessages.Must";
17 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/ConstructTypeKind.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Generators.Typely.Parsing;
2 |
3 | ///
4 | /// Enumeration for possible kinds of type constructs.
5 | ///
6 | internal enum ConstructTypeKind
7 | {
8 | Struct,
9 | Class
10 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/EmittableTypeOrDiagnostic.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing;
4 |
5 | internal class EmittableTypeOrDiagnostic : IEquatable
6 | {
7 | public EmittableType? EmittableType { get; }
8 | public Diagnostic? Diagnostic { get; }
9 |
10 | public EmittableTypeOrDiagnostic(EmittableType emittableType)
11 | {
12 | EmittableType = emittableType;
13 | Diagnostic = null;
14 | }
15 |
16 | public EmittableTypeOrDiagnostic(Diagnostic diagnostic)
17 | {
18 | EmittableType = null;
19 | Diagnostic = diagnostic;
20 | }
21 |
22 |
23 | public bool Equals(EmittableTypeOrDiagnostic? other)
24 | {
25 | if (ReferenceEquals(null, other))
26 | {
27 | return false;
28 | }
29 |
30 | if (ReferenceEquals(this, other))
31 | {
32 | return true;
33 | }
34 |
35 | return Equals(EmittableType, other.EmittableType) && Equals(Diagnostic, other.Diagnostic);
36 | }
37 |
38 | public override bool Equals(object? obj)
39 | {
40 | return ReferenceEquals(this, obj) || obj is EmittableTypeOrDiagnostic other && Equals(other);
41 | }
42 |
43 | public override int GetHashCode()
44 | {
45 | unchecked
46 | {
47 | return ((EmittableType != null ? EmittableType.GetHashCode() : 0) * 397) ^ (Diagnostic != null ? Diagnostic.GetHashCode() : 0);
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/NamespaceResolver.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing;
4 |
5 | public static class NamespaceResolver
6 | {
7 | public static IEnumerable GetNamespacesFromLambda(SyntaxNode lambdaNode, SemanticModel semanticModel)
8 | {
9 | var namespaces = new HashSet();
10 | var symbolInfo = semanticModel.GetSymbolInfo(lambdaNode);
11 |
12 | if (symbolInfo.Symbol != null)
13 | {
14 | var containingNamespace = symbolInfo.Symbol.ContainingNamespace;
15 | if (containingNamespace != null)
16 | {
17 | namespaces.Add(containingNamespace.ToDisplayString());
18 | }
19 | }
20 |
21 | return namespaces;
22 | }
23 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/ParsedInvocation.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis.CSharp.Syntax;
2 | using System.Diagnostics;
3 |
4 | namespace Typely.Generators.Typely.Parsing;
5 |
6 | [DebuggerDisplay("{MemberName}({ArgumentListSyntax})")]
7 | internal class ParsedInvocation
8 | {
9 | public string MemberName { get; set; }
10 | public ArgumentListSyntax ArgumentListSyntax { get; set; }
11 |
12 | public ParsedInvocation(ArgumentListSyntax argumentListSyntax, string memberName)
13 | {
14 | ArgumentListSyntax = argumentListSyntax;
15 | MemberName = memberName;
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/ParsedStatement.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing;
4 |
5 | internal class ParsedStatement
6 | {
7 | public ParsedStatement(SemanticModel semanticModel)
8 | {
9 | SemanticModel = semanticModel;
10 | }
11 |
12 | public string Root { get; set; } = string.Empty;
13 | public SemanticModel SemanticModel { get; }
14 |
15 | public List Invocations { get; } = new();
16 |
17 | public bool IsValid() => !string.IsNullOrWhiteSpace(Root);
18 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfBool.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfBool : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfBool(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("bool", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfByte.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfByte : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfByte(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("byte", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfChar.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfChar : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfChar(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("char", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfDateOnly.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfDateOnly : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfDateOnly(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("DateOnly", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfDateTime.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfDateTime : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfDateTime(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("DateTime", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfDateTimeOffset.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfDateTimeOffset : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfDateTimeOffset(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("DateTimeOffset", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfDecimal.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfDecimal : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfDecimal(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("decimal", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfDouble.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfDouble : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfDouble(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("double", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfFloat.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfFloat : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfFloat(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("float", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfGuid.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfGuid : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfGuid(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("Guid", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfInt.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfInt : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfInt(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("int", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfLong.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfLong : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfLong(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("long", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfSByte.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfSByte : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfSByte(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("sbyte", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfShort.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfShort : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfShort(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("short", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfTimeOnly.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfTimeOnly : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfTimeOnly(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("TimeOnly", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfTimeSpan.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfTimeSpan : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfTimeSpan(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("TimeSpan", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfULong.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfULong : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfULong(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("ulong", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfUShort.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfUShort : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfUShort(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("ushort", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/EmittableTypeBuilderOfUint.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
4 |
5 | internal class EmittableTypeBuilderOfUint : EmittableTypeBuilderBase, IEmittableTypeBuilder
6 | {
7 | public EmittableTypeBuilderOfUint(string defaultNamespace, IEnumerable invocations, SemanticModel model)
8 | : base(invocations, new EmittableTypeBuilder("uint", true, defaultNamespace), model)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeBuilders/IEmittableTypeBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Generators.Typely.Parsing.TypeBuilders;
2 |
3 | internal interface IEmittableTypeBuilder
4 | {
5 | EmittableType? Build();
6 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/Parsing/TypeProperties.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Generators.Typely.Parsing;
2 |
3 | public class TypeProperties : Dictionary
4 | {
5 | public const string MaxLength = nameof(MaxLength);
6 |
7 | public void SetMaxLength(int value)
8 | {
9 | if (!ContainsKey(MaxLength))
10 | {
11 | Add(MaxLength, value);
12 | }
13 | else
14 | {
15 | if ((int)this[MaxLength] > value)
16 | {
17 | this[MaxLength] = value;
18 | }
19 | }
20 | }
21 |
22 | public bool ContainsMaxLength() => ContainsKey(MaxLength);
23 |
24 | public object GetMaxLength() => this[MaxLength];
25 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/SupportedMembers.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Generators.Typely;
2 |
3 | public class SupportedMembers
4 | {
5 | public static IEnumerable All = new[]
6 | {
7 | TypelyBuilder.OfBool, TypelyBuilder.OfByte, TypelyBuilder.OfChar, TypelyBuilder.OfDateTime,
8 | TypelyBuilder.OfDateTimeOffset, TypelyBuilder.OfDecimal, TypelyBuilder.OfDouble, TypelyBuilder.OfFloat,
9 | TypelyBuilder.OfGuid, TypelyBuilder.OfInt, TypelyBuilder.OfLong, TypelyBuilder.OfSByte,
10 | TypelyBuilder.OfShort, TypelyBuilder.OfString, TypelyBuilder.OfTimeSpan, TypelyBuilder.OfUInt,
11 | TypelyBuilder.OfULong, TypelyBuilder.OfUShort, TypelyBuilder.OfDateOnly, TypelyBuilder.OfTimeOnly,
12 | TypelyBuilderOf.ForMethodName, TypelyBuilderOf.AsClassMethodName, TypelyBuilderOf.AsStructMethodName,
13 | TypelyBuilderOf.WithNameMethodName, TypelyBuilderOf.WithNamespaceMethodName,
14 | TypelyBuilderOf.NormalizeMethodName, TypelyBuilderOf.AsFactoryMethodName,
15 | TypelyBuilderOf.NotEmptyMethodName, TypelyBuilderOf.NotEqualMethodName, TypelyBuilderOf.MustMethodName,
16 | TypelyBuilderOf.MinLengthMethodName, TypelyBuilderOf.MaxLengthMethodName, TypelyBuilderOf.MatchesMethodName,
17 | TypelyBuilderOf.LengthMethodName, TypelyBuilderOf.GreaterThanMethodName,
18 | TypelyBuilderOf.GreaterThanOrEqualToMethodName, TypelyBuilderOf.LessThanMethodName,
19 | TypelyBuilderOf.LessThanOrEqualToMethodName, TypelyBuilderOf.WithMessageMethodName,
20 | TypelyBuilderOf.WithErrorCodeMethodName,
21 | };
22 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/TypelyBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Generators.Typely;
2 |
3 | public static class TypelyBuilder
4 | {
5 | public const string OfBool = nameof(OfBool);
6 | public const string OfByte = nameof(OfByte);
7 | public const string OfChar = nameof(OfChar);
8 | public const string OfDateTime = nameof(OfDateTime);
9 | public const string OfDateTimeOffset = nameof(OfDateTimeOffset);
10 | public const string OfDecimal = nameof(OfDecimal);
11 | public const string OfDouble = nameof(OfDouble);
12 | public const string OfFloat = nameof(OfFloat);
13 | public const string OfGuid = nameof(OfGuid);
14 | public const string OfInt = nameof(OfInt);
15 | public const string OfLong = nameof(OfLong);
16 | public const string OfSByte = nameof(OfSByte);
17 | public const string OfShort = nameof(OfShort);
18 | public const string OfString = nameof(OfString);
19 | public const string OfTimeSpan = nameof(OfTimeSpan);
20 | public const string OfUInt = nameof(OfUInt);
21 | public const string OfULong = nameof(OfULong);
22 | public const string OfUShort = nameof(OfUShort);
23 | public const string OfDateOnly = nameof(OfDateOnly);
24 | public const string OfTimeOnly = nameof(OfTimeOnly);
25 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/TypelyBuilderOf.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Generators.Typely;
2 |
3 | public static class TypelyBuilderOf
4 | {
5 | public const string ForMethodName = "For";
6 | public const string AsClassMethodName = "AsClass";
7 | public const string AsStructMethodName = "AsStruct";
8 | public const string WithNameMethodName = "WithName";
9 | public const string WithNamespaceMethodName = "WithNamespace";
10 | public const string NormalizeMethodName = "Normalize";
11 | public const string AsFactoryMethodName = "AsFactory";
12 | public const string NotEmptyMethodName = "NotEmpty";
13 | public const string NotEqualMethodName = "NotEqual";
14 | public const string MustMethodName = "Must";
15 | public const string MinLengthMethodName = "MinLength";
16 | public const string MaxLengthMethodName = "MaxLength";
17 | public const string MatchesMethodName = "Matches";
18 | public const string LengthMethodName = "Length";
19 | public const string GreaterThanMethodName = "GreaterThan";
20 | public const string GreaterThanOrEqualToMethodName = "GreaterThanOrEqualTo";
21 | public const string LessThanMethodName = "LessThan";
22 | public const string LessThanOrEqualToMethodName = "LessThanOrEqualTo";
23 | public const string WithMessageMethodName = "WithMessage";
24 | public const string WithErrorCodeMethodName = "WithErrorCode";
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/src/Typely.Generators/Typely/ValidationPlaceholder.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.Generators.Typely;
2 |
3 | public static class ValidationPlaceholder
4 | {
5 | public const string ComparisonValue = nameof(ComparisonValue);
6 | public const string MinLength = nameof(MinLength);
7 | public const string MaxLength = nameof(MaxLength);
8 | public const string ExactLength = nameof(ExactLength);
9 | public const string RegularExpression = nameof(RegularExpression);
10 | }
--------------------------------------------------------------------------------
/src/Typely.Generators/nuget-readme.md:
--------------------------------------------------------------------------------
1 | Typely: Unleashing the power of value object creation with a fluent Api.
2 |
3 | ## Example
4 |
5 | ```csharp
6 | public class TypesSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("Votes");
11 | builder.OfString().For("Code").Length(4).NotEqual("0000");
12 |
13 | builder.OfString()
14 | .For("UserId")
15 | .WithNamespace("UserAggregate")
16 | .WithName("Owner identifier")
17 | .NotEmpty()
18 | .NotEqual("0").WithMessage("{Name} cannot be equal to {ComparisonValue}.").WithErrorCode("ERR001")
19 | .MaxLength(20);
20 |
21 | builder.OfString()
22 | .For("Monday")
23 | .AsClass()
24 | .WithName(() => LocalizedNames.Moment)
25 | .MinLength(1).WithMessage(() => LocalizedMessages.MinLengthCustom)
26 | .MaxLength(20).WithMessage(() => LocalizedMessages.MaxLengthCustom);
27 | }
28 | }
29 | ```
30 |
31 | # Documentation
32 |
33 | - https://docs.typely.net/
34 |
35 | # Prerequisites
36 |
37 | - Supported .NET versions
38 | - .NET 7.0 and greater
39 |
40 | # Getting started
41 |
42 | Install packages
43 | ```
44 | dotnet add package Typely.Core
45 | dotnet add package Typely.Generators
46 | ```
47 |
48 | Create a class inheriting from `ITypelySpecification`
49 | ```csharp
50 | public class TypesSpecification : ITypelySpecification
51 | {
52 | public void Create(ITypelyBuilder builder)
53 | {
54 | builder.OfString().For("FirstName").NotEmpty();
55 | }
56 | }
57 | ```
58 |
59 | Usage
60 | ```csharp
61 | var firstName = FirstName.From("Adam");
62 | FirstName.From(""); //Throws ValidationException
63 |
64 | if(!FirstName.TryFrom("value", out FirstName instance, out ValidationError? validationError))
65 | {
66 | // Handle error
67 | }
68 | ```
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Swashbuckle.Tests/BaseFixture.cs:
--------------------------------------------------------------------------------
1 | using AutoFixture;
2 | using AutoFixture.AutoMoq;
3 |
4 | namespace Typely.AspNetCore.Swashbuckle.Tests;
5 |
6 | public class BaseFixture
7 | {
8 | protected IFixture Fixture { get; } = new Fixture().Customize(new AutoMoqCustomization());
9 |
10 | public T Create() => Fixture.Create();
11 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Swashbuckle.Tests/SchemaFilterContextFixture.cs:
--------------------------------------------------------------------------------
1 | using AutoFixture;
2 | using Microsoft.OpenApi.Models;
3 | using Moq;
4 | using Swashbuckle.AspNetCore.SwaggerGen;
5 | using System.Reflection;
6 |
7 | namespace Typely.AspNetCore.Swashbuckle.Tests;
8 |
9 | public class SchemaFilterContextFixture : BaseFixture
10 | {
11 | public SchemaFilterContextFixture()
12 | {
13 | Fixture.Register(() => null);
14 | Fixture.Register(() => null);
15 | }
16 |
17 | public SchemaFilterContextFixture WithType()
18 | {
19 | Fixture.Register(() => typeof(T));
20 | Fixture.Freeze>()
21 | .Setup(x =>
22 | x.GenerateSchema(It.IsAny(), It.IsAny(), null, null, null))
23 | .Returns(new OpenApiSchema
24 | {
25 | Type = "string",
26 | Properties = new Dictionary
27 | {
28 | ["property1"] = new() { Type = "string" }
29 | }
30 | });
31 | return this;
32 | }
33 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Swashbuckle.Tests/SwaggerGenOptionsExtensionsTests.cs:
--------------------------------------------------------------------------------
1 | using Swashbuckle.AspNetCore.SwaggerGen;
2 |
3 | namespace Typely.AspNetCore.Swashbuckle.Tests;
4 |
5 | public class SwaggerGenOptionsExtensionsTests
6 | {
7 | [Fact]
8 | public void UseTypelyValueSchemaFilter_Should_AddTheFilter()
9 | {
10 | var options = new SwaggerGenOptions();
11 | options.UseTypelySchemaFilter();
12 | Assert.Single(options.SchemaFilterDescriptors);
13 | }
14 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Swashbuckle.Tests/Typely.AspNetCore.Swashbuckle.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | false
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 | all
18 |
19 |
20 | all
21 | runtime; build; native; contentfiles; analyzers; buildtransitive
22 |
23 |
24 | runtime; build; native; contentfiles; analyzers; buildtransitive
25 | all
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Swashbuckle.Tests/TypelySpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.AspNetCore.Swashbuckle.Tests;
5 |
6 | public class TypelySpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfByte().For("ByteTest");
11 | }
12 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Swashbuckle.Tests/TypelyValueSchemaFilterTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.OpenApi.Models;
2 |
3 | namespace Typely.AspNetCore.Swashbuckle.Tests;
4 |
5 | public class TypelyValueSchemaFilterTests
6 | {
7 | private readonly TypelyValueSchemaFilter _filter = new();
8 |
9 | [Fact]
10 | public void ApplyTheUnderlyingSchemaType_When_ImplementsITypelyValue()
11 | {
12 | var schema = new OpenApiSchema();
13 | var context = new SchemaFilterContextFixture()
14 | .WithType()
15 | .Create();
16 |
17 | _filter.Apply(schema, context);
18 |
19 | Assert.Equal("string", schema.Type);
20 | Assert.Single(schema.Properties.Values);
21 | }
22 |
23 | [Fact]
24 | public void SchemaIsNotTouched_When_TypeDoesNotImplementITypelyValue()
25 | {
26 | var schema = new OpenApiSchema();
27 | var context = new SchemaFilterContextFixture()
28 | .WithType()
29 | .Create();
30 |
31 | _filter.Apply(schema, context);
32 |
33 | Assert.Null(schema.Type);
34 | Assert.Empty(schema.Properties.Values);
35 | }
36 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Swashbuckle.Tests/Usings.cs:
--------------------------------------------------------------------------------
1 | global using Xunit;
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Tests/Http/TestServerFixture.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.AspNetCore.Http;
4 | using Microsoft.AspNetCore.TestHost;
5 | using Typely.AspNetCore.Http;
6 | using Typely.Core;
7 |
8 | namespace Typely.AspNetCore.Tests.Http;
9 |
10 | public class TestServerFixture
11 | {
12 | private IWebHostBuilder _webHostBuilder = new WebHostBuilder();
13 |
14 | public TestServerFixture WithTypelyMiddlewareAndValidationError()
15 | {
16 | _webHostBuilder = new WebHostBuilder()
17 | .Configure(app =>
18 | {
19 | app.UseTypelyValidationResult();
20 | app.Run(context =>
21 | {
22 | throw new ValidationException(
23 | new ValidationError(
24 | "ErrorCode",
25 | "ErrorMessageWithPlaceholders",
26 | "AttemptedValue",
27 | "TypeName",
28 | new Dictionary { { "Placeholder", "PlaceholderValue" } }));
29 | });
30 | });
31 |
32 | return this;
33 | }
34 |
35 | public TestServerFixture WithTypelyMiddleware()
36 | {
37 | _webHostBuilder = new WebHostBuilder()
38 | .Configure(app =>
39 | {
40 | app.UseTypelyValidationResult();
41 | app.Run(async context =>
42 | {
43 | await context.Response.WriteAsync("Hello World!");
44 | });
45 | });
46 |
47 | return this;
48 | }
49 |
50 | public TestServer Create() => new(_webHostBuilder);
51 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Tests/Http/TypelyValidationResultMiddlewareTests.Middleware_ShouldReturn_ErrorResponse.verified.txt:
--------------------------------------------------------------------------------
1 | {
2 | "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
3 | "title": "One or more validation errors occurred.",
4 | "status": 422,
5 | "templates": {
6 | "TypeName": [
7 | {
8 | "code": "ErrorCode",
9 | "message": "ErrorMessageWithPlaceholders",
10 | "attemptedValue": "AttemptedValue",
11 | "typeName": "TypeName",
12 | "placeholderValues": {
13 | "Placeholder": "PlaceholderValue"
14 | }
15 | }
16 | ]
17 | },
18 | "errors": {
19 | "TypeName": [
20 | "ErrorMessageWithPlaceholders"
21 | ]
22 | }
23 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Tests/Http/TypelyValidationResultMiddlewareTests.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Nodes;
2 |
3 | namespace Typely.AspNetCore.Tests.Http;
4 |
5 | public class TypelyValidationResultMiddlewareTests
6 | {
7 | [Fact]
8 | public async Task Middleware_ShouldReturn_ErrorResponse()
9 | {
10 | var server = new TestServerFixture().WithTypelyMiddlewareAndValidationError().Create();
11 | var response = await server.CreateClient().GetAsync("/");
12 | var jsonContent = await response.Content.ReadAsStringAsync();
13 | var formattedJson = JsonNode.Parse(jsonContent)!.ToString();
14 |
15 | await Verify(formattedJson);
16 | }
17 |
18 | [Fact]
19 | public async Task Middleware_Should_PassThrough()
20 | {
21 | var server = new TestServerFixture().WithTypelyMiddleware().Create();
22 | var response = await server.CreateClient().GetAsync("/");
23 | Assert.True(response.IsSuccessStatusCode);
24 | }
25 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Tests/Mvc/ModelBinding/ModelBinderFixture.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc.ModelBinding;
2 | using Typely.AspNetCore.Mvc.ModelBinding;
3 |
4 | namespace Typely.AspNetCore.Tests.Mvc.ModelBinding;
5 |
6 | public class ModelBinderFixture
7 | {
8 | private Type _modelType = typeof(int);
9 |
10 | public ModelBinderFixture WithModelType(Type modelType)
11 | {
12 | _modelType = modelType;
13 | return this;
14 | }
15 |
16 | public IModelBinder? Build()
17 | {
18 | var binderProviderContext = new ModelBinderProviderContextFixture()
19 | .WithModelType(_modelType)
20 | .Build();
21 |
22 | return new TypelyValueModelBinderProvider().GetBinder(binderProviderContext);
23 | }
24 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Tests/Mvc/ModelBinding/ModelBinderProviderContextFixture.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc.ModelBinding;
2 | using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
3 | using Moq;
4 |
5 | namespace Typely.AspNetCore.Tests.Mvc.ModelBinding;
6 |
7 | public class ModelBinderProviderContextFixture
8 | {
9 | private Type? _modelType;
10 |
11 | public ModelBinderProviderContextFixture WithModelType(Type modelType)
12 | {
13 | _modelType = modelType;
14 | return this;
15 | }
16 |
17 | public TestModelBinderProviderContext Build()
18 | {
19 | var metadetailsProvider = new Mock();
20 | var modelMetadataProvider = new Mock();
21 | var modelMetadata =
22 | new DefaultModelMetadataProvider(metadetailsProvider.Object)
23 | .GetMetadataForType(_modelType!);
24 | return new TestModelBinderProviderContext(modelMetadata, modelMetadataProvider.Object);
25 | }
26 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Tests/Mvc/ModelBinding/ModelBindingContextFixture.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.AspNetCore.Mvc;
3 | using Microsoft.AspNetCore.Mvc.ModelBinding;
4 | using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Microsoft.Extensions.Primitives;
7 | using Moq;
8 | using System.Globalization;
9 |
10 | namespace Typely.AspNetCore.Tests.Mvc.ModelBinding;
11 |
12 | public class ModelBindingContextFixture
13 | {
14 | private Type? _modelType;
15 | private string? _value;
16 |
17 | public ModelBindingContextFixture WithModelType(Type modelType)
18 | {
19 | _modelType = modelType;
20 | return this;
21 | }
22 |
23 | public ModelBindingContextFixture WithValue(string? value)
24 | {
25 | _value = value;
26 | return this;
27 | }
28 |
29 | public ModelBindingContext Build()
30 | {
31 | var metadetailsProvider = new Mock();
32 | var modelMetadataProvider = new DefaultModelMetadataProvider(metadetailsProvider.Object);
33 |
34 | return new DefaultModelBindingContext
35 | {
36 | ModelMetadata = modelMetadataProvider.GetMetadataForType(_modelType!),
37 | ModelName = "SampleTypelyValue",
38 | ModelState = new ModelStateDictionary(),
39 | ValueProvider = new CompositeValueProvider
40 | {
41 | new QueryStringValueProvider(BindingSource.Query, new QueryCollection(
42 | new Dictionary { { "SampleTypelyValue", new StringValues(_value) } })
43 | , CultureInfo.InvariantCulture)
44 | },
45 | ActionContext = new ActionContext
46 | {
47 | HttpContext = new DefaultHttpContext
48 | {
49 | RequestServices = new ServiceCollection().BuildServiceProvider()
50 | }
51 | }
52 | };
53 | }
54 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Tests/Mvc/ModelBinding/TestModelBinderProviderContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc.ModelBinding;
2 |
3 | namespace Typely.AspNetCore.Tests.Mvc.ModelBinding;
4 |
5 | public class TestModelBinderProviderContext : ModelBinderProviderContext
6 | {
7 | public TestModelBinderProviderContext(ModelMetadata metadata, IModelMetadataProvider metadataProvider)
8 | {
9 | Metadata = metadata;
10 | MetadataProvider = metadataProvider;
11 | }
12 |
13 | public override BindingInfo BindingInfo { get; } = new();
14 | public override ModelMetadata Metadata { get; }
15 | public override IModelMetadataProvider MetadataProvider { get; }
16 |
17 | public override IModelBinder CreateBinder(ModelMetadata metadata)
18 | {
19 | throw new NotImplementedException();
20 | }
21 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Tests/Mvc/ModelBinding/TypelySpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.AspNetCore.Tests.Mvc.ModelBinding;
5 |
6 | public class TypelySpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfString().For("Code").Length(4);
11 | builder.OfInt().For("Id").GreaterThan(0);
12 | }
13 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Tests/Mvc/ModelBinding/TypelyValueModelBinderProviderTests.cs:
--------------------------------------------------------------------------------
1 | using Typely.AspNetCore.Mvc.ModelBinding;
2 | using Typely.Core;
3 |
4 | namespace Typely.AspNetCore.Tests.Mvc.ModelBinding;
5 |
6 | public class TypelyValueModelBinderProviderTests
7 | {
8 | [Fact]
9 | public void GetBinder_ReturnsModelBinder_WhenModelTypeIsTypelyValue()
10 | {
11 | var binderProviderContext = new ModelBinderProviderContextFixture()
12 | .WithModelType(typeof(Code))
13 | .Build();
14 |
15 | var binder = new TypelyValueModelBinderProvider().GetBinder(binderProviderContext);
16 |
17 | Assert.NotNull(binder);
18 | }
19 |
20 | [Fact]
21 | public void GetBinder_ReturnsNull_WhenModelTypeIsNotTypelyValue()
22 | {
23 | var binderProviderContext = new ModelBinderProviderContextFixture()
24 | .WithModelType(typeof(string))
25 | .Build();
26 |
27 | var binder = new TypelyValueModelBinderProvider().GetBinder(binderProviderContext);
28 |
29 | Assert.Null(binder);
30 | }
31 |
32 | [Fact]
33 | public void GetBinder_ReturnsNull_WhenModelTypeIsNotTypelyValueGeneric()
34 | {
35 | var binderProviderContext = new ModelBinderProviderContextFixture()
36 | .WithModelType(typeof(ITypelyValue<,>))
37 | .Build();
38 |
39 | var binder = new TypelyValueModelBinderProvider().GetBinder(binderProviderContext);
40 |
41 | Assert.Null(binder);
42 | }
43 |
44 | [Fact]
45 | public void GetBinder_ThrowsArgumentNullException_WhenContextIsNull()
46 | {
47 | Assert.Throws(() => new TypelyValueModelBinderProvider().GetBinder(null!));
48 | }
49 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Tests/Mvc/MvcOptionsTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Typely.AspNetCore.Mvc;
3 | using Typely.AspNetCore.Mvc.ModelBinding;
4 |
5 | namespace Typely.AspNetCore.Tests.Mvc;
6 |
7 | public class MvcOptionsTests
8 | {
9 | [Fact]
10 | public void UseTypelyModelBinderProvider_Should_InsertTypelyValueModelBinderProvider()
11 | {
12 | var options = new MvcOptions();
13 | options.UseTypelyModelBinderProvider();
14 | Assert.IsType(options.ModelBinderProviders[0]);
15 | }
16 | }
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Tests/Typely.AspNetCore.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | false
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 | all
18 |
19 |
20 | all
21 | runtime; build; native; contentfiles; analyzers; buildtransitive
22 |
23 |
24 | runtime; build; native; contentfiles; analyzers; buildtransitive
25 | all
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/tests/Typely.AspNetCore.Tests/Usings.cs:
--------------------------------------------------------------------------------
1 | global using Xunit;
--------------------------------------------------------------------------------
/tests/Typely.EfCore.Tests/Common/MyDbContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Typely.EfCore.Conventions;
3 |
4 | namespace Typely.EfCore.Tests.Common;
5 |
6 | public class MyDbContext : DbContext
7 | {
8 | public DbSet Persons { get; set; } = null!;
9 |
10 | public MyDbContext(DbContextOptions options) : base(options)
11 | {
12 | }
13 |
14 | protected override void OnModelCreating(ModelBuilder modelBuilder)
15 | {
16 | var person = modelBuilder.Entity();
17 |
18 | person.Property(x => x.Id);
19 | person.Property(x => x.Email);
20 | person.Property(x => x.FirstName);
21 | }
22 |
23 | protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
24 | {
25 | configurationBuilder.Conventions.AddTypelyConventions();
26 | }
27 | }
--------------------------------------------------------------------------------
/tests/Typely.EfCore.Tests/Common/Person.cs:
--------------------------------------------------------------------------------
1 | namespace Typely.EfCore.Tests.Common;
2 |
3 | public class Person
4 | {
5 | public PersonId Id { get; set; }
6 | public required FirstName FirstName { get; set; }
7 | public Email? Email { get; set; }
8 | }
--------------------------------------------------------------------------------
/tests/Typely.EfCore.Tests/Common/TypelySpecificationOfPerson.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 | using Typely.Core;
3 | using Typely.Core.Builders;
4 |
5 | namespace Typely.EfCore.Tests.Common;
6 |
7 | public class TypelySpecificationOfPerson : ITypelySpecification
8 | {
9 | public void Create(ITypelyBuilder builder)
10 | {
11 | builder.OfInt().For("PersonId").NotEmpty();
12 | builder.OfString().For("FirstName").NotEmpty();
13 | builder.OfString().For("Email").NotEmpty().MaxLength(100)
14 | .Matches(new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"));
15 | }
16 | }
--------------------------------------------------------------------------------
/tests/Typely.EfCore.Tests/Conventions/ConventionTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Typely.EfCore.Tests.Common;
4 |
5 | namespace Typely.EfCore.Tests.Conventions;
6 |
7 | public class ConventionTests
8 | {
9 | [Fact]
10 | public void SaveChanges_ShouldWork_WithConventions()
11 | {
12 | using var serviceProvider = CreateServiceProvider();
13 | using var context = serviceProvider.GetRequiredService();
14 |
15 | var person = new Person
16 | {
17 | Id = PersonId.From(1),
18 | Email = Email.From("a@a.com"),
19 | FirstName = FirstName.From("John")
20 | };
21 | context.Persons.Add(person);
22 | context.SaveChanges();
23 |
24 | Assert.Equal(1, context.Persons.Count());
25 | }
26 |
27 | private static ServiceProvider CreateServiceProvider() => new ServiceCollection()
28 | .AddDbContext(options => options.UseInMemoryDatabase("MyDb"))
29 | .BuildServiceProvider();
30 | }
--------------------------------------------------------------------------------
/tests/Typely.EfCore.Tests/Typely.EfCore.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | false
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 | all
17 |
18 |
19 | all
20 | runtime; build; native; contentfiles; analyzers; buildtransitive
21 |
22 |
23 | runtime; build; native; contentfiles; analyzers; buildtransitive
24 | all
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/tests/Typely.EfCore.Tests/TypelyValueConverterTests.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.EfCore.Tests;
5 |
6 | public class TypelySpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfString().For("MyString");
11 | }
12 | }
13 |
14 | public class TypelyValueConverterTests
15 | {
16 | private readonly TypelyValueConverter converter = new();
17 | const string expectedString = "MyValue";
18 | readonly MyString expected = MyString.From(expectedString);
19 |
20 | [Fact]
21 | public void ConvertFromProvider() =>
22 | Assert.Equal(expected, converter.ConvertFromProvider(expectedString));
23 |
24 | [Fact]
25 | public void ConvertToProvider() =>
26 | Assert.Equal(expectedString, converter.ConvertToProvider(expected));
27 | }
--------------------------------------------------------------------------------
/tests/Typely.EfCore.Tests/Usings.cs:
--------------------------------------------------------------------------------
1 | global using Xunit;
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Analysers/TypelySpecificationAnalyserFixture.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.Diagnostics;
3 | using System.Collections.Immutable;
4 | // ReSharper disable InconsistentNaming
5 |
6 | namespace Typely.Generators.Tests.Analysers;
7 |
8 | internal static class AnalyserRunner
9 | {
10 | private const string CS5001_ProgramDoesNotContainValidEntryPointId = "CS5001";
11 | private const string CS0012_TypeIsDefinedInAnAssemblyThatIsNotReferenced = "CS0012";
12 |
13 | private static readonly string[] DisabledDiagnostics = {
14 | CS5001_ProgramDoesNotContainValidEntryPointId,
15 | CS0012_TypeIsDefinedInAnAssemblyThatIsNotReferenced
16 | };
17 |
18 | public static async Task> GetDiagnostics()
19 | where TAnalyser : DiagnosticAnalyzer, new()
20 | {
21 | var compilation =
22 | new CompilationWithAnalysersFixture()
23 | .WithSpecification()
24 | .WithAnalyser()
25 | .Create();
26 |
27 | var diagnostics = await compilation.GetAllDiagnosticsAsync();
28 | return diagnostics.Where(x => !DisabledDiagnostics.Contains(x.Id)).ToImmutableArray();
29 | }
30 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/BaseFixture.cs:
--------------------------------------------------------------------------------
1 | using AutoFixture;
2 | using AutoFixture.AutoMoq;
3 |
4 | namespace Typely.Generators.Tests;
5 |
6 | internal class BaseFixture
7 | {
8 | protected IFixture Fixture { get; }
9 |
10 | public BaseFixture()
11 | {
12 | Fixture = new Fixture().Customize(new AutoMoqCustomization());
13 | }
14 |
15 | public T Create() => Fixture.Create();
16 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Comparers/ImmutableArrayComparerTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using Typely.Generators.Comparers;
3 |
4 | namespace Typely.Generators.Tests.Comparers;
5 |
6 | public class ImmutableArrayComparerTests
7 | {
8 | [Fact]
9 | public void TwoArray_WithTheSameValues_ShouldBe_Equal()
10 | {
11 | var array1 = new[] { "Value1", "Value2" }.ToImmutableArray();
12 | var array2 = new[] { "Value1", "Value2" }.ToImmutableArray();
13 |
14 | Assert.True(ImmutableArrayComparer.Default.Equals(array1, array2));
15 | Assert.Equal(ImmutableArrayComparer.Default.GetHashCode(array1), ImmutableArrayComparer.Default.GetHashCode(array2));
16 | }
17 |
18 | [Fact]
19 | public void TwoArray_WithDiffLength_ShouldNotBe_Equal()
20 | {
21 | var array1 = new[] { "Value1", "Value2" }.ToImmutableArray();
22 | var array2 = new[] { "Value1" }.ToImmutableArray();
23 |
24 | Assert.False(ImmutableArrayComparer.Default.Equals(array1, array2));
25 | }
26 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/CompilationWithAnalysersFixture.cs:
--------------------------------------------------------------------------------
1 | using AutoFixture;
2 | using Microsoft.CodeAnalysis.Diagnostics;
3 | using System.Collections.Immutable;
4 |
5 | namespace Typely.Generators.Tests;
6 |
7 | internal class CompilationWithAnalysersFixture : CompilationFixture
8 | {
9 | private ImmutableArray _diagnosticAnalyzers;
10 |
11 | public CompilationWithAnalysersFixture()
12 | {
13 | Fixture.Register(() => CreateCompilation().WithAnalyzers(_diagnosticAnalyzers));
14 | }
15 |
16 | public CompilationWithAnalysersFixture WithAnalysers(ImmutableArray diagnosticAnalyzers)
17 | {
18 | _diagnosticAnalyzers = diagnosticAnalyzers;
19 | return this;
20 | }
21 |
22 | public CompilationWithAnalysersFixture WithAnalyser() where TAnalyser : DiagnosticAnalyzer, new()
23 | {
24 | _diagnosticAnalyzers = ImmutableArray.Create(new TAnalyser());
25 | return this;
26 | }
27 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Extensions/ClassDeclarationSyntaxExtensionsTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis.CSharp;
2 | using Microsoft.CodeAnalysis.CSharp.Syntax;
3 | using Typely.Generators.Extensions;
4 |
5 | namespace Typely.Generators.Tests.Extensions;
6 | public class ClassDeclarationSyntaxExtensionsTests
7 | {
8 | [Fact]
9 | public void HasInterface_ShoudReturn_False_WhenNotBaseList()
10 | {
11 | var syntaxTree = CSharpSyntaxTree.ParseText("public class Test {}");
12 | var classSyntax = syntaxTree.GetRoot().DescendantNodes().OfType().Single();
13 |
14 | Assert.False(classSyntax.HasInterface("ABC"));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/ModuleInitializer.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | namespace Typely.Generators.Tests;
4 |
5 | public static class ModuleInitializer
6 | {
7 | [ModuleInitializer]
8 | public static void Init()
9 | {
10 | VerifySourceGenerators.Initialize();
11 | }
12 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/TestExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using System.Collections.Immutable;
3 |
4 | namespace Typely.Generators.Tests;
5 |
6 | public static class TestExtensions
7 | {
8 | internal static void ShouldContainExactlyNDiagnosticsWithId(this ImmutableArray diagnostics, int n, string diagnosticId)
9 | {
10 | Assert.Equal(n, diagnostics.Count(x => x.Id == diagnosticId));
11 | }
12 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Parsing/EmittableRuleTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using Typely.Generators.Typely.Parsing;
3 |
4 | namespace Typely.Generators.Tests.Typely.Parsing;
5 |
6 | public class EmittableRuleTests
7 | {
8 | [Fact]
9 | public void TwoRulesWithTheSameProperties_ShouldBe_Equal()
10 | {
11 | var rule1 = new EmittableRule("string", "Name", "Name",
12 | new Dictionary { { "MaxLength", "10" } }.ToImmutableDictionary());
13 |
14 | var rule2 = new EmittableRule("string", "Name", "Name",
15 | new Dictionary { { "MaxLength", "10" } }.ToImmutableDictionary());
16 |
17 | Assert.True(rule1.Equals(rule2));
18 | Assert.Equal(rule1.GetHashCode(), rule2.GetHashCode());
19 | }
20 |
21 | [Fact]
22 | public void TwoRulesWithOneDiffObject_ShouldNotBe_Equal()
23 | {
24 | var rule1 = new EmittableRule("string", "Name", "Name",
25 | new Dictionary { { "MaxLength", "10" } }.ToImmutableDictionary());
26 |
27 | // ReSharper disable once SuspiciousTypeConversion.Global
28 | Assert.False(rule1.Equals("value"));
29 | }
30 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Parsing/EmittableTypeBuilderFactoryTests.cs:
--------------------------------------------------------------------------------
1 | using Typely.Generators.Typely.Parsing;
2 | using Typely.Generators.Typely.Parsing.TypeBuilders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Parsing;
5 |
6 | public class EmittableTypeBuilderFactoryTests
7 | {
8 | [Fact]
9 | public void UnsupportedType_Should_Throw()
10 | {
11 | var statement = new ParsedStatement(null!) { Root = "builder", };
12 | statement.Invocations.Add(new ParsedInvocation(null!, "OfUnsupportedType"));
13 |
14 | Assert.Throws(() => EmittableTypeBuilderFactory.Create("namespace", statement));
15 | }
16 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Parsing/ParenthesizedDeclarationSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Parsing;
5 |
6 | public class ParenthesizedDeclarationSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var (a, b) = (1, 2);
11 | }
12 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Parsing/ParserContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CSharp.Syntax;
3 |
4 | namespace Typely.Generators.Tests.Typely.Parsing;
5 |
6 | public record ParserContext(ClassDeclarationSyntax ClassDeclarationSyntax, SemanticModel SemanticModel);
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Parsing/TypePropertiesTests.cs:
--------------------------------------------------------------------------------
1 | using Typely.Generators.Comparers;
2 | using Typely.Generators.Typely.Parsing;
3 |
4 | namespace Typely.Generators.Tests.Typely.Parsing;
5 |
6 | public class TypePropertiesTests
7 | {
8 | [Fact]
9 | public void TwoTypePropertiesWithSameMaxLengthAreEqual()
10 | {
11 | var typeProperties1 = new TypeProperties();
12 | typeProperties1.SetMaxLength(10);
13 |
14 | var typeProperties2 = new TypeProperties();
15 | typeProperties2.SetMaxLength(10);
16 |
17 | Assert.True(DictionaryComparer.Default.Equals(typeProperties1, typeProperties2));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Snapshots/TypelyGeneratorSnapshotTests.MissingName_Should_OutputDiagnostic.verified.txt:
--------------------------------------------------------------------------------
1 | {
2 | Diagnostics: [
3 | {
4 | Id: TYPLY0002,
5 | Title: Name missing.,
6 | Severity: Error,
7 | WarningLevel: 0,
8 | Location: : (0,0)-(0,0),
9 | Description: ,
10 | HelpLink: ,
11 | MessageFormat: A type is declared without a user friendly name in the namespace '{0}'.,
12 | Message: A type is declared without a user friendly name in the namespace 'Typely.Generators.Tests.Typely.Configurations'.,
13 | Category: TypelyGenerator
14 | },
15 | {
16 | Id: TYPLY0001,
17 | Title: Type name missing.,
18 | Severity: Error,
19 | WarningLevel: 0,
20 | Location: : (0,0)-(0,0),
21 | Description: ,
22 | HelpLink: ,
23 | MessageFormat: A type is declared without a name in the namespace '{0}'.,
24 | Message: A type is declared without a name in the namespace 'Typely.Generators.Tests.Typely.Configurations'.,
25 | Category: TypelyGenerator
26 | }
27 | ]
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Snapshots/TypelyGeneratorSnapshotTests.MissingTypeName_Should_Continue.verified.txt:
--------------------------------------------------------------------------------
1 | {
2 | Diagnostics: [
3 | {
4 | Id: TYPLY0002,
5 | Title: Name missing.,
6 | Severity: Error,
7 | WarningLevel: 0,
8 | Location: : (0,0)-(0,0),
9 | Description: ,
10 | HelpLink: ,
11 | MessageFormat: A type is declared without a user friendly name in the namespace '{0}'.,
12 | Message: A type is declared without a user friendly name in the namespace 'Typely.Generators.Tests.Typely.Configurations'.,
13 | Category: TypelyGenerator
14 | },
15 | {
16 | Id: TYPLY0001,
17 | Title: Type name missing.,
18 | Severity: Error,
19 | WarningLevel: 0,
20 | Location: : (0,0)-(0,0),
21 | Description: ,
22 | HelpLink: ,
23 | MessageFormat: A type is declared without a name in the namespace '{0}'.,
24 | Message: A type is declared without a name in the namespace 'Typely.Generators.Tests.Typely.Configurations'.,
25 | Category: TypelyGenerator
26 | }
27 | ]
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/A/CustomLocalization.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | text/microsoft-resx
11 |
12 |
13 | 1.3
14 |
15 |
16 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
17 |
18 |
19 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
20 |
21 |
22 | Value
23 |
24 |
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/A/MultipleSpecificationA.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications.A;
5 |
6 | internal class MultipleSpecificationA : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("MultiConfigDiffNamespace");
11 | }
12 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/B/MultipleSpecificationB.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications.B;
5 |
6 | internal class MultipleSpecificationB : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("MultiConfigDiffNamespace");
11 | }
12 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/BoolSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class BoolSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfBool()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(false);
20 |
21 | vote.Must(x => x == true).WithMessage(() => LocalizedMessages.CustomMessage)
22 | .Must((x) => !x.Equals(10)).WithMessage(() => A.CustomLocalization.Value);
23 | }
24 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/ByteSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class ByteSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfByte()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(1);
20 |
21 | vote.Must(x => x == 122)
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(10).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(10).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(20)
26 | .LessThanOrEqualTo(20);
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/CharSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class CharSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfChar()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual('a');
20 |
21 | vote.Must(x => x == 122)
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan('a').WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo('f').WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan('b')
26 | .LessThanOrEqualTo('c');
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/DateOnlySpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class DateOnlySpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfDateOnly()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(new DateOnly(2021, 1, 1));
20 |
21 | vote.Must(x => x == new DateOnly(2022, 1, 1))
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(new DateOnly(2022, 1, 1)).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(new DateOnly(2022, 1, 1)).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(new DateOnly(2022, 1, 1))
26 | .LessThanOrEqualTo(new DateOnly(2022, 1, 1));
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/DateTimeOffsetSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class DateTimeOffsetSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfDateTimeOffset()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(new DateTimeOffset(new DateTime(2022, 1, 1)));
20 |
21 | vote.Must(x => x == new DateTimeOffset(new DateTime(2022, 1, 1)))
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(new DateTimeOffset(new DateTime(2022, 1, 1)))
24 | .WithMessage(() => LocalizedMessages.CustomMessage)
25 | .GreaterThanOrEqualTo(new DateTimeOffset(new DateTime(2022, 1, 1)))
26 | .WithMessage(() => A.CustomLocalization.Value)
27 | .LessThan(new DateTimeOffset(new DateTime(2022, 1, 1)))
28 | .LessThanOrEqualTo(new DateTimeOffset(new DateTime(2022, 1, 1)));
29 | }
30 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/DateTimeSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class DateTimeSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfDateTime()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(new DateTime(2021, 1, 1));
20 |
21 | vote.Must(x => x == new DateTime(2022, 1, 1))
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(new DateTime(2022, 1, 1)).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(new DateTime(2022, 1, 1)).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(new DateTime(2022, 1, 1))
26 | .LessThanOrEqualTo(new DateTime(2022, 1, 1));
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/DecimalSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class DecimalSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfDecimal()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(-1);
20 |
21 | vote.Must(x => x == 122)
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(10).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(10).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(20)
26 | .LessThanOrEqualTo(20);
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/Diagnostics/UnsupportedFieldsSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications.Diagnostics;
5 |
6 | public class UnsupportedFieldsSpecification: ITypelySpecification
7 | {
8 | private const string Field1 = "Field1";
9 | private string Field2 = "Field2";
10 |
11 | public void Create(ITypelyBuilder builder)
12 | {
13 | builder.OfString().For(Field1);
14 | builder.OfString().For(Field2);
15 | }
16 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/Diagnostics/UnsupportedMethodsSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications.Diagnostics;
5 |
6 | public class UnsupportedMethodsSpecification: ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfString().For("Unsupported").UnsupportedExtension();
11 |
12 | CreateTypeFromUnsupportedCall(builder);
13 |
14 | var unsupported = CreateTypeFromUnsupportedCall(builder);
15 | unsupported.NotEmpty();
16 | }
17 |
18 | public IRuleBuilderOfInt CreateTypeFromUnsupportedCall(ITypelyBuilder builder) =>
19 | builder.OfInt().For("AddressId").GreaterThan(0);
20 | }
21 |
22 | internal static class TypelyBuilderExtensions
23 | {
24 | public static void UnsupportedExtension(this ITypelyBuilderOfString builder) =>
25 | builder.MinLength(3).MaxLength(20).Normalize(x => x.ToUpper());
26 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/Diagnostics/UnsupportedPropertiesSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications.Diagnostics;
5 |
6 | public class UnsupportedPropertiesSpecification : ITypelySpecification
7 | {
8 | private string TypeName => "MyType";
9 |
10 | public void Create(ITypelyBuilder builder)
11 | {
12 | builder.OfString().For(TypeName);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/Diagnostics/UnsupportedTypesSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications.Diagnostics;
5 |
6 | public class UnsupportedTypesSpecification : ITypelySpecification
7 | {
8 | public class A
9 | {
10 | }
11 |
12 | public record B
13 | {
14 | }
15 |
16 | private struct C
17 | {
18 | }
19 |
20 | protected enum D { }
21 |
22 | public void Create(ITypelyBuilder builder)
23 | {
24 | }
25 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/Diagnostics/UnsupportedVariablesSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications.Diagnostics;
5 |
6 | public class UnsupportedVariablesSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfString().AsFactory();
11 | factory.For("BasicType");
12 |
13 | const string constVar1 = "constVar1";
14 | string var2 = "var2";
15 | int i = 0;
16 |
17 | builder.OfString().For(constVar1);
18 | builder.OfString().For(var2);
19 | builder.OfString().For(i.ToString());
20 |
21 | var number = builder.OfInt().LessThan(100);
22 | number.For("A");
23 | }
24 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/DiagnosticsSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class DiagnosticsSpecification : ITypelySpecification
7 | {
8 | private const string TypeName = "MyType";
9 |
10 | public void Create(ITypelyBuilder builder)
11 | {
12 | const string myVar1 = "MyVar1";
13 | string myVar2 = "MyVar2";
14 |
15 | builder.OfString().For(myVar1);
16 | builder.OfString().For(myVar2);
17 | builder.OfString().For(TypeName);
18 | builder.OfString().For("Unsupported").UnsupportedExtension();
19 |
20 | CreateTypeFromUnsupportedCall(builder);
21 |
22 | var unsupported = CreateTypeFromUnsupportedCall(builder);
23 | unsupported.NotEmpty();
24 |
25 | builder.OfString().For("ShouldGenerate");
26 | }
27 |
28 | public IRuleBuilderOfInt CreateTypeFromUnsupportedCall(ITypelyBuilder builder) =>
29 | builder.OfInt().For("AddressId").GreaterThan(0);
30 | }
31 |
32 | internal static class TypelyBuilderExtensions
33 | {
34 | public static ITypelyBuilderOfString UnsupportedExtension(this ITypelyBuilderOfString builder) =>
35 | builder.MinLength(3).MaxLength(20).Normalize(x => x.ToUpper());
36 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/DoubleSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class DoubleSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfDouble()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(-1);
20 |
21 | vote.Must(x => x == 122)
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(10).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(10).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(20)
26 | .LessThanOrEqualTo(20);
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/EmptySpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class EmptySpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/FloatSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class FloatSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfFloat()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(-1);
20 |
21 | vote.Must(x => x == 122)
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(10).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(10).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(20)
26 | .LessThanOrEqualTo(20);
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/GuidSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class GuidSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfGuid()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(Guid.Empty);
20 |
21 | vote.Must(x => x == Guid.Parse("bf820b37-c090-4d51-8a69-07db3d2f42ea"))
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(Guid.Parse("bf820b37-c090-4d51-8a69-07db3d2f42ea"))
24 | .WithMessage(() => LocalizedMessages.CustomMessage)
25 | .GreaterThanOrEqualTo(Guid.Parse("bf820b37-c090-4d51-8a69-07db3d2f42ea"))
26 | .WithMessage(() => A.CustomLocalization.Value)
27 | .LessThan(Guid.Parse("bf820b37-c090-4d51-8a69-07db3d2f42ea"))
28 | .LessThanOrEqualTo(Guid.Parse("bf820b37-c090-4d51-8a69-07db3d2f42ea"));
29 | }
30 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/IntSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfInt()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .Normalize(abc => abc + 1)
20 | .NotEqual(-1);
21 |
22 | vote.Must(x => x == 122)
23 | .Must((x) => !x.Equals(10))
24 | .GreaterThan(10).WithMessage(() => LocalizedMessages.CustomMessage)
25 | .GreaterThanOrEqualTo(10).WithMessage(() => A.CustomLocalization.Value)
26 | .LessThan(20)
27 | .LessThanOrEqualTo(20);
28 | }
29 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/LongSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class LongSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfLong()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(-1);
20 |
21 | vote.Must(x => x == 122)
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(10).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(10).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(20)
26 | .LessThanOrEqualTo(20);
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/NamespaceSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications
5 | {
6 | internal class NamespaceSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/NoNamespaceSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | // ReSharper disable CheckNamespace
5 |
6 | namespace Typely.Generators.Tests.Typely.Specifications;
7 |
8 | internal class NoNamespaceSpecification : ITypelySpecification
9 | {
10 | public void Create(ITypelyBuilder builder)
11 | {
12 | }
13 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/SByteSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class SByteSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfSByte()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(1);
20 |
21 | vote.Must(x => x == 122)
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(10).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(10).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(20)
26 | .LessThanOrEqualTo(20);
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/ShortSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class ShortSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfShort()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(-1);
20 |
21 | vote.Must(x => x == 122)
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(10).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(10).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(20)
26 | .LessThanOrEqualTo(20);
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/StringSpecification.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using System.Text.RegularExpressions;
3 | using Typely.Core;
4 | using Typely.Core.Builders;
5 |
6 | namespace Typely.Generators.Tests.Typely.Specifications;
7 |
8 | internal class StringSpecification : ITypelySpecification
9 | {
10 | public void Create(ITypelyBuilder builder)
11 | {
12 | builder.OfString().For("Code")
13 | .AsClass()
14 | .WithName(() => "Code")
15 | .Length(21)
16 | .Length(4, 20)
17 | .NotEqual("0000")
18 | .Matches(new Regex(".+"))
19 | .GreaterThan("A")
20 | .GreaterThanOrEqualTo("A")
21 | .LessThan("A")
22 | .MinLength(2)
23 | .LessThanOrEqualTo("A")
24 | .Normalize(x => x.Trim().ToLower());
25 |
26 | var sf = builder.OfString().AsFactory();
27 | var sf2 = sf.WithName("Username").AsFactory();
28 | sf2.For("UserId")
29 | .WithNamespace("UserAggregate")
30 | .WithName("Owner identifier")
31 | .NotEmpty()
32 | .NotEqual("0").WithMessage("{Name} cannot be equal to {ComparisonValue}.").WithErrorCode("ERR001")
33 | .MaxLength(20)
34 | .Must(x => x != "1" && x.ToLower() == "12")
35 | .Normalize((x) => CultureInfo.InvariantCulture.TextInfo.ToTitleCase(x));
36 | }
37 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/TimeOnlySpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class TimeOnlySpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfTimeOnly()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(new TimeOnly(2021, 1, 1));
20 |
21 | vote.Must(x => x == new TimeOnly(2022, 1, 1))
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(new TimeOnly(2022, 1, 1)).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(new TimeOnly(2022, 1, 1)).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(new TimeOnly(2022, 1, 1))
26 | .LessThanOrEqualTo(new TimeOnly(2022, 1, 1));
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/TimeSpanSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class TimeSpanSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfTimeSpan()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(new TimeSpan(2021, 1, 1));
20 |
21 | vote.Must(x => x == new TimeSpan(2022, 1, 1))
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(new TimeSpan(2022, 1, 1)).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(new TimeSpan(2022, 1, 1)).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(new TimeSpan(2022, 1, 1))
26 | .LessThanOrEqualTo(new TimeSpan(2022, 1, 1));
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/UIntSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class UIntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfUInt()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(1);
20 |
21 | vote.Must(x => x == 122)
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(10).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(10).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(20)
26 | .LessThanOrEqualTo(20);
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/ULongSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class ULongSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfULong()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(1);
20 |
21 | vote.Must(x => x == 122)
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(10).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(10).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(20)
26 | .LessThanOrEqualTo(20);
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/UShortSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications;
5 |
6 | internal class UShortSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | var factory = builder.OfUShort()
11 | .AsStruct()
12 | .WithNamespace("Election")
13 | .NotEmpty().WithMessage("The value cannot be empty").WithErrorCode("ERR-001")
14 | .AsFactory();
15 |
16 | var vote = factory
17 | .For("Votes")
18 | .WithName("Presidency vote")
19 | .NotEqual(1);
20 |
21 | vote.Must(x => x == 122)
22 | .Must((x) => !x.Equals(10))
23 | .GreaterThan(10).WithMessage(() => LocalizedMessages.CustomMessage)
24 | .GreaterThanOrEqualTo(10).WithMessage(() => A.CustomLocalization.Value)
25 | .LessThan(20)
26 | .LessThanOrEqualTo(20);
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/Specifications/WrappedNamespaceSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Generators.Tests.Typely.Specifications
5 | {
6 | internal class ParentClass
7 | {
8 | internal class WrappedNamespaceSpecification : ITypelySpecification
9 | {
10 | public void Create(ITypelyBuilder builder)
11 | {
12 | }
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Typely/TypelyGeneratorDriver.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CSharp;
3 | using Typely.Generators.Typely;
4 |
5 | namespace Typely.Generators.Tests.Typely;
6 |
7 | internal class TypelyGeneratorDriver
8 | {
9 | private readonly Compilation _compilation;
10 |
11 | public TypelyGeneratorDriver(Compilation compilation)
12 | {
13 | _compilation = compilation;
14 | }
15 |
16 | public GeneratorDriver Run()
17 | {
18 | // Directly create an instance of the generator
19 | // (Note: in the compiler this is loaded from an assembly, and created via reflection at runtime)
20 | var generator = new TypelyGenerator();
21 | // Create the driver that will control the generation, passing in our generator
22 | GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
23 |
24 | // Run the generation pass
25 | // (Note: the generator driver itself is immutable, and all calls return an updated version of the driver that you should use for subsequent calls)
26 | return driver.RunGenerators(_compilation);
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Typely.Generators.Tests/Usings.cs:
--------------------------------------------------------------------------------
1 | global using Xunit;
--------------------------------------------------------------------------------
/tests/Typely.Isolated.Tests/Typely.Isolated.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 | false
9 | true
10 |
11 |
12 |
13 |
14 |
15 |
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 | all
18 |
19 |
20 | runtime; build; native; contentfiles; analyzers; buildtransitive
21 | all
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/tests/Typely.Isolated.Tests/Usings.cs:
--------------------------------------------------------------------------------
1 | global using Xunit;
--------------------------------------------------------------------------------
/tests/Typely.Isolated.Tests/ValidationErrorFactory.Tests.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 |
3 | namespace Typely.Isolated.Tests;
4 |
5 | public class ValidationErrorFactoryTests
6 | {
7 | [Fact]
8 | public void EnableSensitiveDataLogging_Should_AddValueToPlaceholders()
9 | {
10 | TypelyOptions.Instance.EnableSensitiveDataLogging();
11 | var validationError = ValidationErrorFactory.Create("value123", "code", "message", "typeName", null);
12 | TypelyOptions.Instance.EnableSensitiveDataLogging(false);
13 |
14 | Assert.Equal("value123", validationError.PlaceholderValues[ValidationPlaceholders.Value]);
15 | }
16 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/Asserts.cs:
--------------------------------------------------------------------------------
1 | using CsCheck;
2 | using Typely.Core;
3 |
4 | namespace Typely.Tests;
5 |
6 | ///
7 | /// Common asserts.
8 | ///
9 | internal static class Asserts
10 | {
11 | ///
12 | /// Test many possibilities that should not return an error and one case that does.
13 | ///
14 | /// Type generated by Typely.
15 | /// Underlying type of .
16 | /// Generator of the underlying value.
17 | /// Predicate that should create a validation error.
18 | /// Value that always causes a validation error.
19 | public static void ValidationMatchPredicate(Gen gen,
20 | Func predicate, TUnderlyingValue valueThatReturnError)
21 | where TTypelyValue : ITypelyValue
22 | {
23 | gen.Sample(x => predicate(x) == (TTypelyValue.Validate(x) != null));
24 | Assert.NotNull(TTypelyValue.Validate(valueThatReturnError));
25 | }
26 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/CompleteTypelySpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests;
5 |
6 | public class CompleteTypelySpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("TypelyOptionTestsType").NotEmpty();
11 | builder.OfInt().For("ValidationErrorTestsType").NotEmpty();
12 | var factory = builder.OfString().AsFactory();
13 | factory.For("BasicType");
14 | factory.For("BasicType2");
15 |
16 | builder
17 | .OfString()
18 | .AsClass()
19 | .For("UserId")
20 | .WithNamespace("UserAggregate1")
21 | .WithName("Owner identifier")
22 | .AsStruct()
23 | .NotEmpty().WithMessage("'Name' cannot be empty.").WithErrorCode("ERR001")
24 | .NotEqual("1");
25 | }
26 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/Converters/TypelyJsonConverterTests.cs:
--------------------------------------------------------------------------------
1 | using CsCheck;
2 | using System.Text.Json;
3 |
4 | namespace Typely.Tests.Converters;
5 |
6 | public class TypelyJsonConverterTests
7 | {
8 | internal class BasicClass
9 | {
10 | public StringSerializationTestsType? Prop { get; set; }
11 | }
12 |
13 | [Fact]
14 | public void SystemTextJson_Serialize_RoundTrip() =>
15 | Gen.String.Sample(x =>
16 | {
17 | var obj = StringSerializationTestsType.From(x);
18 | var serialized = JsonSerializer.Serialize(obj);
19 | return JsonSerializer.Deserialize(serialized)!.Equals(obj);
20 | });
21 |
22 | [Fact]
23 | public void SystemTextJson_Should_DeserializeEmpty()
24 | {
25 | var obj = "{\"Prop\":null}";
26 |
27 | var actual = JsonSerializer.Deserialize(obj)!;
28 |
29 | Assert.Null(actual.Prop?.Value);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/Typely.Tests/Converters/TypelySpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.Converters;
5 |
6 | public class TypelySpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfString().For("StringSerializationTestsType");
11 | builder.OfInt().For("IntTypeConverterTestsType");
12 | }
13 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/Converters/TypelyTypeConverterTests.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core.Converters;
2 |
3 | namespace Typely.Tests.Converters;
4 |
5 | public class TypelyTypeConverterTests
6 | {
7 | [Fact]
8 | public void CanConvertFromInt_ReturnsTrue_WhenUnderlyingTypeIsInt()
9 | {
10 | var converter = new TypelyTypeConverter();
11 | Assert.True(converter.CanConvertFrom(null, typeof(int)));
12 | }
13 |
14 | [Fact]
15 | public void ConvertFromInt_ReturnsTypelyType_WhenUnderlyingTypeIsInt()
16 | {
17 | var converter = new TypelyTypeConverter();
18 | var result = converter.ConvertFrom(null, null, 1);
19 | Assert.IsType(result);
20 | }
21 |
22 | [Fact]
23 | public void ConvertFromInt_ReturnsTypelyType_WhenUnderlyingTypeIsString()
24 | {
25 | var converter = new TypelyTypeConverter();
26 | var result = converter.ConvertFrom(null, null, "1");
27 | Assert.IsType(result);
28 | }
29 |
30 | [Fact]
31 | public void CanConvertToInt_ReturnsTrue_WhenUnderlyingTypeIsInt()
32 | {
33 | var converter = new TypelyTypeConverter();
34 | Assert.True(converter.CanConvertTo(null, typeof(int)));
35 | }
36 |
37 | [Fact]
38 | public void ConvertToInt_ReturnsTrue_WhenTypeIsTypelyTypeOfInt()
39 | {
40 | var converter = new TypelyTypeConverter();
41 | var value = IntTypeConverterTestsType.From(1);
42 | var result = converter.ConvertTo(null, null, value, typeof(int));
43 | Assert.IsType(result);
44 | }
45 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/BoolType/BoolSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.BoolType;
5 |
6 | public class BoolSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfBool().For("BasicType");
11 | builder.OfBool().For("NotEmptyType").NotEmpty();
12 | builder.OfBool().For("NotEqualType").NotEqual(false);
13 | builder.OfBool().For("MustType").Must((x) => !x.Equals(10));
14 | }
15 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/BoolType/BoolTests.cs:
--------------------------------------------------------------------------------
1 | using CsCheck;
2 | using Typely.Core;
3 |
4 | namespace Typely.Tests.TypeGeneration.BoolType;
5 |
6 | public class BoolTests
7 | {
8 | [Fact] public void Equals_ShouldBe_True() => GenTrueEquals.Sample((x, y) => x.Equals(y));
9 | [Fact] public void Equals_ShouldBe_False() => GenFalseEquals.Sample((x, y) => !x.Equals(y));
10 | [Fact] public void OperatorEqual_ShouldBe_True() => GenTrueEquals.Sample((x, y) => x == y);
11 | [Fact] public void OperatorEqual_ShouldBe_False() => GenFalseEquals.Sample((x, y) => !(x == y));
12 | [Fact] public void OperatorNotEqual_ShouldBe_True() => GenTrueEquals.Sample((x, y) => !(x != y));
13 | [Fact] public void OperatorNotEqual_ShouldBe_False() => GenFalseEquals.Sample((x, y) => x != y);
14 | [Fact] public void CompareTo() => GenComparable.Sample((x) => x.primitive.CompareTo(x.randomObj.Value) == x.valueObject.CompareTo(x.randomObj));
15 | [Fact] public void CompareToObject() => GenComparable.Sample((x) => x.primitive.CompareTo((object)x.randomObj.Value) == x.valueObject.CompareTo((object)x.randomObj));
16 | [Fact] public void NotEmpty() => Assert.Throws(() => NotEmptyType.From(default));
17 | //[Fact] public void NotEqual() => Asserts.ValidationMatchPredicate(Gen.Bool, (s) => s.Equals(true), false);
18 | //[Fact] public void Must() => Asserts.ValidationMatchPredicate(Gen.Bool, (s) => s.Equals(true), false);
19 |
20 | private Gen<(BasicType, BasicType)> GenTrueEquals => Gen.Bool.Select(x => (BasicType.From(x), BasicType.From(x)));
21 | private Gen<(BasicType, BasicType)> GenFalseEquals => Gen.Bool.Select(x => (BasicType.From(x), BasicType.From(!x)));
22 | private Gen<(bool primitive, BasicType valueObject, BasicType randomObj)> GenComparable =>
23 | Gen.Select(Gen.Bool, Gen.Bool, (x, y) => (x, BasicType.From(x), BasicType.From(y)));
24 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/ByteType/ByteSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.ByteType;
5 |
6 | public class ByteSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfByte().For("BasicType");
11 | builder.OfByte().For("NotEmptyType").NotEmpty();
12 | builder.OfByte().For("NotEqualType").NotEqual(10);
13 | builder.OfByte().For("GreaterThanType").GreaterThan(10);
14 | builder.OfByte().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfByte().For("LessThanType").LessThan(10);
16 | builder.OfByte().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfByte().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/CharType/CharConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.CharType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfChar().For("BasicType");
11 | builder.OfChar().For("NotEmptyType").NotEmpty();
12 | builder.OfChar().For("NotEqualType").NotEqual('A');
13 | builder.OfChar().For("GreaterThanType").GreaterThan('B');
14 | builder.OfChar().For("GreaterThanOrEqualType").GreaterThanOrEqualTo('B');
15 | builder.OfChar().For("LessThanType").LessThan('A');
16 | builder.OfChar().For("LessThanOrEqualType").LessThanOrEqualTo('A');
17 | builder.OfChar().For("MustType").Must((x) => !x.Equals('A'));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/DateOnlyType/DateOnlyConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.DateOnlyType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/DateTimeOffsetType/DateTimeOffsetConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.DateTimeOffsetType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/DateTimeType/DateTimeConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.DateTimeType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfDateTime().For("BasicType");
11 | builder.OfDateTime().For("NotEmptyType").NotEmpty();
12 | builder.OfDateTime().For("NotEqualType").NotEqual(new DateTime(2023,04,26));
13 | builder.OfDateTime().For("GreaterThanType").GreaterThan(new DateTime(2023,04,26));
14 | builder.OfDateTime().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(new DateTime(2023,04,26));
15 | builder.OfDateTime().For("LessThanType").LessThan(new DateTime(2023,04,26));
16 | builder.OfDateTime().For("LessThanOrEqualType").LessThanOrEqualTo(new DateTime(2023,04,26));
17 | builder.OfDateTime().For("MustType").Must((x) => !x.Equals(new DateTime(2023,04,26)));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/DecimalType/DecimalConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.DecimalType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/DoubleType/DoubleConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.DoubleType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/FloatType/FloatConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.FloatType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/GuidType/GuidConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.GuidType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/IntType/IntSpecification.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.IntType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/LongType/LongConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.LongType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/SByteType/SByteConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.SByteType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/ShortType/ShortConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.ShortType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/StringType/StringSpecification.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 | using Typely.Core;
3 | using Typely.Core.Builders;
4 |
5 | namespace Typely.Tests.TypeGeneration.StringType;
6 |
7 | public class StringSpecification : ITypelySpecification
8 | {
9 | public void Create(ITypelyBuilder builder)
10 | {
11 | builder.OfString().For("BasicType");
12 | builder.OfString().For("NotEmptyType").NotEmpty();
13 | builder.OfString().For("NotEqualType").NotEqual("value");
14 | builder.OfString().For("GreaterThanType").GreaterThan("value");
15 | builder.OfString().For("GreaterThanOrEqualType").GreaterThanOrEqualTo("value");
16 | builder.OfString().For("LessThanType").LessThan("value");
17 | builder.OfString().For("LessThanOrEqualType").LessThanOrEqualTo("value");
18 | builder.OfString().For("MinLengthType").MinLength(5);
19 | builder.OfString().For("MaxLengthType").MaxLength(5);
20 | builder.OfString().For("LengthType").Length(5, 10);
21 | builder.OfString().For("MatchesType").Matches(new Regex("[0-9]{1}"));
22 | builder.OfString().For("MustType").Must((x) => !x.Equals("A"));
23 | }
24 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/TimeOnlyType/TimeOnlyConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.TimeOnlyType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/TimeSpanType/TimeSpanConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.TimeSpanType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/UIntType/UIntConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.UIntType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/ULongType/ULongConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.ULongType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypeGeneration/UShortType/UShortConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 | using Typely.Core.Builders;
3 |
4 | namespace Typely.Tests.TypeGeneration.UShortType;
5 |
6 | public class IntSpecification : ITypelySpecification
7 | {
8 | public void Create(ITypelyBuilder builder)
9 | {
10 | builder.OfInt().For("BasicType");
11 | builder.OfInt().For("NotEmptyType").NotEmpty();
12 | builder.OfInt().For("NotEqualType").NotEqual(10);
13 | builder.OfInt().For("GreaterThanType").GreaterThan(10);
14 | builder.OfInt().For("GreaterThanOrEqualType").GreaterThanOrEqualTo(10);
15 | builder.OfInt().For("LessThanType").LessThan(10);
16 | builder.OfInt().For("LessThanOrEqualType").LessThanOrEqualTo(10);
17 | builder.OfInt().For("MustType").Must((x) => !x.Equals(10));
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/Typely.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | false
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 | all
17 |
18 |
19 | all
20 | runtime; build; native; contentfiles; analyzers; buildtransitive
21 |
22 |
23 | runtime; build; native; contentfiles; analyzers; buildtransitive
24 | all
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/tests/Typely.Tests/TypelyOptionsTests.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 |
3 | namespace Typely.Tests;
4 |
5 | public class TypelyOptionsTests
6 | {
7 | [Fact]
8 | public void DisabledSensitiveDataLogging_ShouldNot_OutputCurrentValue()
9 | {
10 | TypelyOptions.Instance.EnableSensitiveDataLogging(false);
11 | var validationError = TypelyOptionTestsType.Validate(0)!;
12 |
13 | Assert.False( validationError.PlaceholderValues.ContainsKey(ValidationPlaceholders.Value));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/tests/Typely.Tests/Usings.cs:
--------------------------------------------------------------------------------
1 | global using Xunit;
--------------------------------------------------------------------------------
/tests/Typely.Tests/ValidationErrorTests.Validate_ShouldReturn_ValidationError.verified.txt:
--------------------------------------------------------------------------------
1 | {
2 | ErrorCode: NotEmpty,
3 | ErrorMessage: 'ValidationErrorTestsType' must not be empty.,
4 | ErrorMessageWithPlaceholders: '{Name}' must not be empty.,
5 | TypeName: ValidationErrorTestsType,
6 | PlaceholderValues: {
7 | Name: ValidationErrorTestsType
8 | }
9 | }
--------------------------------------------------------------------------------
/tests/Typely.Tests/ValidationErrorTests.cs:
--------------------------------------------------------------------------------
1 | using Typely.Core;
2 |
3 | namespace Typely.Tests;
4 |
5 | public class ValidationErrorTests
6 | {
7 | [Fact]
8 | public Task Validate_ShouldReturn_ValidationError() => Verify(ValidationErrorTestsType.Validate(default));
9 |
10 | [Fact]
11 | public void Exception_ShouldBe_ValidationException()
12 | {
13 | try
14 | {
15 | ValidationErrorTestsType.From(default);
16 | }
17 | catch (ValidationException ex)
18 | {
19 | Assert.NotNull(ex.ValidationError);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------