-
Notifications
You must be signed in to change notification settings - Fork 606
Allow serialization to implementers of fmt::Write #1280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Since ser::Serializer writes to an implementer of io::Write, the fact that the Serializer is producing valid utf-8 is lost to the type system. This prevents users from using instances of fmt::Write with ser::Serializer. This commit adds a ser::Write trait which is automatically implemented by io::Write instances, and which can be implemented by fmt::Write instances using the FmtWrite wrapper. A large number of mechanical changes are required in order to get ser::Serializer and ser::Formatter to use ser::Write rather than io::Write. For ease of review, this commit only contains the new traits or functions that needed refactoring. The next commit contains the mechanical changes.
See parent commit for more information. This commit switches out io::Write for ser::Write, write_all for write_string, and removes casts from strings to byte slices.
Most of the code is shared between io::Write and fmt::Write, so the existing tests will ensure that fmt::Write implementers work correctly. However, escape codes are handled differently, so this commit adds a test to ensure that io::Write and fmt::Write implementers see the same results.
io::Error::other is only supported in 1.74 and above, which is higher than our MSRV.
Evidently old versions of io::Error only accept static strings as context for the error.
Some PR checks are not passing. The complaints seems to be that I think these are due to a spurrious bug in stable/beta editions of rustc, because both the nightly and older rust versions pass the CI tests. If you have any idea about what is going on though, I'd appreciate it. |
This silences rustc version 1.89 specifically from complaining that these are not constructable/used. This issue is caused by the ser module only being public if the "std" feature is turned on.
Figured out the CI issues, which were due to the Since this PR enables |
I found out that there is a very similar |
@dilr I came across this PR looking into solutions to #1281. Given your recent comment
if I understand correctly, this PR would largely solve that issue as well. , I've linked this PR to #1281. If possible, consider that issue as well while you work through this PR. You also might want to consider this comment #1122 (comment). |
I'll make sure the updated version exposes |
Hey @dilr , just following up on this. Happy to help if I can. |
The primary issue I ran into is that the I could create a new trait (say I was hoping that I could come up with something more clever, but so far I am drawing a blank. If you have any suggestions, I am all ears. If not, I can try finishing up my prototype of the declarative macro idea this weekend. I think it is the better of the two ideas, but still likely to be rejected because it is a major change and this is my first PR to this project. Perhaps it could at least spark some discussion about how this could be accomplished in the future though. |
Thank you for the update. I can take a closer look soon, but to avoid breaking changes can we create an identical trait to This doesn't solve the code duplication but if the change isn't breaking, maintainer might be more open to accepting the PR. |
This PR creates a
ser::Write
trait which is used byser::Serializer
rather thanio::Write
. Theser::Write
trait has a blanket implementation forio::Write
implementers, and can be implemented through theFmtWrite
transparent wrapper forfmt::Write
implementers. This has been done in an API compatible manner (machine checked bycargo-semver-checks
, though this is not conclusive)Structure
Most of the code is just mechanically changing out which
Write
implementation is being used, and making sure the input to theWrite
implementation is a string rather than a byte slice. Thus, for ease of review, this is split into 3 commits.The first commit contains the new
ser::Write
andFmtWrite
implementations and all of the functions which had some non-mechanical refactoring involved. This commit is small and is easiest to review first. The second commit contains the mechanical refactoring. The third commit contains a new test that confirms that theio::Write
andfmt::Write
implementations produce the same output for escaped characters.Of Note
This PR does introduce one behavior change to existing code. If
PrettyFormatter::with_indent
is fed a byte slice that is not valid utf-8, then it will revert to using two spaces as an indent instead of using the non utf-8 string as an indent.Motivation
I had wanted to clean up the unsafe code in
to_string
andto_string_pretty
, because the safety comment only holds if all other serialization code in this module actually produces utf-8. (which is a safety promise that relies on a large amount of code) In attempting to make the change, I discovered thatser::Serializer
operating on anio::Write
implementation causes the fact that the JSON being serialized to utf-8 to be lost to the type system.This could be an issue for users of the library as well. For instance, right now there is no way to choose the level of indentation when serializing directly to a
String
. (to_string_pretty
does not expose anindent
parameter) Similarly, a user might want to create JSON in an encoding other than utf-8 without serializing to a utf-8String
buffer first. This would be quickest with afmt::Write
implementation that translated the utf-8 from serde_json directly to the target encoding as serde_json is producing it.Note that despite this motivation, this PR does not touch
to_string
andto_string_pretty
, as this PR is already large enough on its own.Regarding API design
I exposed new public API surface in this PR because I think it would be useful to users of the library for reasons stated in the previous section. That being said, I'm happy to change this surface. Possible changes include; only implementing
ser::Write
onString
rather than having aFmtWrite
wrapper, or hiding the existence of theser::Write
trait from users, and only using it internally.