1use {
5 crate::{attr::{InnerDocComment, OuterDocComment}, util::Braced},
6 super::{InnerAttrOrDocComment, OuterAttrOrDocComment},
7 rustidy_ast_util::delimited,
8 rustidy_format::{
9 Format,
10 FormatOutput,
11 FormatTag,
12 Formattable,
13 WhitespaceConfig,
14 WhitespaceFormat,
15 },
16 rustidy_parse::{ParsableRecursive, Parse, Parser},
17 rustidy_print::Print,
18 rustidy_util::Whitespace,
19};
20
21#[derive(PartialEq, Eq, Clone, Debug)]
23#[derive(serde::Serialize, serde::Deserialize)]
24#[derive(Parse, Formattable, Print)]
25pub struct WithOuterAttributes<T> {
26 pub attrs: Vec<OuterAttrOrDocComment>,
27 pub inner: T,
28}
29
30impl<T> WithOuterAttributes<T> {
31 pub const fn without_attributes(inner: T) -> Self {
33 Self { attrs: vec![], inner }
34 }
35
36 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> WithOuterAttributes<U> {
38 WithOuterAttributes { attrs: self.attrs, inner: f(self.inner), }
39 }
40}
41
42impl<A, T: Format<WhitespaceConfig, A>> Format<WhitespaceConfig, FmtArgs<A>> for WithOuterAttributes<T> {
43 fn format(
44 &mut self,
45 ctx: &mut rustidy_format::Context,
46 prefix_ws: WhitespaceConfig,
47 args: FmtArgs<A>,
48 ) -> FormatOutput {
49 let mut output = FormatOutput::default();
50
51 let mut is_after_newline = false;
52 let mut has_prefix_ws = true;
53 for attr in &mut self.attrs {
54 if is_after_newline {
55 ctx.add_tag(FormatTag::AfterNewline);
56 }
57
58 match has_prefix_ws {
59 true => ctx.format(attr, prefix_ws),
60 false => ctx.format(attr, args.prefix_ws),
61 }.append_to(&mut output);
62
63 is_after_newline = matches!(attr, OuterAttrOrDocComment::DocComment(OuterDocComment::Line(_)));
64 has_prefix_ws = false;
65 }
66
67 let mut value_ctx = ctx.sub_context();
68 for attr in &mut self.attrs {
69 if let Some(attr) = attr.try_as_attr_ref() && let Err(err) = super::update_from_attr(&attr.open.value, &mut value_ctx) {
70 tracing::warn!("Malformed `#[rustidy::config(...)]` attribute: {err:?}");
71 }
72 }
73
74
75 if is_after_newline {
76 value_ctx.add_tag(FormatTag::AfterNewline);
77 }
78
79 match has_prefix_ws {
80 true => value_ctx
81 .format_with(&mut self.inner, prefix_ws, args.inner_args),
82 false => value_ctx
83 .format_with(&mut self.inner, args.prefix_ws, args.inner_args),
84 }.append_to(&mut output);
85
86 output
87 }
88}
89
90impl<T> From<T> for WithOuterAttributes<T> {
91 fn from(inner: T) -> Self {
92 Self { attrs: vec![], inner }
93 }
94}
95
96impl<T, R> ParsableRecursive<R> for WithOuterAttributes<T>
97where
98 T: ParsableRecursive<R> {
99 type Base = WithOuterAttributes<T::Base>;
100 type Infix = T::Infix;
101 type Prefix = T::Prefix;
102 type Suffix = T::Suffix;
103
104 fn from_base(base: Self::Base, parser: &mut Parser) -> Self {
105 Self {
106 attrs: base.attrs,
107 inner: T::from_base(base.inner, parser),
108 }
109 }
110
111 fn join_suffix(root: R, suffix: Self::Suffix, parser: &mut Parser) -> Self {
112 Self {
113 attrs: vec![],
114 inner: T::join_suffix(root, suffix, parser),
115 }
116 }
117
118 fn join_prefix(prefix: Self::Prefix, root: R, parser: &mut Parser) -> Self {
119 Self {
120 attrs: vec![],
121 inner: T::join_prefix(prefix, root, parser),
122 }
123 }
124
125 fn join_infix(lhs: R, infix: Self::Infix, rhs: R, parser: &mut Parser) -> Self {
126 Self {
127 attrs: vec![],
128 inner: T::join_infix(lhs, infix, rhs, parser),
129 }
130 }
131}
132
133#[derive(PartialEq, Eq, Clone, Debug)]
135#[derive(serde::Serialize, serde::Deserialize)]
136#[derive(Parse, Formattable, Print)]
137pub struct BracedWithInnerAttributes<T>(Braced<WithInnerAttributes<T>>);
138
139impl<T: Format<WhitespaceConfig, ()>> Format<WhitespaceConfig, ()> for BracedWithInnerAttributes<T> {
140 fn format(
141 &mut self,
142 ctx: &mut rustidy_format::Context,
143 prefix_ws: WhitespaceConfig,
144 _args: ()
145 ) -> FormatOutput {
146 self.format(
147 ctx,
148 prefix_ws,
149 FmtArgs { prefix_ws: Whitespace::INDENT, inner_args: () }
150 )
151 }
152}
153
154impl<T: Format<WhitespaceConfig, A>, A: Clone> Format<WhitespaceConfig, FmtArgs<A>> for BracedWithInnerAttributes<T> {
155 fn format(
156 &mut self,
157 ctx: &mut rustidy_format::Context,
158 prefix_ws: WhitespaceConfig,
159 args: FmtArgs<A>,
160 ) -> FormatOutput {
161 let mut ctx = ctx.sub_context();
162 for attr in &self.0.value.attrs {
163 if let Some(attr) = attr.try_as_attr_ref() && let Err(err) = super::update_from_attr(&attr.attr.value, &mut ctx) {
164 tracing::warn!("Malformed `#![rustidy::config(...)]` attribute: {err:?}");
165 }
166 }
167
168 ctx.format_with(
169 &mut self.0,
170 prefix_ws,
171 delimited::fmt_indent_if_non_blank_with_value(args)
172 )
173
174 }
175}
176
177#[derive(PartialEq, Eq, Clone, Debug)]
179#[derive(serde::Serialize, serde::Deserialize)]
180#[derive(Parse, Formattable, Print)]
181struct WithInnerAttributes<T> {
182 pub attrs: Vec<InnerAttrOrDocComment>,
183 pub inner: T,
184}
185
186impl<T: Format<WhitespaceConfig, A>, A> Format<WhitespaceConfig, FmtArgs<A>> for WithInnerAttributes<T> {
187 fn format(
188 &mut self,
189 ctx: &mut rustidy_format::Context,
190 prefix_ws: WhitespaceConfig,
191 args: FmtArgs<A>
192 ) -> FormatOutput {
193 let mut output = FormatOutput::default();
194
195 let mut is_after_newline = false;
196 let mut has_prefix_ws = true;
197 for attr in &mut self.attrs {
198 if is_after_newline {
199 ctx.add_tag(FormatTag::AfterNewline);
200 }
201
202 let prefix_ws = match has_prefix_ws {
203 true => prefix_ws,
204 false => args.prefix_ws,
205 };
206
207 ctx
208 .format(attr, prefix_ws)
209 .append_to(&mut output);
210
211 is_after_newline = matches!(attr, InnerAttrOrDocComment::DocComment(InnerDocComment::Line(_)));
212 has_prefix_ws = false;
213 }
214
215 if is_after_newline {
218 ctx.add_tag(FormatTag::AfterNewline);
219 }
220
221 let prefix_ws = match has_prefix_ws {
222 true => prefix_ws,
223 false => args.prefix_ws,
224 };
225 ctx
226 .format_with(&mut self.inner, prefix_ws, args.inner_args)
227 .append_to(&mut output);
228
229 output
230 }
231}
232
233#[derive(Clone, Copy, Debug)]
235pub struct FmtArgs<A> {
236 pub prefix_ws: WhitespaceConfig,
237 pub inner_args: A,
238}
239
240#[must_use]
241pub const fn fmt(prefix_ws: WhitespaceConfig) -> FmtArgs<()> {
242 self::fmt_with(prefix_ws, ())
243}
244
245#[must_use]
246pub const fn fmt_with<A>(prefix_ws: WhitespaceConfig, inner_args: A) -> FmtArgs<A> {
247 FmtArgs { prefix_ws, inner_args }
248}