A generated DTO's Default disagrees with the boolean a client's omission now gives #232
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
#226 gave 65 generated booleans the value upstream's constructor starts them at when a client omits them.
Defaultwas left alone, so the two now disagree.The disagreement
T::default()still yieldsfalsefor every one of those fields, whileserde_json::from_str::<T>("{}")yieldstrue. Any..Default::default()construction therefore servesfalsewhere upstream servestrue.Nothing is broken today:
editing.rs::shown()sets the sixLibraryOptionsflags by hand, andconfiguration.rs::described()sets theServerConfigurationones. The trap is the next boolean upstream adds to a constructor: the generator picks it up on the wire side and silently not on the construct side.The shape of the fix
Have
gen_dtoemit animpl Defaultbeside a struct whenever any of its fields usescrate::wire::yes, so the two cannot drift. The check that belongs with it —T::default() == from_str::<T>("{}")for every generated type — fails for all twenty schemas until that impl exists, so it has to come second.Two latent limits in the initialiser reader
xtask/src/initializers.rs, neither of which bites at the pin:constructor_bodytakes the firstpublic {Name}(…).ProfileConditionhas three, and the parameterless one happens to come first. A class declaring it second would be read from the wrong body. Its[^)]*also breaks on a parameter containing).X = true;inside anifas an unconditional default. Every constructor it reads at the pin is flat.Found reviewing #226.
Fixed at the generator, and landed on
mainatec3a4ee8.gen_dtoemits animpl Defaultbeside any struct carrying a field that starts attrue, so a type's own default and the value a client's omission gives are one and the same — twenty structs, sixty-eight booleans. It also emits a test assertingT::default() == from_str("{}")for all 259 generated structs, which fails on a single mutated field and names it.The two limits in the reader were measured rather than assumed: only
ProfileConditionandTranscodingProfilehave overloaded constructors and both declare the parameterless one first, and no owning class assigns a boolean inside a branch. Both are fixed anyway — the constructor is now matched explicitly and only assignments at brace depth zero count — and the generated output is unchanged by either.The corrected default exposed a real bug. Upstream's
CreatePlaylistDto.IsPublicstartstrue, but the controller forwards it as a nullable and the manager settlesOpenAccess = request.Public ?? false. So a playlist made with no body is private, and one whose body omits the flag is public. Ours collapsed both. All three cases are pinned by tests now, including the explicitfalsethat is the leak direction.Two more call sites moved to upstream's own value rather than
false: threeServerConfigurationflags, andEnableAudioVbrEncodingon a transcoding profile, which reaches the streaming URL.