Skip to main content

rustidy_ast/attr/
with.rs

1//! Type with attributes
2
3// Imports
4use {
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/// A type with outer attributes
22#[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	/// Creates a new value without any attributes
32	pub const fn without_attributes(inner: T) -> Self {
33		Self { attrs: vec![], inner }
34	}
35
36	/// Maps the inner type
37	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/// A braced type with inner attributes.
134#[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/// A type with inner attributes
178#[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		// Note: `self.inner` might be empty, so we add the tag
216		//       for the suffix in `BracedWithInnerAttributes`.
217		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/// Formatting arguments
234#[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}