Skip to content

feat(quantities): IPhysicalQuantity surface + typed In() (closes #59)#80

Merged
matt-edmondson merged 2 commits into
vectorsfrom
claude/review-vectors-branch-pgIZG
May 13, 2026
Merged

feat(quantities): IPhysicalQuantity surface + typed In() (closes #59)#80
matt-edmondson merged 2 commits into
vectorsfrom
claude/review-vectors-branch-pgIZG

Conversation

@matt-edmondson
Copy link
Copy Markdown
Contributor

Summary

This PR contains the actual #59 implementation plus a small cleanup. The earlier PR #79 merged with only the docs commit; the implementation work was pushed to the same branch after merge and never went through CI on its own.

Closes #59 — canonical IPhysicalQuantity surface + typed In()

Implement the IPhysicalQuantity<T> contract decided in #59 with compile-time dimension safety on unit conversion.

Runtime:

  • IPhysicalQuantity<T> now exposes Dimension (DimensionInfo), IComparable<IPhysicalQuantity<T>>, IEquatable<IPhysicalQuantity<T>>. CompareTo throws on cross-dimension comparison; Equals returns false (equality is total).
  • IUnit gains Name/Symbol/Dimension/ToBaseFactor/ToBaseOffset plus default-implemented ToBase<T> / FromBase<T> over the affine conversion base = value × factor + offset.
  • New UnitConversionException type (reserved for any future untyped path).

Generator:

  • DimensionsGenerator emits a marker interface per dimension (I{Dim}Unit : IUnit).
  • UnitsGenerator combines units.json with dimensions.json so each unit implements the right I{Dim}Unit marker(s). Emits class records with the full property surface, folds metric magnitude + conversionFactor into a single ToBaseFactor, and emits a static Units catalogue of singletons.
  • QuantitiesGenerator emits Dimension override + typed In(I{Dim}Unit) on every V0/V1 base + V0/V1 overload type.
length.In(Units.Kilometer);   // ✓
length.In(Units.Kilogram);    // ❌ compile error — IMassUnit, not ILengthUnit

Cleanup commit

CI flagged 9 IDE0370 "Suppression is unnecessary" findings on the ! null-forgiving operator on return expressions in SemanticQuantity.cs and a reflection call site in SemanticString.cs. Create<TResult>() is non-nullable, so the suppression was noise. Also adds if-no-files-found: ignore to the Upload Coverage Report step so runs that don't produce coverage don't surface a workflow warning.

Out of scope (follow-up)

  • V2/V3/V4 per-component In() (vector types don't implement IPhysicalQuantity).
  • Fahrenheit-style scaled-plus-offset conversions (existing offset metadata is incorrect for these).

Heads-up — landed without local build verification

The sandbox build is blocked by an analyzer mismatch unrelated to this change (existing committed files fail IDE0055 on net8 with the current SDK). Generator changes shipped blind. CI is the source of truth on this one.

Test plan

  • CI compiles Semantics.Quantities for all four TFMs (net7/8/9/10).
  • CI runs PhysicalQuantityCoreTests (Length+Kilometer = 0.01, Temperature+Celsius = 26.85, CompareTo / Equals cross-dim behaviour).
  • Removing the ! suppressions doesn't introduce CS86xx nullable warnings.
  • Diff the regenerated Semantics.Quantities/Generated/ and verify the new Dimension + In(...) members appear on every V0/V1 type.

https://claude.ai/code/session_01LqtywMUn5GwFATD5FDdLn6


Generated by Claude Code

claude added 2 commits May 13, 2026 03:50
…loses #59)

Implement the IPhysicalQuantity<T> contract decided in #59 with compile-time
dimension safety on unit conversion.

Runtime:
- IPhysicalQuantity<T> now exposes Dimension (DimensionInfo), IComparable, and
  IEquatable; CompareTo throws on cross-dimension comparison, Equals returns
  false (equality is total).
- IUnit gains Name/Symbol/Dimension/ToBaseFactor/ToBaseOffset plus default
  ToBase<T>/FromBase<T> methods over the affine conversion.
- New UnitConversionException type (reserved for any future untyped path).

Generator:
- DimensionsGenerator emits a marker interface per dimension (I{Dim}Unit : IUnit)
  so units can be dimensionally typed at compile time.
- UnitsGenerator now combines units.json with dimensions.json so each unit
  implements the correct I{Dim}Unit marker(s); emits class records (not structs)
  with full property surface (folding metric magnitude + conversionFactor into
  ToBaseFactor) plus a static Units catalogue of singletons.
- QuantitiesGenerator emits Dimension override + typed In(I{Dim}Unit) on every
  V0/V1 base and overload type. Cross-dimension In() calls fail to compile.

Tests + docs:
- PhysicalQuantityCoreTests rewritten against the new compile-time API.
- docs/architecture.md documents the IPhysicalQuantity contract and the typed
  In(I{Dim}Unit) pattern.

The previously aspirational test's runtime "should throw UnitConversionException"
case is now expressed at compile time (length.In(Units.Kilogram) doesn't build).

V2/V3/V4 vector types don't implement IPhysicalQuantity and are out of scope
for In() — per-component conversion is left as follow-up work.

https://claude.ai/code/session_01LqtywMUn5GwFATD5FDdLn6
…arning

CI flagged 9 IDE0370 "Suppression is unnecessary" findings on the null-forgiving
operator (`!`) applied to return expressions in SemanticQuantity.cs and a
reflection call site in SemanticString.cs. Create<TResult>() is non-nullable
already, so the suppression is noise.

Also adds `if-no-files-found: ignore` to the Upload Coverage Report step so
runs that don't produce coverage don't surface a workflow warning.

https://claude.ai/code/session_01LqtywMUn5GwFATD5FDdLn6
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@matt-edmondson matt-edmondson merged commit d0d9012 into vectors May 13, 2026
4 of 5 checks passed
@matt-edmondson matt-edmondson deleted the claude/review-vectors-branch-pgIZG branch May 13, 2026 12:30
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.

2 participants