use super::*;
use crate::parts_write_adapter::CoreWriteAsPartsWrite;
use core::convert::Infallible;
pub trait TryWriteable {
type Error;
fn try_write_to<W: fmt::Write + ?Sized>(
&self,
sink: &mut W,
) -> Result<Result<(), Self::Error>, fmt::Error> {
self.try_write_to_parts(&mut CoreWriteAsPartsWrite(sink))
}
fn try_write_to_parts<S: PartsWrite + ?Sized>(
&self,
sink: &mut S,
) -> Result<Result<(), Self::Error>, fmt::Error>;
fn writeable_length_hint(&self) -> LengthHint {
LengthHint::undefined()
}
fn try_writeable_borrow(&self) -> Option<Result<&str, (Self::Error, &str)>> {
None
}
#[cfg(feature = "alloc")]
fn try_write_to_string(&self) -> Result<Cow<'_, str>, (Self::Error, Cow<'_, str>)> {
if let Some(borrow) = self.try_writeable_borrow() {
return borrow
.map(Cow::Borrowed)
.map_err(|(e, s)| (e, Cow::Borrowed(s)));
}
let hint = self.writeable_length_hint();
if hint.is_zero() {
return Ok(Cow::Borrowed(""));
}
let mut output = String::with_capacity(hint.capacity());
match self
.try_write_to(&mut output)
.unwrap_or_else(|fmt::Error| Ok(()))
{
Ok(()) => Ok(Cow::Owned(output)),
Err(e) => Err((e, Cow::Owned(output))),
}
}
}
impl<T, E> TryWriteable for Result<T, E>
where
T: Writeable,
E: Writeable + Clone,
{
type Error = E;
#[inline]
fn try_write_to<W: fmt::Write + ?Sized>(
&self,
sink: &mut W,
) -> Result<Result<(), Self::Error>, fmt::Error> {
match self {
Ok(t) => t.write_to(sink).map(Ok),
Err(e) => e.write_to(sink).map(|()| Err(e.clone())),
}
}
#[inline]
fn try_write_to_parts<S: PartsWrite + ?Sized>(
&self,
sink: &mut S,
) -> Result<Result<(), Self::Error>, fmt::Error> {
match self {
Ok(t) => t.write_to_parts(sink).map(Ok),
Err(e) => sink
.with_part(Part::ERROR, |sink| e.write_to_parts(sink))
.map(|()| Err(e.clone())),
}
}
#[inline]
fn writeable_length_hint(&self) -> LengthHint {
match self {
Ok(t) => t.writeable_length_hint(),
Err(e) => e.writeable_length_hint(),
}
}
fn try_writeable_borrow(&self) -> Option<Result<&str, (Self::Error, &str)>> {
match self {
Ok(t) => t.writeable_borrow().map(Ok),
Err(e) => e.writeable_borrow().map(|s| Err((e.clone(), s))),
}
}
#[inline]
#[cfg(feature = "alloc")]
fn try_write_to_string(&self) -> Result<Cow<'_, str>, (Self::Error, Cow<'_, str>)> {
match self {
Ok(t) => Ok(t.write_to_string()),
Err(e) => Err((e.clone(), e.write_to_string())),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
#[allow(clippy::exhaustive_structs)] pub struct TryWriteableInfallibleAsWriteable<T>(pub T);
impl<T> Writeable for TryWriteableInfallibleAsWriteable<T>
where
T: TryWriteable<Error = Infallible>,
{
#[inline]
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
match self.0.try_write_to(sink) {
Ok(Ok(())) => Ok(()),
Ok(Err(infallible)) => match infallible {},
Err(e) => Err(e),
}
}
#[inline]
fn write_to_parts<S: PartsWrite + ?Sized>(&self, sink: &mut S) -> fmt::Result {
match self.0.try_write_to_parts(sink) {
Ok(Ok(())) => Ok(()),
Ok(Err(infallible)) => match infallible {},
Err(e) => Err(e),
}
}
#[inline]
fn writeable_length_hint(&self) -> LengthHint {
self.0.writeable_length_hint()
}
#[inline]
fn writeable_borrow(&self) -> Option<&str> {
let Ok(s) = self.0.try_writeable_borrow()?;
Some(s)
}
#[inline]
#[cfg(feature = "alloc")]
fn write_to_string(&self) -> Cow<'_, str> {
match self.0.try_write_to_string() {
Ok(s) => s,
Err((infallible, _)) => match infallible {},
}
}
}
impl<T> fmt::Display for TryWriteableInfallibleAsWriteable<T>
where
T: TryWriteable<Error = Infallible>,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write_to(f)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
#[allow(clippy::exhaustive_structs)] pub struct WriteableAsTryWriteableInfallible<T>(pub T);
impl<T> TryWriteable for WriteableAsTryWriteableInfallible<T>
where
T: Writeable,
{
type Error = Infallible;
#[inline]
fn try_write_to<W: fmt::Write + ?Sized>(
&self,
sink: &mut W,
) -> Result<Result<(), Infallible>, fmt::Error> {
self.0.write_to(sink).map(Ok)
}
#[inline]
fn try_write_to_parts<S: PartsWrite + ?Sized>(
&self,
sink: &mut S,
) -> Result<Result<(), Infallible>, fmt::Error> {
self.0.write_to_parts(sink).map(Ok)
}
#[inline]
fn writeable_length_hint(&self) -> LengthHint {
self.0.writeable_length_hint()
}
#[inline]
fn try_writeable_borrow(&self) -> Option<Result<&str, (Self::Error, &str)>> {
self.0.writeable_borrow().map(Ok)
}
#[inline]
#[cfg(feature = "alloc")]
fn try_write_to_string(&self) -> Result<Cow<'_, str>, (Infallible, Cow<'_, str>)> {
Ok(self.0.write_to_string())
}
}
#[macro_export]
macro_rules! impl_try_writeable_delegate {
($ty:ty, |&$self:ident| $delegate:expr, Error = $error:ty $(, |$error_arg:ident| $error_map:expr)? $(, #[$alloc_feature:meta] fn try_write_to_string)? $(, where $($generics:tt)*)?) => {
impl$(<$($generics)*>)? $crate::TryWriteable for $ty {
type Error = $error;
#[inline]
fn try_write_to<W: core::fmt::Write + ?Sized>(
&$self,
sink: &mut W,
) -> core::result::Result<core::result::Result<(), Self::Error>, core::fmt::Error> {
let result = ($delegate).try_write_to(sink)?;
$(
let result = result.map_err(|$error_arg| { $error_map });
)?
Ok(result)
}
#[inline]
fn try_write_to_parts<S: $crate::PartsWrite + ?Sized>(
&$self,
sink: &mut S,
) -> core::result::Result<core::result::Result<(), Self::Error>, core::fmt::Error> {
let result = ($delegate).try_write_to_parts(sink)?;
$(
let result = result.map_err(|$error_arg| { $error_map });
)?
Ok(result)
}
#[inline]
fn writeable_length_hint(&$self) -> $crate::LengthHint {
($delegate).writeable_length_hint()
}
#[inline]
fn try_writeable_borrow(&$self) -> Option<Result<&str, (Self::Error, &str)>> {
let result = ($delegate).try_writeable_borrow()?;
$(
let error_map = |$error_arg| { $error_map };
let result = result.map_err(|(err, cow)| (error_map(err), cow));
)?
Some(result)
}
#[inline]
$(#[$alloc_feature])?
fn try_write_to_string(
&$self,
) -> core::result::Result<
$crate::_internal::Cow<'_, str>,
(Self::Error, $crate::_internal::Cow<'_, str>),
> {
let result = ($delegate).try_write_to_string();
$(
let error_map = |$error_arg| { $error_map };
let result = result.map_err(|(err, cow)| (error_map(err), cow));
)?
result
}
}
};
}
impl_try_writeable_delegate!(
&T,
|&self| *self,
Error = T::Error,
#[cfg(feature = "alloc")] fn try_write_to_string,
where T: TryWriteable + ?Sized
);
#[macro_export]
macro_rules! assert_try_writeable_eq {
($actual_writeable:expr, $expected_str:expr $(,)?) => {
$crate::assert_try_writeable_eq!($actual_writeable, $expected_str, Ok(()))
};
($actual_writeable:expr, $expected_str:expr, $expected_result:expr $(,)?) => {
$crate::assert_try_writeable_eq!($actual_writeable, $expected_str, $expected_result, "")
};
($actual_writeable:expr, $expected_str:expr, $expected_result:expr, $($arg:tt)+) => {{
$crate::assert_try_writeable_eq!(@internal, $actual_writeable, $expected_str, $expected_result, $($arg)*);
}};
(@internal, $actual_writeable:expr, $expected_str:expr, $expected_result:expr, $($arg:tt)+) => {{
let actual_writeable = &$actual_writeable;
let (actual_str, actual_parts, actual_error) = $crate::_internal::try_writeable_to_parts_for_test(actual_writeable);
assert_eq!(actual_str, $expected_str, $($arg)*);
assert_eq!(actual_error, Result::<(), _>::from($expected_result).err(), $($arg)*);
let actual_result = match $crate::TryWriteable::try_write_to_string(&actual_writeable) {
Ok(actual_cow_str) => {
assert_eq!(actual_cow_str, $expected_str, $($arg)+);
Ok(())
}
Err((e, actual_cow_str)) => {
assert_eq!(actual_cow_str, $expected_str, $($arg)+);
Err(e)
}
};
assert_eq!(actual_result, Result::<(), _>::from($expected_result), $($arg)*);
let length_hint = $crate::TryWriteable::writeable_length_hint(&actual_writeable);
assert!(
length_hint.0 <= actual_str.len(),
"hint lower bound {} larger than actual length {}: {}",
length_hint.0, actual_str.len(), format!($($arg)*),
);
if let Some(upper) = length_hint.1 {
assert!(
actual_str.len() <= upper,
"hint upper bound {} smaller than actual length {}: {}",
length_hint.0, actual_str.len(), format!($($arg)*),
);
}
actual_parts }};
}
#[macro_export]
macro_rules! assert_try_writeable_parts_eq {
($actual_writeable:expr, $expected_str:expr, $expected_parts:expr $(,)?) => {
$crate::assert_try_writeable_parts_eq!($actual_writeable, $expected_str, Ok(()), $expected_parts)
};
($actual_writeable:expr, $expected_str:expr, $expected_result:expr, $expected_parts:expr $(,)?) => {
$crate::assert_try_writeable_parts_eq!($actual_writeable, $expected_str, $expected_result, $expected_parts, "")
};
($actual_writeable:expr, $expected_str:expr, $expected_result:expr, $expected_parts:expr, $($arg:tt)+) => {{
let actual_parts = $crate::assert_try_writeable_eq!(@internal, $actual_writeable, $expected_str, $expected_result, $($arg)*);
assert_eq!(actual_parts, $expected_parts, $($arg)+);
}};
}
#[test]
fn test_result_try_writeable() {
let mut result: Result<&str, usize> = Ok("success");
assert_try_writeable_eq!(result, "success");
result = Err(44);
assert_try_writeable_eq!(result, "44", Err(44));
assert_try_writeable_parts_eq!(result, "44", Err(44), [(0, 2, Part::ERROR)])
}
#[cfg(test)]
struct DelegatedTryMessage<'s>(Result<&'s str, usize>);
#[cfg(test)]
impl_try_writeable_delegate!(DelegatedTryMessage<'_>, |&self| &self.0, Error = usize);
#[test]
fn test_delegated_try_writeable() {
let mut message = DelegatedTryMessage(Ok("success"));
assert_try_writeable_eq!(message, "success");
message = DelegatedTryMessage(Err(44));
assert_try_writeable_eq!(message, "44", Err(44));
}