Skip to content

Add BoundedInt<TBounds> with custom-bounds witness pattern (closes #59)#79

Open
KaliCZ wants to merge 4 commits into
mainfrom
claude/custom-type-definition-Ez4YZ
Open

Add BoundedInt<TBounds> with custom-bounds witness pattern (closes #59)#79
KaliCZ wants to merge 4 commits into
mainfrom
claude/custom-type-definition-Ez4YZ

Conversation

@KaliCZ

@KaliCZ KaliCZ commented Apr 26, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #59. Adds BoundedInt<TBounds> — an int constrained to a closed [Min, Max] range carried by a witness type implementing IBounds<int>, so e.g. BoundedInt<PageSizeBounds> makes the 1..100 invariant part of the signature.

public readonly struct PageSizeBounds : IBounds<int>
{
    public static int Min => 1;
    public static int Max => 100;
}

void GetUsers(BoundedInt<PageSizeBounds> pageSize) {}

While I was here I also opened up the JSON converter factory to recognize any struct carrying [NumericWrapper], not just the four built-ins. Combined with the existing source generator, defining a brand-new validated numeric wrapper is now just an attribute and a Value / TryCreate pair — the rest (Create, equality, comparison, operators, JSON round-trip, Min / Max / Unwrap extensions) is generated. Readme has a new "Defining your own validated wrapper" section showing the recipe.

What's in the diff

  • src/StrongTypes/Numbers/IBounds.cs — the witness interface with static abstract Min/Max.
  • src/StrongTypes/Numbers/BoundedInt.cs — the struct itself. Stores an offset relative to TBounds.Min so default(BoundedInt<TBounds>) wraps Min and satisfies the invariant. The dynamic "must be between … and …" error message is achieved by passing an interpolated InvariantDescription through to the source generator, which emits it inside the partial type body where TBounds is in scope.
  • NumericStrongTypeJsonConverterFactoryCanConvert now matches by [NumericWrapper] attribute presence; underlying type is read off Value so BoundedInt<TBounds> works without a special case.
  • StrongTypesConvention (EF Core) + MissingEfCorePackageAnalyzer — both now recognize BoundedInt<>.
  • NumberExtensionsAsBounded<TBounds> / ToBounded<TBounds> on int.
  • Tests — BoundedIntTests.cs (FsCheck property tests for in/out-of-range, boundary fixed-cases, default-invariant, equality/comparison, JSON round-trip, error-message content, extensions); analyzer test theory gets a new Detects_bounded_int_wrapper case.
  • Readmes — main + EfCore.

Per the comment on #59 about the readme number-line diagram, the actual SVG isn't included — let me know if you'd like me to follow up with one.

Test plan

  • dotnet build (could not run locally — no SDK in this sandbox; CI will tell us)
  • dotnet test src/StrongTypes.TestsBoundedIntTests covers the issue's three test requirements (in-range round-trip, out-of-range rejection by both TryCreate and Create, boundary acceptance) plus generated-member branch coverage and a JSON round-trip.
  • dotnet test src/StrongTypes.Analyzers.Tests — new Detects_bounded_int_wrapper fact.
  • Manual: confirm IntelliSense for BoundedInt<PageSizeBounds>.Create(0) shows the dynamic "between 1 and 100 (inclusive)" message at runtime.

https://claude.ai/code/session_01TsE6jPbPttZztM6h2g7KqR


Generated by Claude Code

@github-actions

github-actions Bot commented Apr 26, 2026

Copy link
Copy Markdown

Coverage

Lines: 3923 / 5326 (73.7%)    Branches: 2072 / 2800 (74.0%)

Files changed in this PR

File Lines Branches
StrongTypes/Numbers/BoundedInt.cs 11 / 11 (100.0%) 4 / 4 (100.0%)
StrongTypes/Numbers/NumberExtensions.cs 14 / 14 (100.0%) 4 / 4 (100.0%)
StrongTypes/Numbers/NumericStrongTypeJsonConverterFactory.cs 39 / 39 (100.0%) 5 / 6 (83.3%)
StrongTypes.Analyzers/MissingEfCorePackageAnalyzer.cs 141 / 154 (91.6%) 53 / 62 (85.5%)
StrongTypes.Api/Controllers/BoundedIntEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
StrongTypes.Api/Data/PostgreSqlDbContext.cs 28 / 28 (100.0%) 0 / 0 (n/a)
StrongTypes.Api/Data/SqlServerDbContext.cs 28 / 28 (100.0%) 0 / 0 (n/a)
StrongTypes.Api/Entities/Entities.cs 2 / 2 (100.0%) 0 / 0 (n/a)
StrongTypes.EfCore/StrongTypesConvention.cs 47 / 61 (77.0%) 30 / 46 (65.2%)
StrongTypes.EfCore/UnwrapMethodCallTranslator.cs 31 / 31 (100.0%) 10 / 12 (83.3%)
StrongTypes.OpenApi.Core/NumericWrapperPainter.cs 20 / 20 (100.0%) 4 / 6 (66.7%)
StrongTypes.OpenApi.Core/StrongTypeSchemaTypes.cs 55 / 60 (91.7%) 39 / 48 (81.2%)
StrongTypes.OpenApi.Microsoft/Startup.cs 15 / 15 (100.0%) 0 / 0 (n/a)
StrongTypes.OpenApi.Microsoft/Numbers/BoundedIntSchemaTransformer.cs 8 / 8 (100.0%) 2 / 2 (100.0%)
StrongTypes.OpenApi.Swashbuckle/Startup.cs 13 / 13 (100.0%) 0 / 0 (n/a)
StrongTypes.OpenApi.Swashbuckle/Numbers/BoundedIntSchemaFilter.cs 8 / 8 (100.0%) 4 / 4 (100.0%)
StrongTypes.OpenApi.TestApi.Shared/BasicControllers.cs 0 / 60 (0.0%) 0 / 0 (n/a)
StrongTypes.OpenApi.TestApi.Shared/Models.cs 2 / 48 (4.2%) 0 / 0 (n/a)
StrongTypes — lines 91.5% (1568/1714), branches 86.7% (1014/1170)
Booleans — lines 100.0% (14/14), branches 84.6% (22/26)
File Lines Branches
BooleanExtensions.cs 2 / 2 (100.0%) 2 / 2 (100.0%)
BooleanMapExtensions.cs 12 / 12 (100.0%) 20 / 24 (83.3%)
Collections — lines 90.2% (276/306), branches 85.1% (114/134)
File Lines Branches
IEnumerableExtensions.cs 7 / 7 (100.0%) 6 / 6 (100.0%)
IEnumerableExtensions_Concatenating.cs 2 / 2 (100.0%) 2 / 2 (100.0%)
IEnumerableExtensions_Flattening.cs 1 / 1 (100.0%) 2 / 2 (100.0%)
IEnumerableExtensions_Null.cs 5 / 5 (100.0%) 10 / 10 (100.0%)
IEnumerableExtensions_Partition.cs 11 / 11 (100.0%) 6 / 6 (100.0%)
IEnumerableExtensions_Types.cs 0 / 8 (0.0%) 0 / 6 (0.0%)
NonEmptyEnumerable.cs 59 / 65 (90.8%) 30 / 38 (78.9%)
NonEmptyEnumerableDebugView.cs 0 / 2 (0.0%) 0 / 0 (n/a)
NonEmptyEnumerableExtensions.cs 144 / 149 (96.6%) 37 / 38 (97.4%)
NonEmptyEnumerableJsonConverter.cs 47 / 50 (94.0%) 21 / 26 (80.8%)
ReadOnlyList.cs 0 / 6 (0.0%) 0 / 0 (n/a)
Digits — lines 97.7% (85/87), branches 93.8% (30/32)
File Lines Branches
Digit.cs 78 / 80 (97.5%) 26 / 28 (92.9%)
DigitExtensions.cs 7 / 7 (100.0%) 4 / 4 (100.0%)
Emails — lines 98.6% (68/69), branches 92.5% (37/40)
File Lines Branches
Email.cs 50 / 50 (100.0%) 27 / 30 (90.0%)
EmailExtensions.cs 10 / 11 (90.9%) 8 / 8 (100.0%)
EmailJsonConverter.cs 8 / 8 (100.0%) 2 / 2 (100.0%)
Enums — lines 100.0% (58/58), branches 95.5% (21/22)
File Lines Branches
EnumExtensions.cs 58 / 58 (100.0%) 21 / 22 (95.5%)
Exceptions — lines 78.9% (15/19), branches 50.0% (7/14)
File Lines Branches
ExceptionEnumerableExtensions.cs 15 / 19 (78.9%) 7 / 14 (50.0%)
Maybe — lines 92.1% (151/164), branches 83.9% (94/112)
File Lines Branches
Maybe.cs 58 / 59 (98.3%) 40 / 44 (90.9%)
MaybeCollectionExtensions.cs 41 / 45 (91.1%) 28 / 30 (93.3%)
MaybeExtensions.cs 9 / 14 (64.3%) 10 / 18 (55.6%)
MaybeJsonConverter.cs 43 / 46 (93.5%) 16 / 20 (80.0%)
Nullables — lines 100.0% (12/12), branches 83.3% (20/24)
File Lines Branches
NullableMapExtensions.cs 12 / 12 (100.0%) 20 / 24 (83.3%)
Numbers — lines 100.0% (94/94), branches 95.5% (21/22)
File Lines Branches
BoundedInt.cs 11 / 11 (100.0%) 4 / 4 (100.0%)
Negative.cs 7 / 7 (100.0%) 2 / 2 (100.0%)
NonNegative.cs 7 / 7 (100.0%) 2 / 2 (100.0%)
NonPositive.cs 7 / 7 (100.0%) 2 / 2 (100.0%)
NumberExtensions.cs 14 / 14 (100.0%) 4 / 4 (100.0%)
NumericStrongTypeJsonConverterFactory.cs 39 / 39 (100.0%) 5 / 6 (83.3%)
NumericWrapperAttribute.cs 2 / 2 (100.0%) 0 / 0 (n/a)
Positive.cs 7 / 7 (100.0%) 2 / 2 (100.0%)
Result — lines 98.5% (333/338), branches 95.6% (417/436)
File Lines Branches
Result.cs 68 / 70 (97.1%) 55 / 68 (80.9%)
ResultAccessExtensions.cs 4 / 4 (100.0%) 8 / 8 (100.0%)
ResultAggregate.cs 188 / 188 (100.0%) 318 / 318 (100.0%)
ResultCatch.cs 20 / 20 (100.0%) 0 / 0 (n/a)
ResultFlattenExtensions.cs 7 / 7 (100.0%) 8 / 8 (100.0%)
ResultFromNullableExtensions.cs 10 / 10 (100.0%) 15 / 20 (75.0%)
ResultPartitionExtensions.cs 20 / 20 (100.0%) 5 / 6 (83.3%)
ResultThrowIfErrorExtensions.cs 16 / 19 (84.2%) 8 / 8 (100.0%)
Strings — lines 84.7% (133/157), branches 84.4% (54/64)
File Lines Branches
NonEmptyString.cs 87 / 87 (100.0%) 32 / 32 (100.0%)
NonEmptyStringExtensions.cs 13 / 27 (48.1%) 0 / 0 (n/a)
NonEmptyStringJsonConverter.cs 12 / 15 (80.0%) 4 / 6 (66.7%)
StringExtensions.cs 21 / 28 (75.0%) 18 / 26 (69.2%)
generated — lines 83.1% (329/396), branches 72.5% (177/244)
File Lines Branches
BoundedInt<T>.Extensions.g.cs 0 / 23 (0.0%) 0 / 16 (0.0%)
BoundedInt<T>.g.cs 38 / 49 (77.6%) 13 / 20 (65.0%)
Negative<T>.Extensions.g.cs 32 / 32 (100.0%) 20 / 20 (100.0%)
Negative<T>.g.cs 45 / 49 (91.8%) 24 / 32 (75.0%)
NonNegative<T>.Extensions.g.cs 32 / 32 (100.0%) 20 / 20 (100.0%)
NonNegative<T>.g.cs 36 / 49 (73.5%) 18 / 32 (56.2%)
NonPositive<T>.Extensions.g.cs 32 / 32 (100.0%) 20 / 20 (100.0%)
NonPositive<T>.g.cs 36 / 49 (73.5%) 18 / 32 (56.2%)
Positive<T>.Extensions.g.cs 32 / 32 (100.0%) 20 / 20 (100.0%)
Positive<T>.g.cs 46 / 49 (93.9%) 24 / 32 (75.0%)
StrongTypes.Analyzers — lines 94.7% (323/341), branches 86.8% (132/152)
(root) — lines 94.7% (323/341), branches 86.8% (132/152)
File Lines Branches
AddEfCorePackageCodeFixProvider.cs 48 / 52 (92.3%) 18 / 20 (90.0%)
AddOpenApiPackageCodeFixProvider.cs 52 / 53 (98.1%) 21 / 26 (80.8%)
MissingEfCorePackageAnalyzer.cs 141 / 154 (91.6%) 53 / 62 (85.5%)
MissingOpenApiPackageAnalyzer.cs 82 / 82 (100.0%) 40 / 44 (90.9%)
StrongTypes.Api — lines 96.8% (332/343), branches 86.3% (88/102)
(root) — lines 100.0% (11/11), branches n/a (0/0)
File Lines Branches
Program.cs 11 / 11 (100.0%) 0 / 0 (n/a)
Controllers — lines 96.9% (222/229), branches 86.3% (88/102)
File Lines Branches
BindingProbeController.cs 55 / 62 (88.7%) 32 / 34 (94.1%)
BoundedIntEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
CollectionJsonController.cs 4 / 4 (100.0%) 0 / 0 (n/a)
EmailEntityController.cs 44 / 44 (100.0%) 26 / 30 (86.7%)
EntityControllerBase.cs 9 / 9 (100.0%) 0 / 0 (n/a)
NegativeDecimalEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NegativeDoubleEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NegativeFloatEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NegativeIntEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NegativeLongEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NegativeShortEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonEmptyStringEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonNegativeDecimalEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonNegativeDoubleEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonNegativeFloatEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonNegativeIntEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonNegativeLongEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonNegativeShortEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonPositiveDecimalEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonPositiveDoubleEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonPositiveFloatEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonPositiveIntEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonPositiveLongEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonPositiveShortEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
PositiveDecimalEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
PositiveDoubleEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
PositiveFloatEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
PositiveIntEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
PositiveLongEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
PositiveShortEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
ReferenceTypeEntityControllerBase.cs 42 / 42 (100.0%) 14 / 18 (77.8%)
StructTypeEntityControllerBase.cs 42 / 42 (100.0%) 16 / 20 (80.0%)
Data — lines 100.0% (56/56), branches n/a (0/0)
File Lines Branches
PostgreSqlDbContext.cs 28 / 28 (100.0%) 0 / 0 (n/a)
SqlServerDbContext.cs 28 / 28 (100.0%) 0 / 0 (n/a)
Entities — lines 77.8% (14/18), branches n/a (0/0)
File Lines Branches
Entities.cs 2 / 2 (100.0%) 0 / 0 (n/a)
EntityBase.cs 12 / 12 (100.0%) 0 / 0 (n/a)
IEntity.cs 0 / 4 (0.0%) 0 / 0 (n/a)
Models — lines 100.0% (29/29), branches n/a (0/0)
File Lines Branches
CollectionJsonModels.cs 20 / 20 (100.0%) 0 / 0 (n/a)
EntityModels.cs 9 / 9 (100.0%) 0 / 0 (n/a)
StrongTypes.AspNetCore — lines 89.3% (151/169), branches 84.5% (71/84)
(root) — lines 89.3% (151/169), branches 84.5% (71/84)
File Lines Branches
JsonValidationErrorKeyNormalizer.cs 22 / 22 (100.0%) 20 / 20 (100.0%)
ModelMetadataNullability.cs 12 / 13 (92.3%) 7 / 10 (70.0%)
NonEmptyEnumerableModelBinder.cs 51 / 62 (82.3%) 25 / 30 (83.3%)
NonEmptyEnumerableModelBinderProvider.cs 9 / 9 (100.0%) 4 / 4 (100.0%)
StringElementParser.cs 16 / 19 (84.2%) 2 / 4 (50.0%)
StrongTypesAspNetCoreOptions.cs 2 / 2 (100.0%) 0 / 0 (n/a)
StrongTypesServiceCollectionExtensions.cs 39 / 42 (92.9%) 13 / 16 (81.2%)
StrongTypes.AspNetCore.TestApi — lines 90.7% (39/43), branches 92.5% (37/40)
(root) — lines 100.0% (6/6), branches n/a (0/0)
File Lines Branches
Program.cs 6 / 6 (100.0%) 0 / 0 (n/a)
Controllers — lines 89.2% (33/37), branches 92.5% (37/40)
File Lines Branches
BindingProbeController.cs 29 / 29 (100.0%) 37 / 40 (92.5%)
JsonBodyProbeController.cs 4 / 8 (50.0%) 0 / 0 (n/a)
StrongTypes.EfCore — lines 88.8% (119/134), branches 70.0% (42/60)
(root) — lines 88.8% (119/134), branches 70.0% (42/60)
File Lines Branches
MailAddressValueConverter.cs 3 / 3 (100.0%) 0 / 0 (n/a)
NonEmptyStringValueConverter.cs 3 / 3 (100.0%) 0 / 0 (n/a)
NumericStrongTypeValueConverter.cs 17 / 17 (100.0%) 0 / 0 (n/a)
StrongTypesConvention.cs 47 / 61 (77.0%) 30 / 46 (65.2%)
StrongTypesDbContextOptionsExtension.cs 18 / 19 (94.7%) 2 / 2 (100.0%)
UnwrapMethodCallTranslator.cs 31 / 31 (100.0%) 10 / 12 (83.3%)
StrongTypes.FsCheck — lines 77.4% (96/124), branches n/a (0/0)
(root) — lines 77.4% (96/124), branches n/a (0/0)
File Lines Branches
Generators.cs 96 / 124 (77.4%) 0 / 0 (n/a)
StrongTypes.OpenApi.Core — lines 90.6% (445/491), branches 82.0% (292/356)
(root) — lines 90.8% (275/303), branches 84.2% (170/202)
File Lines Branches
NumericWrapperKinds.cs 11 / 11 (100.0%) 0 / 0 (n/a)
NumericWrapperPainter.cs 20 / 20 (100.0%) 4 / 6 (66.7%)
PrimitiveSchemaMap.cs 19 / 19 (100.0%) 0 / 0 (n/a)
SchemaPaint.cs 82 / 89 (92.1%) 67 / 82 (81.7%)
StrongTypeInlineMarker.cs 6 / 6 (100.0%) 6 / 6 (100.0%)
StrongTypeSchemaTypes.cs 55 / 60 (91.7%) 39 / 48 (81.2%)
WrapperAnnotationApplier.cs 82 / 98 (83.7%) 54 / 60 (90.0%)
Inlining — lines 90.4% (170/188), branches 79.2% (122/154)
File Lines Branches
StrongTypeInliner.cs 170 / 188 (90.4%) 122 / 154 (79.2%)
StrongTypes.OpenApi.Microsoft — lines 39.3% (435/1108), branches 40.8% (174/426)
(root) — lines 95.0% (247/260), branches 75.4% (98/130)
File Lines Branches
MicrosoftSchemaNaming.cs 52 / 52 (100.0%) 8 / 8 (100.0%)
PropertyAnnotationSchemaTransformer.cs 77 / 89 (86.5%) 52 / 74 (70.3%)
Startup.cs 15 / 15 (100.0%) 0 / 0 (n/a)
StrongTypesComponentSchemaFiller.cs 103 / 104 (99.0%) 38 / 48 (79.2%)
Binding — lines 98.1% (103/105), branches 78.6% (55/70)
File Lines Branches
NonBodyStrongTypeOperationTransformer.cs 103 / 105 (98.1%) 55 / 70 (78.6%)
Collections — lines 100.0% (19/19), branches 100.0% (8/8)
File Lines Branches
NonEmptyEnumerableSchemaTransformer.cs 10 / 10 (100.0%) 2 / 2 (100.0%)
StrongTypeCollectionShapeTransformer.cs 9 / 9 (100.0%) 6 / 6 (100.0%)
Digits — lines 100.0% (11/11), branches 100.0% (2/2)
File Lines Branches
DigitSchemaTransformer.cs 11 / 11 (100.0%) 2 / 2 (100.0%)
Emails — lines 100.0% (11/11), branches 100.0% (2/2)
File Lines Branches
EmailSchemaTransformer.cs 11 / 11 (100.0%) 2 / 2 (100.0%)
Inlining — lines 100.0% (7/7), branches 50.0% (1/2)
File Lines Branches
StrongTypeInliningDocumentTransformer.cs 7 / 7 (100.0%) 1 / 2 (50.0%)
Maybe — lines 100.0% (12/12), branches 100.0% (2/2)
File Lines Branches
MaybeSchemaTransformer.cs 12 / 12 (100.0%) 2 / 2 (100.0%)
Numbers — lines 100.0% (16/16), branches 100.0% (4/4)
File Lines Branches
BoundedIntSchemaTransformer.cs 8 / 8 (100.0%) 2 / 2 (100.0%)
NumericStrongTypeSchemaTransformer.cs 8 / 8 (100.0%) 2 / 2 (100.0%)
Strings — lines 100.0% (9/9), branches 100.0% (2/2)
File Lines Branches
NonEmptyStringSchemaTransformer.cs 9 / 9 (100.0%) 2 / 2 (100.0%)
obj/Debug/net10.0/Microsoft.AspNetCore.OpenApi.SourceGenerators/Microsoft.AspNetCore.OpenApi.SourceGenerators.XmlCommentGenerator — lines 0.0% (0/658), branches 0.0% (0/204)
File Lines Branches
OpenApiXmlCommentSupport.generated.cs 0 / 658 (0.0%) 0 / 204 (0.0%)
StrongTypes.OpenApi.Swashbuckle — lines 94.1% (239/254), branches 76.7% (155/202)
(root) — lines 98.6% (68/69), branches 90.3% (56/62)
File Lines Branches
PropertyAnnotationSchemaFilter.cs 55 / 56 (98.2%) 56 / 62 (90.3%)
Startup.cs 13 / 13 (100.0%) 0 / 0 (n/a)
Binding — lines 88.3% (106/120), branches 64.3% (72/112)
File Lines Branches
NonBodyStrongTypeOperationFilter.cs 106 / 120 (88.3%) 72 / 112 (64.3%)
Collections — lines 100.0% (6/6), branches 100.0% (4/4)
File Lines Branches
NonEmptyEnumerableSchemaFilter.cs 6 / 6 (100.0%) 4 / 4 (100.0%)
Digits — lines 100.0% (10/10), branches 100.0% (4/4)
File Lines Branches
DigitSchemaFilter.cs 10 / 10 (100.0%) 4 / 4 (100.0%)
Emails — lines 100.0% (10/10), branches 100.0% (4/4)
File Lines Branches
EmailSchemaFilter.cs 10 / 10 (100.0%) 4 / 4 (100.0%)
Inlining — lines 100.0% (2/2), branches n/a (0/0)
File Lines Branches
StrongTypeInliningDocumentFilter.cs 2 / 2 (100.0%) 0 / 0 (n/a)
Maybe — lines 100.0% (13/13), branches 75.0% (3/4)
File Lines Branches
MaybeSchemaFilter.cs 13 / 13 (100.0%) 3 / 4 (75.0%)
Numbers — lines 100.0% (16/16), branches 100.0% (8/8)
File Lines Branches
BoundedIntSchemaFilter.cs 8 / 8 (100.0%) 4 / 4 (100.0%)
NumericStrongTypeSchemaFilter.cs 8 / 8 (100.0%) 4 / 4 (100.0%)
Strings — lines 100.0% (8/8), branches 100.0% (4/4)
File Lines Branches
NonEmptyStringSchemaFilter.cs 8 / 8 (100.0%) 4 / 4 (100.0%)
StrongTypes.OpenApi.TestApi.Microsoft — lines 41.0% (163/398), branches 31.6% (65/206)
(root) — lines 100.0% (14/14), branches 100.0% (2/2)
File Lines Branches
Program.cs 14 / 14 (100.0%) 2 / 2 (100.0%)
obj/Debug/net10.0/Microsoft.AspNetCore.OpenApi.SourceGenerators/Microsoft.AspNetCore.OpenApi.SourceGenerators.XmlCommentGenerator — lines 38.8% (149/384), branches 30.9% (63/204)
File Lines Branches
OpenApiXmlCommentSupport.generated.cs 149 / 384 (38.8%) 63 / 204 (30.9%)
StrongTypes.OpenApi.TestApi.Shared — lines 1.0% (2/196), branches n/a (0/0)
(root) — lines 1.0% (2/196), branches n/a (0/0)
File Lines Branches
AnnotatedRequests.cs 0 / 84 (0.0%) 0 / 0 (n/a)
BasicControllers.cs 0 / 60 (0.0%) 0 / 0 (n/a)
CustomAnnotationsControllers.cs 0 / 4 (0.0%) 0 / 0 (n/a)
Models.cs 2 / 48 (4.2%) 0 / 0 (n/a)
StrongTypes.OpenApi.TestApi.Swashbuckle — lines 100.0% (11/11), branches 100.0% (2/2)
(root) — lines 100.0% (11/11), branches 100.0% (2/2)
File Lines Branches
Program.cs 11 / 11 (100.0%) 2 / 2 (100.0%)

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 89.28571% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/StrongTypes.EfCore/StrongTypesConvention.cs 60.00% 2 Missing ⚠️
...s/Numbers/NumericStrongTypeJsonConverterFactory.cs 85.71% 0 Missing and 1 partial ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
@@            Coverage Diff             @@
##             main      #79      +/-   ##
==========================================
+ Coverage   89.07%   89.10%   +0.02%     
==========================================
  Files          85       86       +1     
  Lines        1877     1890      +13     
  Branches      408      409       +1     
==========================================
+ Hits         1672     1684      +12     
- Misses        134      135       +1     
  Partials       71       71              
Components Coverage Δ
StrongTypes 89.77% <95.00%> (+0.08%) ⬆️
StrongTypes.Analyzers 86.89% <100.00%> (+0.06%) ⬆️
StrongTypes.EfCore 81.74% <60.00%> (-0.52%) ⬇️
StrongTypes.Api 97.34% <ø> (ø)
StrongTypes.FsCheck 77.27% <ø> (ø)
Files with missing lines Coverage Δ
...ongTypes.Analyzers/MissingEfCorePackageAnalyzer.cs 86.36% <100.00%> (+0.08%) ⬆️
src/StrongTypes/Numbers/BoundedInt.cs 100.00% <100.00%> (ø)
src/StrongTypes/Numbers/NumberExtensions.cs 100.00% <100.00%> (ø)
...s/Numbers/NumericStrongTypeJsonConverterFactory.cs 97.05% <85.71%> (-0.24%) ⬇️
src/StrongTypes.EfCore/StrongTypesConvention.cs 65.51% <60.00%> (-0.56%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

claude and others added 3 commits June 24, 2026 09:39
Introduces an int constrained to [TBounds.Min, TBounds.Max] via a small
witness type implementing IBounds<int>. Bounds travel with the type, so
e.g. `BoundedInt<PageSizeBounds>` makes the 1..100 invariant part of
the signature.

Also opens up the JSON converter factory to any struct marked with
[NumericWrapper], so users can spin up their own validated numeric
wrappers with just the attribute and a `Value` / `TryCreate` pair —
the source generator and JSON converter take care of the rest.

EF Core convention and the missing-EfCore-package analyzer now also
recognize BoundedInt<>.
When a [NumericWrapper] type uses a concrete value type (e.g. int) as its
underlying type, IEquatable<T>.Equals(T) and IComparable<T>.CompareTo(T)
require the unannotated parameter type. Emitting Equals(int? other) /
CompareTo(int? other) is Nullable<int>, which doesn't satisfy the interface
contracts. For generic-parameter underlyings (e.g. T : INumber<T>), T?
is a nullable annotation only, so the existing form remains correct.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Picks up the parts of the BoundedInt feature that didn't exist when the
original PR was opened (the OpenApi.*, Api.IntegrationTests, and AspNetCore
projects all landed on main since), and fills the EF Core gap.

- OpenAPI: BoundedInt renders as `{ type: integer, format: int32, minimum,
  maximum }` on both the Microsoft and Swashbuckle pipelines. The witness
  bounds are read off the closed type's static Min/Max at schema time (the
  range can't be derived from the generic definition alone). Schema tests
  cover value, nullable-value, and a dedicated nullables endpoint across all
  three document variants.
- Api integration: BoundedIntEntity round-trips through the request pipeline
  and EF Core (both providers) via the shared EntityTests suite, which also
  pins the 400 ValidationProblemDetails shape and the `$.value` /
  `$.nullableValue` error keys.
- EF Core: BoundedIntExtensions added to the Unwrap method-call translator so
  `.Unwrap()` translates to a bare int column, matching the convention that
  already recognizes BoundedInt<>.
- Unit tests: full-range witness (int.MinValue..int.MaxValue) pins the
  offset-storage modular round-trip; the error-message test now asserts the
  witness bounds are interpolated, not left as a literal placeholder.
- Docs: Skill numeric/openapi references and the OpenApi package readmes now
  cover BoundedInt and the custom-wrapper recipe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@KaliCZ KaliCZ force-pushed the claude/custom-type-definition-Ez4YZ branch from b0bd53c to 98a8b86 Compare June 24, 2026 08:07
WPF: BoundedInt<TBounds> is a scalar IParsable value, so it belongs in the
WPF TypeConverter wiring alongside the sign wrappers. Adds it to the type
description provider, a PageSize property + PageSizeBounds witness on the
test view-model, and a BoundedIntBindingTests class (OneWay display, TwoWay
valid, out-of-range and non-numeric validation errors). WPF readme + Skill
WPF reference updated.

Docs: the original BoundedInt PR missed WPF, the EF Core Unwrap translator,
and OpenAPI because the guidance only covered *test* projects, never the
production wiring. Adds an "Adding a new strong type — integration checklist"
to CLAUDE.md enumerating every package a type must be wired through (core,
EF Core convention + Unwrap translator, both OpenAPI pipelines, WPF,
analyzers), with pointers to testing.md and CONTRIBUTING for tests and docs.
testing.md gains the missing WPF row and a WPF binding-test section;
CONTRIBUTING points at the checklist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add BoundedRange numeric type (e.g. for pagination 1..100)

3 participants