-
Notifications
You must be signed in to change notification settings - Fork 229
Add writeable::adapters::Concatenate and writeable::concatenate! #6929
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: main
Are you sure you want to change the base?
Changes from all commits
b9e074e
1634bbb
6b7b485
1087867
5feaebb
274aad9
26b4533
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| // This file is part of ICU4X. For terms of use, please see the file | ||
| // called LICENSE at the top level of the ICU4X source tree | ||
| // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). | ||
|
|
||
| use crate::TryWriteable; | ||
| use crate::Writeable; | ||
| use core::fmt; | ||
|
|
||
| /// A [`Writeable`] that efficiently concatenates two other [`Writeable`]s. | ||
| /// | ||
| /// See the [`concatenate!`] macro for a convenient way to make one of these. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use writeable::adapters::Concatenate; | ||
| /// use writeable::assert_writeable_eq; | ||
| /// | ||
| /// assert_writeable_eq!(Concatenate("Number: ", 25), "Number: 25"); | ||
| /// ``` | ||
| /// | ||
| /// With [`TryWriteable`]: | ||
| /// | ||
| /// ``` | ||
| /// use writeable::adapters::Concatenate; | ||
| /// use writeable::TryWriteable; | ||
| /// use writeable::assert_try_writeable_eq; | ||
| /// | ||
| /// struct AlwaysPanic; | ||
| /// | ||
| /// impl TryWriteable for AlwaysPanic { | ||
| /// type Error = &'static str; | ||
| /// fn try_write_to_parts<W: writeable::PartsWrite + ?Sized>(&self, _sink: &mut W) -> Result<Result<(), Self::Error>, core::fmt::Error> { | ||
| /// // Unreachable panic: the first Writeable errors, | ||
| /// // so the second Writeable is not evaluated. | ||
| /// panic!() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's weird to make the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is demonstrating that this code is unreachable for test; no one should write a TryWriteable like this. But since it is in the docs of the writeable crate, I see your point that people may copy and paste this and think it is what they are supposed to do. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe use |
||
| /// } | ||
| /// } | ||
| /// | ||
| /// let writeable0: Result<&str, &str> = Err("error message"); | ||
| /// let writeable1 = AlwaysPanic; | ||
| /// | ||
| /// assert_try_writeable_eq!( | ||
| /// Concatenate(writeable0, writeable1), | ||
| /// "error message", | ||
| /// Err("error message"), | ||
| /// ) | ||
| /// ``` | ||
| #[derive(Debug)] | ||
| #[allow(clippy::exhaustive_structs)] // designed for nesting | ||
| pub struct Concatenate<A, B>(pub A, pub B); | ||
|
|
||
| impl<A, B> Writeable for Concatenate<A, B> | ||
| where | ||
| A: Writeable, | ||
| B: Writeable, | ||
| { | ||
| #[inline] | ||
| fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { | ||
| self.0.write_to(sink)?; | ||
| self.1.write_to(sink) | ||
| } | ||
| #[inline] | ||
| fn write_to_parts<S: crate::PartsWrite + ?Sized>(&self, sink: &mut S) -> fmt::Result { | ||
| self.0.write_to_parts(sink)?; | ||
| self.1.write_to_parts(sink) | ||
| } | ||
| #[inline] | ||
| fn writeable_length_hint(&self) -> crate::LengthHint { | ||
| self.0.writeable_length_hint() + self.1.writeable_length_hint() | ||
| } | ||
| } | ||
|
|
||
| impl<A, B, E> TryWriteable for Concatenate<A, B> | ||
| where | ||
| A: TryWriteable<Error = E>, | ||
| B: TryWriteable<Error = E>, | ||
| { | ||
| type Error = E; | ||
| #[inline] | ||
| fn try_write_to<W: fmt::Write + ?Sized>( | ||
| &self, | ||
| sink: &mut W, | ||
| ) -> Result<Result<(), Self::Error>, fmt::Error> { | ||
| if let Err(e) = self.0.try_write_to(sink)? { | ||
| return Ok(Err(e)); | ||
| } | ||
| self.1.try_write_to(sink) | ||
| } | ||
| #[inline] | ||
| fn try_write_to_parts<S: crate::PartsWrite + ?Sized>( | ||
| &self, | ||
| sink: &mut S, | ||
| ) -> Result<Result<(), Self::Error>, fmt::Error> { | ||
| if let Err(e) = self.0.try_write_to_parts(sink)? { | ||
| return Ok(Err(e)); | ||
| } | ||
| self.1.try_write_to_parts(sink) | ||
| } | ||
| #[inline] | ||
| fn writeable_length_hint(&self) -> crate::LengthHint { | ||
| self.0.writeable_length_hint() + self.1.writeable_length_hint() | ||
| } | ||
| } | ||
|
|
||
| impl<A, B> fmt::Display for Concatenate<A, B> | ||
| where | ||
| A: Writeable, | ||
| B: Writeable, | ||
| { | ||
| #[inline] | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| self.0.write_to(f)?; | ||
| self.1.write_to(f) | ||
| } | ||
| } | ||
|
|
||
| /// Returns a [`Writeable`] concatenating any number of [`Writeable`]s. | ||
sffc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// | ||
| /// The macro resolves to a nested [`Concatenate`]. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use writeable::assert_writeable_eq; | ||
| /// | ||
| /// let concatenated = writeable::concatenate!( | ||
| /// "Health: ", | ||
| /// 5, | ||
| /// '/', | ||
| /// 8 | ||
| /// ); | ||
| /// | ||
| /// assert_writeable_eq!(concatenated, "Health: 5/8"); | ||
| /// ``` | ||
| #[macro_export] | ||
| #[doc(hidden)] // macro | ||
| macro_rules! __concatenate { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
https://github.com/unicode-org/icu4x/actions/runs/17571159926/job/49907497551 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Open to suggestions on other names.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. join? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume it's mostly going to be imported |
||
| // Base case: | ||
| ($x:expr) => ($x); | ||
| // `$x` followed by at least one `$y,` | ||
| ($x:expr, $($y:expr),+) => ( | ||
| // Call `concatenate!` recursively on the tail `$y` | ||
sffc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| $crate::adapters::Concatenate($x, $crate::concatenate!($($y),+)) | ||
| ) | ||
| } | ||
| #[doc(inline)] | ||
| pub use __concatenate as concatenate; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -79,6 +79,7 @@ | |
| extern crate alloc; | ||
|
|
||
| mod cmp; | ||
| mod concat; | ||
| #[cfg(feature = "either")] | ||
| mod either; | ||
| mod impls; | ||
|
|
@@ -93,13 +94,15 @@ use alloc::string::String; | |
| use core::fmt; | ||
|
|
||
| pub use cmp::{cmp_str, cmp_utf8}; | ||
| pub use concat::concatenate; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: do we think this should be toplevel or under adapters? I think either is fine, just calling out the choice. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||
| pub use to_string_or_borrow::to_string_or_borrow; | ||
| pub use try_writeable::TryWriteable; | ||
|
|
||
| /// Helper types for trait impls. | ||
| pub mod adapters { | ||
| use super::*; | ||
|
|
||
| pub use concat::Concatenate; | ||
| pub use parts_write_adapter::CoreWriteAsPartsWrite; | ||
| pub use parts_write_adapter::WithPart; | ||
| pub use try_writeable::TryWriteableInfallibleAsWriteable; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand how this gives you a string-substring comparison, the examples certainly don't show that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm? The example is comparing a string to a list of substrings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to me a string substring comparison is either
eq("foo", 1, 2, "oo")or"foo".contains("oo").the thing that
concatenate!is doing is just concatenatingWriteables without materializing them toStrings. I don't really see the main selling point as "string-substring comparison".