From a5243d446c264202dda6a85bcbd2e0e847153c9a Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Wed, 21 Jun 2023 23:58:24 +0200 Subject: [PATCH 1/2] refactor(rome_formatter): Use custom trait for unique `LabelId`s --- crates/rome_formatter/src/builders.rs | 33 +++- .../rome_formatter/src/format_element/tag.rs | 46 +++--- .../expressions/static_member_expression.rs | 6 +- crates/rome_js_formatter/src/lib.rs | 156 +++--------------- .../src/utils/member_chain/mod.rs | 8 +- crates/rome_json_formatter/src/lib.rs | 138 +--------------- 6 files changed, 79 insertions(+), 308 deletions(-) diff --git a/crates/rome_formatter/src/builders.rs b/crates/rome_formatter/src/builders.rs index 4f575be9001..ac0958ab084 100644 --- a/crates/rome_formatter/src/builders.rs +++ b/crates/rome_formatter/src/builders.rs @@ -493,10 +493,25 @@ impl Format for LineSuffixBoundary { /// ## Examples /// /// ```rust -/// use rome_formatter::prelude::*; -/// use rome_formatter::{format, write, LineWidth}; +/// # use rome_formatter::prelude::*; +/// # use rome_formatter::{format, write, LineWidth}; /// -/// enum SomeLabelId {} +/// #[derive(Debug, Copy, Clone)] +/// enum MyLabels { +/// Main +/// } +/// +/// impl tag::LabelDefinition for MyLabels { +/// fn value(&self) -> u64 { +/// *self as u64 +/// } +/// +/// fn debug_name(&self) -> &'static str { +/// match self { +/// Self::Main => "Main" +/// } +/// } +/// } /// /// # fn main() -> FormatResult<()> { /// let formatted = format!( @@ -505,31 +520,31 @@ impl Format for LineSuffixBoundary { /// let mut recording = f.start_recording(); /// write!(recording, [ /// labelled( -/// LabelId::of::(), +/// LabelId::of(MyLabels::Main), /// &text("'I have a label'") /// ) /// ])?; /// /// let recorded = recording.stop(); /// -/// let is_labelled = recorded.first().map_or(false, |element| element.has_label(LabelId::of::())); +/// let is_labelled = recorded.first().map_or(false, |element| element.has_label(LabelId::of(MyLabels::Main))); /// /// if is_labelled { -/// write!(f, [text(" has label SomeLabelId")]) +/// write!(f, [text(" has label `Main`")]) /// } else { -/// write!(f, [text(" doesn't have label SomeLabelId")]) +/// write!(f, [text(" doesn't have label `Main`")]) /// } /// })] /// )?; /// -/// assert_eq!("'I have a label' has label SomeLabelId", formatted.print()?.as_code()); +/// assert_eq!("'I have a label' has label `Main`", formatted.print()?.as_code()); /// # Ok(()) /// # } /// ``` /// /// ## Alternatives /// -/// Use `Memoized.inspect(f)?.has_label(LabelId::of::()` if you need to know if some content breaks that should +/// Use `Memoized.inspect(f)?.has_label(LabelId::of(MyLabels::Main)` if you need to know if some content breaks that should /// only be written later. #[inline] pub fn labelled(label_id: LabelId, content: &Content) -> FormatLabelled diff --git a/crates/rome_formatter/src/format_element/tag.rs b/crates/rome_formatter/src/format_element/tag.rs index 2f383eb5427..dd9dde79548 100644 --- a/crates/rome_formatter/src/format_element/tag.rs +++ b/crates/rome_formatter/src/format_element/tag.rs @@ -1,8 +1,5 @@ use crate::format_element::PrintMode; use crate::{GroupId, TextSize}; -#[cfg(debug_assertions)] -use std::any::type_name; -use std::any::TypeId; use std::cell::Cell; use std::num::NonZeroU8; @@ -239,37 +236,48 @@ impl Align { } } -#[derive(Eq, PartialEq, Copy, Clone)] +#[derive(Debug, Eq, Copy, Clone)] pub struct LabelId { - id: TypeId, + value: u64, #[cfg(debug_assertions)] - label: &'static str, + name: &'static str, } -#[cfg(debug_assertions)] -impl std::fmt::Debug for LabelId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(self.label) - } -} +impl PartialEq for LabelId { + fn eq(&self, other: &Self) -> bool { + let is_equal = self.value == other.value; + + #[cfg(debug_assertions)] + { + if is_equal { + assert_eq!(self.name, other.name, "Two `LabelId`s with different names have the same `value`. Are you mixing labels of two different `LabelDefinition` or are the values returned by the `LabelDefinition` not unique?"); + } + } -#[cfg(not(debug_assertions))] -impl std::fmt::Debug for LabelId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::write!(f, "#{:?}", self.id) + is_equal } } impl LabelId { - pub fn of() -> Self { + pub fn of(label: T) -> Self { Self { - id: TypeId::of::(), + value: label.value(), #[cfg(debug_assertions)] - label: type_name::(), + name: label.debug_name(), } } } +/// Defines the valid labels of a language. You want to have at most one implementation per formatter +/// project. +pub trait LabelDefinition { + /// Returns the `u64` uniquely identifying this specific label. + fn value(&self) -> u64; + + /// Returns the name of the label that is shown in debug builds. + fn debug_name(&self) -> &'static str; +} + #[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum VerbatimKind { Bogus, diff --git a/crates/rome_js_formatter/src/js/expressions/static_member_expression.rs b/crates/rome_js_formatter/src/js/expressions/static_member_expression.rs index d408b47d9f7..d74f9674015 100644 --- a/crates/rome_js_formatter/src/js/expressions/static_member_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/static_member_expression.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use crate::js::expressions::computed_member_expression::AnyJsComputedMemberLike; use crate::parentheses::NeedsParentheses; -use crate::utils::member_chain::MemberChainLabel; +use crate::JsLabels; use rome_formatter::{format_args, write}; use rome_js_syntax::{ AnyJsAssignment, AnyJsAssignmentPattern, AnyJsExpression, AnyJsName, JsAssignmentExpression, @@ -45,7 +45,7 @@ impl Format for AnyJsStaticMemberLike { recording .stop() - .has_label(LabelId::of::()) + .has_label(LabelId::of(JsLabels::MemberChain)) }; let layout = self.layout(is_member_chain)?; @@ -60,7 +60,7 @@ impl Format for AnyJsStaticMemberLike { write!( f, [labelled( - LabelId::of::(), + LabelId::of(JsLabels::MemberChain), &format_no_break )] ) diff --git a/crates/rome_js_formatter/src/lib.rs b/crates/rome_js_formatter/src/lib.rs index f8f43df32e1..bb5c027c216 100644 --- a/crates/rome_js_formatter/src/lib.rs +++ b/crates/rome_js_formatter/src/lib.rs @@ -179,6 +179,7 @@ mod parentheses; pub(crate) mod separated; mod syntax_rewriter; +use rome_formatter::format_element::tag::LabelDefinition; use rome_formatter::prelude::*; use rome_formatter::{ comments::Comments, write, CstFormatContext, Format, FormatLanguage, FormatToken, @@ -196,143 +197,7 @@ use crate::context::{JsFormatContext, JsFormatOptions}; use crate::cst::FormatJsSyntaxNode; use crate::syntax_rewriter::transform; -/// Used to get an object that knows how to format this object. -pub(crate) trait AsFormat { - type Format<'a>: rome_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) - } -} - -/// Implement [AsFormat] for [SyntaxResult] where `T` implements [AsFormat]. -/// -/// Useful to format mandatory AST fields without having to unwrap the value first. -impl AsFormat for rome_rowan::SyntaxResult -where - T: AsFormat, -{ - type Format<'a> = rome_rowan::SyntaxResult> where Self: 'a; - - fn format(&self) -> Self::Format<'_> { - match self { - Ok(value) => Ok(value.format()), - Err(err) => Err(*err), - } - } -} - -/// Implement [AsFormat] for [Option] when `T` implements [AsFormat] -/// -/// Allows to call format on optional AST fields without having to unwrap the field first. -impl AsFormat for Option -where - T: AsFormat, -{ - type Format<'a> = Option> where Self: 'a; - - fn format(&self) -> Self::Format<'_> { - self.as_ref().map(|value| value.format()) - } -} - -/// 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(crate) trait IntoFormat { - type Format: rome_formatter::Format; - - fn into_format(self) -> Self::Format; -} - -impl IntoFormat for rome_rowan::SyntaxResult -where - T: IntoFormat, -{ - type Format = rome_rowan::SyntaxResult; - - fn into_format(self) -> Self::Format { - self.map(IntoFormat::into_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) - } -} - -/// Formatting specific [Iterator] extensions -pub(crate) 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(crate) 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, -{ -} +include!("../../rome_formatter/shared_traits.rs"); pub(crate) type JsFormatter<'buf> = Formatter<'buf, JsFormatContext>; @@ -537,6 +402,23 @@ pub fn format_sub_tree(options: JsFormatOptions, root: &JsSyntaxNode) -> FormatR rome_formatter::format_sub_tree(root, JsFormatLanguage::new(options)) } +#[derive(Copy, Clone, Debug)] +pub(crate) enum JsLabels { + MemberChain, +} + +impl LabelDefinition for JsLabels { + fn value(&self) -> u64 { + *self as u64 + } + + fn debug_name(&self) -> &'static str { + match self { + JsLabels::MemberChain => "MemberChain", + } + } +} + #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/utils/member_chain/mod.rs b/crates/rome_js_formatter/src/utils/member_chain/mod.rs index 72c59e3fa0e..5edccd274f2 100644 --- a/crates/rome_js_formatter/src/utils/member_chain/mod.rs +++ b/crates/rome_js_formatter/src/utils/member_chain/mod.rs @@ -114,6 +114,7 @@ use crate::utils::member_chain::groups::{ MemberChainGroup, MemberChainGroupsBuilder, TailChainGroups, }; use crate::utils::member_chain::simple_argument::SimpleArgument; +use crate::JsLabels; use rome_formatter::{write, Buffer}; use rome_js_syntax::{ AnyJsCallArgument, AnyJsExpression, AnyJsLiteralExpression, JsCallExpression, @@ -122,8 +123,6 @@ use rome_js_syntax::{ use rome_rowan::{AstNode, SyntaxResult}; use std::iter::FusedIterator; -pub(crate) enum MemberChainLabel {} - #[derive(Debug, Clone)] pub(crate) struct MemberChain { root: JsCallExpression, @@ -399,7 +398,10 @@ impl Format for MemberChain { write!( f, - [labelled(LabelId::of::(), &format_content)] + [labelled( + LabelId::of(JsLabels::MemberChain), + &format_content + )] ) } } diff --git a/crates/rome_json_formatter/src/lib.rs b/crates/rome_json_formatter/src/lib.rs index 89070a1cc15..c5a2d3347c7 100644 --- a/crates/rome_json_formatter/src/lib.rs +++ b/crates/rome_json_formatter/src/lib.rs @@ -19,143 +19,7 @@ use rome_formatter::{Formatted, Printed}; use rome_json_syntax::{AnyJsonValue, JsonLanguage, JsonSyntaxNode, JsonSyntaxToken}; use rome_rowan::{AstNode, SyntaxNode, SyntaxTriviaPieceComments, TextRange}; -/// Used to get an object that knows how to format this object. -pub(crate) trait AsFormat { - type Format<'a>: rome_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) - } -} - -/// Implement [AsFormat] for [SyntaxResult] where `T` implements [AsFormat]. -/// -/// Useful to format mandatory AST fields without having to unwrap the value first. -impl AsFormat for rome_rowan::SyntaxResult -where - T: AsFormat, -{ - type Format<'a> = rome_rowan::SyntaxResult> where Self: 'a; - - fn format(&self) -> Self::Format<'_> { - match self { - Ok(value) => Ok(value.format()), - Err(err) => Err(*err), - } - } -} - -/// Implement [AsFormat] for [Option] when `T` implements [AsFormat] -/// -/// Allows to call format on optional AST fields without having to unwrap the field first. -impl AsFormat for Option -where - T: AsFormat, -{ - type Format<'a> = Option> where Self: 'a; - - fn format(&self) -> Self::Format<'_> { - self.as_ref().map(|value| value.format()) - } -} - -/// 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(crate) trait IntoFormat { - type Format: rome_formatter::Format; - - fn into_format(self) -> Self::Format; -} - -impl IntoFormat for rome_rowan::SyntaxResult -where - T: IntoFormat, -{ - type Format = rome_rowan::SyntaxResult; - - fn into_format(self) -> Self::Format { - self.map(IntoFormat::into_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) - } -} - -/// Formatting specific [Iterator] extensions -pub(crate) 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(crate) 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, -{ -} +include!("../../rome_formatter/shared_traits.rs"); pub(crate) type JsonFormatter<'buf> = Formatter<'buf, JsonFormatContext>; From a79e6eadfa2df6899076211aa6cc12d9ce6ca749 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Thu, 22 Jun 2023 22:18:23 +0200 Subject: [PATCH 2/2] Undo `include` changes, Rename `LabelDefinition` to `Label` Signed-off-by: Micha Reiser --- crates/rome_formatter/src/builders.rs | 4 +- .../rome_formatter/src/format_element/tag.rs | 8 +- crates/rome_js_formatter/src/lib.rs | 144 +++++++++++++++++- crates/rome_json_formatter/src/lib.rs | 138 ++++++++++++++++- 4 files changed, 283 insertions(+), 11 deletions(-) diff --git a/crates/rome_formatter/src/builders.rs b/crates/rome_formatter/src/builders.rs index ac0958ab084..3d0a3a39855 100644 --- a/crates/rome_formatter/src/builders.rs +++ b/crates/rome_formatter/src/builders.rs @@ -501,8 +501,8 @@ impl Format for LineSuffixBoundary { /// Main /// } /// -/// impl tag::LabelDefinition for MyLabels { -/// fn value(&self) -> u64 { +/// impl tag::Label for MyLabels { +/// fn id(&self) -> u64 { /// *self as u64 /// } /// diff --git a/crates/rome_formatter/src/format_element/tag.rs b/crates/rome_formatter/src/format_element/tag.rs index dd9dde79548..ee09b7b3fff 100644 --- a/crates/rome_formatter/src/format_element/tag.rs +++ b/crates/rome_formatter/src/format_element/tag.rs @@ -259,9 +259,9 @@ impl PartialEq for LabelId { } impl LabelId { - pub fn of(label: T) -> Self { + pub fn of(label: T) -> Self { Self { - value: label.value(), + value: label.id(), #[cfg(debug_assertions)] name: label.debug_name(), } @@ -270,9 +270,9 @@ impl LabelId { /// Defines the valid labels of a language. You want to have at most one implementation per formatter /// project. -pub trait LabelDefinition { +pub trait Label { /// Returns the `u64` uniquely identifying this specific label. - fn value(&self) -> u64; + fn id(&self) -> u64; /// Returns the name of the label that is shown in debug builds. fn debug_name(&self) -> &'static str; diff --git a/crates/rome_js_formatter/src/lib.rs b/crates/rome_js_formatter/src/lib.rs index bb5c027c216..2c4c4519f8f 100644 --- a/crates/rome_js_formatter/src/lib.rs +++ b/crates/rome_js_formatter/src/lib.rs @@ -179,7 +179,7 @@ mod parentheses; pub(crate) mod separated; mod syntax_rewriter; -use rome_formatter::format_element::tag::LabelDefinition; +use rome_formatter::format_element::tag::Label; use rome_formatter::prelude::*; use rome_formatter::{ comments::Comments, write, CstFormatContext, Format, FormatLanguage, FormatToken, @@ -197,7 +197,143 @@ use crate::context::{JsFormatContext, JsFormatOptions}; use crate::cst::FormatJsSyntaxNode; use crate::syntax_rewriter::transform; -include!("../../rome_formatter/shared_traits.rs"); +/// Used to get an object that knows how to format this object. +pub(crate) trait AsFormat { + type Format<'a>: rome_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) + } +} + +/// Implement [AsFormat] for [SyntaxResult] where `T` implements [AsFormat]. +/// +/// Useful to format mandatory AST fields without having to unwrap the value first. +impl AsFormat for rome_rowan::SyntaxResult +where + T: AsFormat, +{ + type Format<'a> = rome_rowan::SyntaxResult> where Self: 'a; + + fn format(&self) -> Self::Format<'_> { + match self { + Ok(value) => Ok(value.format()), + Err(err) => Err(*err), + } + } +} + +/// Implement [AsFormat] for [Option] when `T` implements [AsFormat] +/// +/// Allows to call format on optional AST fields without having to unwrap the field first. +impl AsFormat for Option +where + T: AsFormat, +{ + type Format<'a> = Option> where Self: 'a; + + fn format(&self) -> Self::Format<'_> { + self.as_ref().map(|value| value.format()) + } +} + +/// 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(crate) trait IntoFormat { + type Format: rome_formatter::Format; + + fn into_format(self) -> Self::Format; +} + +impl IntoFormat for rome_rowan::SyntaxResult +where + T: IntoFormat, +{ + type Format = rome_rowan::SyntaxResult; + + fn into_format(self) -> Self::Format { + self.map(IntoFormat::into_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) + } +} + +/// Formatting specific [Iterator] extensions +pub(crate) 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(crate) 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, +{ +} pub(crate) type JsFormatter<'buf> = Formatter<'buf, JsFormatContext>; @@ -407,8 +543,8 @@ pub(crate) enum JsLabels { MemberChain, } -impl LabelDefinition for JsLabels { - fn value(&self) -> u64 { +impl Label for JsLabels { + fn id(&self) -> u64 { *self as u64 } diff --git a/crates/rome_json_formatter/src/lib.rs b/crates/rome_json_formatter/src/lib.rs index c5a2d3347c7..89070a1cc15 100644 --- a/crates/rome_json_formatter/src/lib.rs +++ b/crates/rome_json_formatter/src/lib.rs @@ -19,7 +19,143 @@ use rome_formatter::{Formatted, Printed}; use rome_json_syntax::{AnyJsonValue, JsonLanguage, JsonSyntaxNode, JsonSyntaxToken}; use rome_rowan::{AstNode, SyntaxNode, SyntaxTriviaPieceComments, TextRange}; -include!("../../rome_formatter/shared_traits.rs"); +/// Used to get an object that knows how to format this object. +pub(crate) trait AsFormat { + type Format<'a>: rome_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) + } +} + +/// Implement [AsFormat] for [SyntaxResult] where `T` implements [AsFormat]. +/// +/// Useful to format mandatory AST fields without having to unwrap the value first. +impl AsFormat for rome_rowan::SyntaxResult +where + T: AsFormat, +{ + type Format<'a> = rome_rowan::SyntaxResult> where Self: 'a; + + fn format(&self) -> Self::Format<'_> { + match self { + Ok(value) => Ok(value.format()), + Err(err) => Err(*err), + } + } +} + +/// Implement [AsFormat] for [Option] when `T` implements [AsFormat] +/// +/// Allows to call format on optional AST fields without having to unwrap the field first. +impl AsFormat for Option +where + T: AsFormat, +{ + type Format<'a> = Option> where Self: 'a; + + fn format(&self) -> Self::Format<'_> { + self.as_ref().map(|value| value.format()) + } +} + +/// 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(crate) trait IntoFormat { + type Format: rome_formatter::Format; + + fn into_format(self) -> Self::Format; +} + +impl IntoFormat for rome_rowan::SyntaxResult +where + T: IntoFormat, +{ + type Format = rome_rowan::SyntaxResult; + + fn into_format(self) -> Self::Format { + self.map(IntoFormat::into_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) + } +} + +/// Formatting specific [Iterator] extensions +pub(crate) 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(crate) 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, +{ +} pub(crate) type JsonFormatter<'buf> = Formatter<'buf, JsonFormatContext>;