diff --git a/crates/ruff_python_formatter/src/lib.rs b/crates/ruff_python_formatter/src/lib.rs index 7697789fe0..4159bda82d 100644 --- a/crates/ruff_python_formatter/src/lib.rs +++ b/crates/ruff_python_formatter/src/lib.rs @@ -16,6 +16,7 @@ use crate::comments::{ }; pub use crate::context::PyFormatContext; pub use crate::options::{MagicTrailingComma, PreviewMode, PyFormatOptions, QuoteStyle}; +pub use crate::shared_traits::{AsFormat, FormattedIter, FormattedIterExt, IntoFormat}; use crate::verbatim::suppressed_node; pub(crate) mod builders; @@ -29,12 +30,11 @@ mod options; pub(crate) mod other; pub(crate) mod pattern; mod prelude; +mod shared_traits; pub(crate) mod statement; pub(crate) mod type_param; mod verbatim; -include!("../../ruff_formatter/shared_traits.rs"); - /// 'ast is the lifetime of the source code (input), 'buf is the lifetime of the buffer (output) pub(crate) type PyFormatter<'ast, 'buf> = Formatter<'buf, PyFormatContext<'ast>>; diff --git a/crates/ruff_python_formatter/src/shared_traits.rs b/crates/ruff_python_formatter/src/shared_traits.rs new file mode 100644 index 0000000000..7ffa5d9484 --- /dev/null +++ b/crates/ruff_python_formatter/src/shared_traits.rs @@ -0,0 +1,113 @@ +// These traits are copied verbatim from ../../ruff_formatter/shared_traits.rs. +// They should stay in sync. Originally, they were included via +// `include!("...")`, but this seems to break rust-analyzer as it treats the +// included file as unlinked. Since there isn't much to copy, we just do that. + +/// Used to get an object that knows how to format this object. +pub trait AsFormat { + type Format<'a>: ruff_formatter::Format + where + Self: 'a; + + /// Returns an object that is able to format this object. + fn format(&self) -> Self::Format<'_>; +} + +/// Implement [`AsFormat`] for references to types that implement [`AsFormat`]. +impl AsFormat for &T +where + T: AsFormat, +{ + type Format<'a> = T::Format<'a> where Self: 'a; + + fn format(&self) -> Self::Format<'_> { + AsFormat::format(&**self) + } +} + +/// Used to convert this object into an object that can be formatted. +/// +/// The difference to [`AsFormat`] is that this trait takes ownership of `self`. +pub trait IntoFormat { + type Format: ruff_formatter::Format; + + fn into_format(self) -> Self::Format; +} + +/// Implement [`IntoFormat`] for [`Option`] when `T` implements [`IntoFormat`] +/// +/// Allows to call format on optional AST fields without having to unwrap the +/// field first. +impl IntoFormat for Option +where + T: IntoFormat, +{ + type Format = Option; + + fn into_format(self) -> Self::Format { + self.map(IntoFormat::into_format) + } +} + +/// Implement [`IntoFormat`] for references to types that implement [`AsFormat`]. +impl<'a, T, C> IntoFormat for &'a T +where + T: AsFormat, +{ + type Format = T::Format<'a>; + + fn into_format(self) -> Self::Format { + AsFormat::format(self) + } +} + +/// Formatting specific [`Iterator`] extensions +pub trait FormattedIterExt { + /// Converts every item to an object that knows how to format it. + fn formatted(self) -> FormattedIter + where + Self: Iterator + Sized, + Self::Item: IntoFormat, + { + FormattedIter { + inner: self, + options: std::marker::PhantomData, + } + } +} + +impl FormattedIterExt for I where I: std::iter::Iterator {} + +pub struct FormattedIter +where + Iter: Iterator, +{ + inner: Iter, + options: std::marker::PhantomData, +} + +impl std::iter::Iterator for FormattedIter +where + Iter: Iterator, + Item: IntoFormat, +{ + type Item = Item::Format; + + fn next(&mut self) -> Option { + Some(self.inner.next()?.into_format()) + } +} + +impl std::iter::FusedIterator for FormattedIter +where + Iter: std::iter::FusedIterator, + Item: IntoFormat, +{ +} + +impl std::iter::ExactSizeIterator for FormattedIter +where + Iter: Iterator + std::iter::ExactSizeIterator, + Item: IntoFormat, +{ +}