Skip to content

fix(count-codec): accept integral floats in Count response fields across SDKs - #369

Merged
benbenbang merged 3 commits into
mainfrom
fix/367-count-float
Sep 29, 2026
Merged

benbenbang merged 3 commits into
mainfrom
fix/367-count-float

Conversation

@benbenbang

Copy link
Copy Markdown
Member

Providers intermittently send count values as integral JSON floats such as 3.0 where an integer is documented, which previously failed to decode. This change makes Count decoding tolerant of integral floats across the Rust, Go, and Python SDKs while still rejecting values that are not valid counts.

  • Add a Rust count codec with deserialize and deserialize_option helpers that decode a Count from an integer or a finite integral float below 2^64, rejecting fractional, negative, non-finite, out-of-range, and non-numeric values without echoing the offending value.
  • Apply the codec to every Count response field across analyst, company, fundraising, funds, insider-trading, statement summary, and TipRanks models, keeping serde's default u64 serialization.
  • Teach gen_go to map a bare Count field to a public uint64 decoded via a new decodeCount helper, rejecting Option<Count> and Vec<Count> shapes that have no Go equivalent yet.
  • Regenerate the Go SDK models so count fields decode through decodeCount, matching the Rust codec they are generated from.
  • Document the accepted range on the Count type alias and the count codec rule in ADR 0030.
  • Add Rust, Go, and Python tests covering accepted integral floats, rejected fractional values, and the source-scan contract enforcing the decoding rules.

Closes #367

The generated Go decoders now understand a bare `Count` (u64) field
carrying the `crate::codecs::count::deserialize` path. gen_go maps it to
a public `uint64` with a raw `jsontext.Value` shadow, treats a missing or
null value as a missing member, and emits a `decodeCount` call; an
`Option<Count>` or `Vec<Count>` shape is rejected with a clear error
because it has no Go shape yet.

**Decode helper:**
* Added `decodeCount` to `sdk/go/decode.go`, which accepts a non-negative
  JSON integer or a finite integral float such as `3.0` below `2^64`, as
  the provider intermittently sends, and reports a fractional, negative,
  out-of-range, or non-numeric value as a Decode error that never echoes
  the value.

**Tests and docs:**
* Added Rust tests covering the mapping, rendering, and rejection paths
  and Go tests exercising the accepted and rejected inputs.
* Documented the count codec rule in ADR 0030.
Providers occasionally send a count as an integral JSON float such as
`3.0` where an integer is documented, which previously failed to decode.
Count response fields now accept a non-negative finite integral number
from either the integer or float path, while still rejecting fractional,
negative, non-finite, or out-of-range numbers and non-numbers.

**Rust count codec:**
* Added `crate::codecs::count` with `deserialize` and `deserialize_option`
  helpers that decode a `Count` from an integer or an integral float below
  `2^64`, with an error message that never echoes the offending value.
* Applied the codec via `deserialize_with` to every `Count` response field
  across analyst, company, fundraising, funds, insider-trading, statement
  summary, and TipRanks models; serialization stays serde's default `u64`.
* Documented the accepted range on the `Count` type alias and covered both
  the `from_str` and `from_value` decode paths with tests.

**Regenerated Go models:**
* Regenerated the Go SDK models so count shadow fields decode through
  `decodeCount`, matching the Rust codec they are generated from and
  keeping the go-sdk-tests hook satisfied in the same commit.

Refs: #367
This commit adds coverage that verifies Count members accept integral floats such as 3.0 while rejecting fractional values such as 2.9.

**Count source-scan test:**
* Added `crates/libfmp/tests/count_fields.rs` to scan the Count source and enforce the decoding contract.

**Decode tests across SDKs:**
* Added a Rust test in `analyst_stock_grades_endpoints.rs` asserting `3.0` decodes to `3` and `2.9` raises a decode error for `grades-consensus`.
* Added a Go test in `analyst_test.go` covering the same integral-float and fractional-count behaviour.
* Added Python tests in `test_analyst.py` confirming `3.0` decodes to an `int` and `2.9` maps to `FmpDecodeError`.

close #367
@benbenbang
benbenbang enabled auto-merge (rebase) September 29, 2026 02:30
@github-actions github-actions Bot added documentation Improvements or additions to documentation enhancement New feature or request labels Sep 29, 2026
@benbenbang
benbenbang merged commit 739eca8 into main Sep 29, 2026
9 checks passed
@benbenbang
benbenbang deleted the fix/367-count-float branch September 29, 2026 02:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: accept integral floats for Count members

1 participant