-
-
Notifications
You must be signed in to change notification settings - Fork 283
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
Add backwards compatible support for fmt::Write
#407
Conversation
Codecov Report
@@ Coverage Diff @@
## main #407 +/- ##
=======================================
Coverage 99.71% 99.71%
=======================================
Files 60 60
Lines 5615 5696 +81
=======================================
+ Hits 5599 5680 +81
Misses 16 16
Continue to review full report at Codecov.
|
For now `notion-generator` can't be published to crates.io because of relying on the fork of time crate Might be able to published it after time-rs/time#407
For now `notion-generator` can't be published to crates.io because of relying on the fork of time crate Might be able to published it after time-rs/time#407
Hey there. Sorry about the delay in getting to this. Would you mind rebasing? The conflict is just due to a dependency upgrade and is trivial. The other thing I immediately see is that the error branches aren't covered; could you do this? Once these two things are done I'll do an in-depth review and merge if appropriate. |
Oh, and assuming that the bytes are UTF-8 is not an assumption that should be made. It is currently possible to construct a |
No worries! Happy new year ❤️
Yeah it looks like my assumption was indeed in incorrect, I was able to observe different behaviors using this test case use time::format_description::FormatItem;
use time::macros::date;
const NON_UTF8: &[FormatItem<'_>] = &[
FormatItem::Literal(&[195, 40]),
];
fn main() {
let today = date!(2022 - 01 - 03);
let mut bytes = vec![195, 40];
dbg!(today.format_into(&mut bytes, NON_UTF8));
dbg!(bytes);
} On One possible solution is to reverse the roles and implement struct Compat<'a, W: fmt::Write> {
writer: &'a mut W,
}
impl<'a, W> io::Write for Compat<'a, W>
where
W: fmt::Write,
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let string = String::from_utf8_lossy(buf);
self.writer
.write_str(&string)
.map(|_| string.len())
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Internal fmt error"))
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
} But this can be done in userland so I am not really sure if there's any benefit to including it here. So I am not sure if there's anything here that's actionable for |
Well, it's been requested a few times so there's some benefit in just doing it once so others don't have to. It's not like it's out of scope or anything.
Wouldn't the only place that would need to be changed be My thought right now (having largely not reviewed the approach you actually took) is to create a private trait that any type implementing |
This made me realize that there was actually a path forward within the current implementation, I pushed it in the latest commits alongside a test to verify that it works (and that it indeed did fail before the changes)
I am not entirely sure how would this look, could you elaborate on it? I agree that this sounds like something that would quickly hit specialization though |
Basically it would require all internal methods like I'll take a look at the code soon™ |
Finally coming back around to this. After some more thought, I don't think that this is the best way forward. Duplicating methods has some overhead from the user's perspective, as you need to know which to call — and they're split for no apparent reason to someone new to or unfamiliar with Rust. In addition, the compatibility is ultimately a hack. While possible to avoid, doing so would lead to significant duplication as noted earlier in this thread. For these reasons (along with minor nits), I'm going to close this. I sincerely thank you for your work on this; it's not trivial to do. Your work is appreciated even though this PR is not ultimately being merged. Know that this code may still be used, in whole or in part, in the future. Looking forward, I think specialization will lead to the ideal solution here. Being able to accept either |
Sounds good! And thank you for your time ❤️ Looking forward to specialization stabilization as well |
Hello, first thank you for all the work on this lovely library! ❤️
I am submitting this PR for discussion and learning more than I am trying to get it actually merged
Implementation
This resolves #375 by adding a compatibility layer between
fmt::Write
andio::Write
. And switching the formatting machinery to usefmt::Write
.Rationale
The reason for picking
fmt::Write
as the canonical implementation is because of this quote from Rustfmt::Write
doc page:Since we only want to accept UTF-8 (Is this assumption correct?) and we don't need flushing (couldn't find any calls to
.flush()
in the codebase) it felt more correct to usefmt::Write
.Also since
fmt::Write
has more guarantees (i.e UTF-8) it's easier to implementfmt::Write
forio::Write
than the other way around.Uncertainties
format_into()
requiresio::Write
; what aboutfmt::Write
in Debug/Display impls? #375 (comment).format_into_old
but I can keep the names of internal methods matching the names of externals methods.Possible follow-ups
SafeLiteral(&str)
toFormatItem
. We should also be able to update the macros and runtime parsers to use that instead ofLiteral(&[u8])
.Write
s for foreseeable future or to deprecate the support forio::Write
to be removed in a future breaking change. (Could also deprecateLiteral
in favor ofSafeLiteral
in parallel)Tell me what do you think and if you think this is taking a completely wrong dimension please don't hesitate to close it!
Thank you ❤️