BREAKING CHANGES
The upgrade checklist. Each item is stated fully, with the reason it moved, in the
section it belongs to below.
- A text reply that is not entirely an integer is now an error. Code reading a
field into an integer target got the numeric prefix of whatever the field held
--1.75as1,12abcas12-- and now getsCannotParseInteger. Nothing
changes for a value that is an integer; what changes is that a value that is not
one stops being silently narrowed into one.
Added
i128andu128serialize as command arguments. A struct field of either
type could be read out of a reply but not written into a command: the argument
serializer had no 128-bit arm, so serde fell back to its default, which fails.
hseton a struct holding au128returned an error naming a type the
deserializer accepts, and the round trip a caller expects to be symmetric was
not. All three writers -- the serializer, the argument counter, and the
fast-path builder -- now format them throughitoalike every other width.
Fixed
-
A text reply read as an integer is read whole or rejected. The wire
deserializer parsed integers withatoi::atoi, which stops at the first byte
that is not a digit and returns what it read:HGETon a field holding1.75
answered1for au32target,12abcanswered12, and0x10answered0
-- a value the server never sent, indistinguishable from one it did. The crate
rejects exactly this elsewhere, deliberately:double_to_intrefuses to
truncate a RESP3 double, and theValueDeserializerparses through
str::parse, so the two deserializers disagreed on every such reply and the
path a caller took decided the answer. Integers now come fromint_from_text,
which requires every byte to be consumed and the last one to be a digit, so a
leading remainder (12), a trailing one (12abc) and a lone sign (-) are
allCannotParseInteger. An explicit+stays accepted, as RESP3 allows on an
integer reply, and overflow was already rejected. The same rule now covers the
digits behind a:, where a malformed integer frame was read as its prefix too. -
The test suite builds on Windows.
keep_alive_and_no_delay_are_applied
read the keep-alive time back withsocket2::SockRef::tcp_keepalive_time,
which Windows has no equivalent for and socket2 therefore compiles only on the
platforms that expose a getter. The wholelib testtarget failed to build
withE0599, so no test ran at all --cargo teston Windows was unusable,
and every job in CI runs onubuntu-latest, which is why nothing reported it.
The assertion is now guarded by#[cfg(not(windows))]; the value is still set
on Windows, and still asserted everywhere it can be read.
Added
- A
structmaps onto a hash, and the tests say so.hsettakes any
Serializeandhgetallreturns anyDeserialize, so a struct round-trips
through a hash in two calls -- the argument serializer flattens it into
field/value pairs, taking the field names from the struct's own, and the
deserializer reads the reply back as a map. Nothing in the suite covered that
path, which is the reason to write a hash from a struct at all, and nothing
covered the details a caller trips on either:rename/rename_alldecide the
field names on the wire, an unknown field in the hash is skipped rather than
fatal, a nested struct needs#[serde(flatten)]or a field of its own, and an
Optionfield must carryskip_serializing_if-- aNoneserializes to no
argument at all, leaving its field name paired with the next field's value.
hset_hgetall_struct_of_primitivespins the wire text a hash actually holds
(1for abool,1.75for anf32), since another client reads that text,
anda_bulk_string_reads_into_every_primitivepins the other direction: every
integer width,f32/f64includinginfand exponent notation,bool,
char,String,Option, plus the eleven values that are rejected -- out of
range, empty, or not that type -- because a hash field is a bulk string
whatever it holds, and that single wire form has to reach every target.