Skip to main content

rustidy_ast/
item.rs

1//! Item
2
3// Modules
4pub mod const_;
5pub mod enum_;
6pub mod extern_block;
7pub mod extern_crate;
8pub mod function;
9pub mod implementation;
10pub mod macro_;
11pub mod macro_rules;
12pub mod mod_;
13pub mod static_;
14pub mod struct_;
15pub mod trait_;
16pub mod type_alias;
17pub mod union;
18pub mod use_;
19
20// Exports
21pub use self::{
22	const_::ConstantItem,
23	enum_::Enumeration,
24	extern_block::ExternBlock,
25	extern_crate::ExternCrate,
26	function::Function,
27	implementation::Implementation,
28	macro_::MacroInvocationSemi,
29	macro_rules::MacroRulesDefinition,
30	mod_::Module,
31	static_::StaticItem,
32	struct_::Struct,
33	trait_::Trait,
34	type_alias::TypeAlias,
35	union::Union,
36	use_::UseDeclaration,
37};
38
39// Imports
40use {
41	crate::attr,
42	super::{
43		attr::{
44			DelimTokenTree,
45			DelimTokenTreeInner,
46			OuterAttrOrDocComment,
47			WithOuterAttributes,
48		},
49		token,
50		util::{Braced, Parenthesized},
51		vis::Visibility,
52	},
53	core::mem,
54	rustidy_ast_util::{Identifier, PunctuatedTrailing, delimited, punct},
55	rustidy_format::{Format, Formattable, WhitespaceFormat},
56	rustidy_parse::Parse,
57	rustidy_print::Print,
58	rustidy_util::{ArenaIdx, Whitespace, decl_arena},
59};
60
61#[derive(PartialEq, Eq, Clone, Debug)]
62#[derive(serde::Serialize, serde::Deserialize)]
63#[derive(Parse, Formattable, Format, Print)]
64#[format(before_with = Self::merge_use)]
65pub struct Items(
66	#[format(args = rustidy_format::vec::args_prefix_ws(Whitespace::INDENT))]
67	pub Vec<Item>,
68);
69
70impl Items {
71	pub fn merge_use(&mut self, ctx: &mut rustidy_format::Context) {
72		#[expect(clippy::unused_peekable, reason = "We use `Peekable::next_if_map`")]
73		let mut items = mem::take(&mut self.0).into_iter().peekable();
74		while let Some(mut item) = items.next() {
75			item = match item.try_into_use_decl() {
76				Ok((attrs, vis, mut first_use_decl)) => {
77					while let Some(use_decl) = items.next_if_map(
78						|item| item
79							.try_into_just_use_decl(ctx, vis.as_ref())
80					) {
81						first_use_decl.merge(use_decl);
82					}
83
84					Item(ArenaIdx::new(
85						WithOuterAttributes { attrs, inner: ItemInner::Vis(
86							VisItem { vis, inner: VisItemInner::Use(first_use_decl), }
87						), }
88					))
89				},
90				Err(item) => item,
91			};
92
93			self.0.push(item);
94		}
95	}
96}
97
98/// `Item`
99#[derive(PartialEq, Eq, Clone, Debug)]
100#[derive(serde::Serialize, serde::Deserialize)]
101#[derive(Parse, Formattable, Format, Print)]
102pub struct Item(
103	#[format(args = attr::with::fmt(Whitespace::INDENT))]
104	pub ArenaIdx<WithOuterAttributes<ItemInner>>,
105);
106
107impl Item {
108	#[expect(clippy::result_large_err, reason = "TODO")]
109	fn try_into_use_decl(self) -> Result<(Vec<OuterAttrOrDocComment>, Option<Visibility>, UseDeclaration), Self> {
110		self.0.try_take_map(|item| match item.inner {
111			ItemInner::Vis(VisItem { vis, inner: VisItemInner::Use(use_decl), }) => Ok((item.attrs, vis, use_decl)),
112			_ => Err(item),
113		}).map_err(Self)
114	}
115
116	// TODO: This needs to check for comments in the prefix whitespace.
117	#[expect(clippy::result_large_err, reason = "TODO")]
118	fn try_into_just_use_decl(
119		self,
120		ctx: &mut rustidy_format::Context,
121		expected_vis: Option<&Visibility>,
122	) -> Result<UseDeclaration, Self> {
123		self.0.try_take_map(|mut item| {
124			// Note: If no prefix whitespace exists, we can merge them anyway.
125			if matches!(item.prefix_ws_is_pure(ctx), Some(false)) {
126				return Err(item);
127			}
128			if !item.attrs.is_empty() {
129				return Err(item);
130			}
131
132			match item.inner {
133				ItemInner::Vis(VisItem { vis, inner: VisItemInner::Use(use_decl), }) if vis.as_ref() == expected_vis => Ok(use_decl),
134				_ => Err(item),
135			}
136		}).map_err(Self)
137	}
138}
139
140#[derive(PartialEq, Eq, Clone, Debug)]
141#[derive(serde::Serialize, serde::Deserialize)]
142#[derive(Parse, Formattable, Format, Print)]
143#[parse(name = "an item")]
144pub enum ItemInner {
145	Vis(VisItem),
146	Macro(MacroItem),
147}
148
149decl_arena! { WithOuterAttributes<ItemInner> }
150
151/// `VisItem`
152#[derive(PartialEq, Eq, Clone, Debug)]
153#[derive(serde::Serialize, serde::Deserialize)]
154#[derive(Parse, Formattable, Format, Print)]
155pub struct VisItem {
156	pub vis:   Option<Visibility>,
157	#[format(prefix_ws(expr = Whitespace::SINGLE, if_ = self.vis.is_some()))]
158	pub inner: VisItemInner,
159}
160
161#[derive(PartialEq, Eq, Clone, Debug)]
162#[derive(serde::Serialize, serde::Deserialize)]
163#[derive(Parse, Formattable, Format, Print)]
164pub enum VisItemInner {
165	Module(Module),
166	ExternCrate(ExternCrate),
167	Use(UseDeclaration),
168	Function(Function),
169	TypeAlias(TypeAlias),
170	Struct(Struct),
171	Enum(Enumeration),
172	Union(Union),
173	Constant(ConstantItem),
174	Static(StaticItem),
175	Trait(Trait),
176	Implementation(Implementation),
177	ExternBlock(ExternBlock),
178	DeclMacro(DeclMacro),
179}
180
181/// `MacroItem`
182#[derive(PartialEq, Eq, Clone, Debug)]
183#[derive(serde::Serialize, serde::Deserialize)]
184#[derive(Parse, Formattable, Format, Print)]
185pub enum MacroItem {
186	Invocation(MacroInvocationSemi),
187	Definition(MacroRulesDefinition),
188}
189
190
191// Note: Nightly-only
192#[derive(PartialEq, Eq, Clone, Debug)]
193#[derive(serde::Serialize, serde::Deserialize)]
194#[derive(Parse, Formattable, Format, Print)]
195pub struct DeclMacro {
196	pub macro_: token::Macro,
197	#[parse(fatal)]
198	#[format(prefix_ws = Whitespace::SINGLE)]
199	pub ident:  Identifier,
200	#[format(prefix_ws = match self.body {
201		DeclMacroBody::Branches(_) => Whitespace::SINGLE,
202		DeclMacroBody::Inline(_) => Whitespace::REMOVE,
203	})]
204	pub body:   DeclMacroBody,
205}
206
207#[derive(PartialEq, Eq, Clone, Debug)]
208#[derive(serde::Serialize, serde::Deserialize)]
209#[derive(Parse, Formattable, Format, Print)]
210pub enum DeclMacroBody {
211	Branches(DeclMacroBodyBranches),
212	Inline(DeclMacroBodyInline),
213}
214
215#[derive(PartialEq, Eq, Clone, Debug)]
216#[derive(serde::Serialize, serde::Deserialize)]
217#[derive(Parse, Formattable, Format, Print)]
218pub struct DeclMacroBodyInline {
219	#[format(args = delimited::fmt_indent_if_non_blank())]
220	pub args: Parenthesized<DelimTokenTreeInner>,
221	#[format(prefix_ws = Whitespace::SINGLE)]
222	#[format(args = delimited::fmt_indent_if_non_blank())]
223	pub body: Braced<DelimTokenTreeInner>,
224}
225
226#[derive(PartialEq, Eq, Clone, Debug)]
227#[derive(serde::Serialize, serde::Deserialize)]
228#[derive(Parse, Formattable, Format, Print)]
229pub struct DeclMacroBodyBranches(
230	#[format(args = delimited::fmt_indent_if_non_blank())]
231	pub Braced<DeclMacroBodyBranchesInner>,
232);
233
234#[derive(PartialEq, Eq, Clone, Debug)]
235#[derive(serde::Serialize, serde::Deserialize)]
236#[derive(Parse, Formattable, Format, Print)]
237pub struct DeclMacroBodyBranchesInner(
238	#[format(args = punct::fmt(Whitespace::INDENT, Whitespace::REMOVE))]
239	pub PunctuatedTrailing<DeclMacroBranch, token::Comma>,
240);
241
242#[derive(PartialEq, Eq, Clone, Debug)]
243#[derive(serde::Serialize, serde::Deserialize)]
244#[derive(Parse, Formattable, Format, Print)]
245pub struct DeclMacroBranch {
246	pub extra: Option<DeclMacroBranchExtra>,
247	#[format(prefix_ws(expr = Whitespace::SINGLE, if_ = self.extra.is_some()))]
248	pub args:  DelimTokenTree,
249	#[format(prefix_ws = Whitespace::SINGLE)]
250	pub arrow: token::FatArrow,
251	#[format(prefix_ws = Whitespace::SINGLE)]
252	pub body:  DelimTokenTree,
253}
254
255#[derive(PartialEq, Eq, Clone, Debug)]
256#[derive(serde::Serialize, serde::Deserialize)]
257#[derive(Parse, Formattable, Format, Print)]
258pub enum DeclMacroBranchExtra {
259	Attr(DeclMacroBranchAttr),
260	Derive(DeclMacroBranchDerive),
261}
262
263#[derive(PartialEq, Eq, Clone, Debug)]
264#[derive(serde::Serialize, serde::Deserialize)]
265#[derive(Parse, Formattable, Format, Print)]
266pub struct DeclMacroBranchAttr {
267	pub attr: token::Attr,
268	#[format(prefix_ws = Whitespace::REMOVE)]
269	#[format(args = delimited::FmtRemove)]
270	pub args: Parenthesized<DelimTokenTreeInner>,
271}
272
273#[derive(PartialEq, Eq, Clone, Debug)]
274#[derive(serde::Serialize, serde::Deserialize)]
275#[derive(Parse, Formattable, Format, Print)]
276pub struct DeclMacroBranchDerive {
277	pub derive: token::Derive,
278	#[format(prefix_ws = Whitespace::REMOVE)]
279	#[format(args = delimited::FmtArgs {
280		indent: false,
281		value_non_blank: (),
282		value_blank: (),
283		suffix_non_blank: Whitespace::REMOVE,
284		suffix_blank: Whitespace::REMOVE,
285		prefix_args: (),
286		value_args: (),
287		suffix_args: ()
288	})]
289	pub args:   Parenthesized<()>,
290}