1use core::{convert::Infallible, fmt::Display};
2
3use alloc::boxed::Box;
4
5use crate::{Cursor, Parse, ReprForm, method::MethodDescriptor, strip_digits_prefix};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub enum ClassName<'a> {
12 TopLevel(CanonicalClassName<'a>),
14 #[doc(alias = "Generic")]
17 Member {
18 parent: Box<Self>,
20 simple: &'a str,
22 },
23 Local {
25 parent: Box<Self>,
27 simple: &'a str,
29 index: u32,
31 },
32 Anonymous {
34 parent: Box<Self>,
36 index: u32,
38 },
39 #[doc(alias = "ConstructorGeneric")]
42 MethodGeneric {
43 class: Box<Self>,
45 method: MethodDescriptor<'a>,
47 simple: &'a str,
49 },
50}
51
52impl Display for ClassName<'_> {
53 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54 match self {
55 ClassName::TopLevel(canonical_class_name) => write!(f, "{canonical_class_name}"),
56 ClassName::Member { parent, simple } => write!(f, "{parent}${simple}"),
57 ClassName::Local {
58 parent,
59 simple,
60 index,
61 } => write!(f, "{parent}${index}{simple}"),
62 ClassName::Anonymous { parent, index } => write!(f, "{parent}${index}"),
63 ClassName::MethodGeneric {
64 class,
65 method,
66 simple,
67 } => write!(f, "{class}${method}${simple}"),
68 }
69 }
70}
71
72impl<'a> Parse<'a> for ClassName<'a> {
73 type Error = Infallible;
74
75 fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
76 let s = cursor.get();
77 if let Some((parent, simple)) = s.rsplit_once('$') {
78 if let Some((parent, method)) = parent.rsplit_once('$')
79 && method.chars().next().is_some_and(|c| c == '(')
80 && let Ok(method) = MethodDescriptor::parse_from(&mut Cursor::new(method))
81 {
82 return Ok(Self::MethodGeneric {
83 class: Box::new(Self::parse_from(&mut Cursor::new(parent)).unwrap()),
84 method,
85 simple,
86 });
87 }
88
89 let parent = Box::new(Self::parse_from(&mut Cursor::new(parent))?);
90 let (digits, simple) = strip_digits_prefix(simple);
91 cursor.clear();
92 Ok(match (digits, simple.is_empty()) {
93 (None, _) => Self::Member { parent, simple },
95 (Some(index), true) => Self::Anonymous { parent, index },
96 (Some(index), false) => Self::Local {
97 parent,
98 simple,
99 index,
100 },
101 })
102 } else {
103 CanonicalClassName::parse_from(cursor).map(Self::TopLevel)
104 }
105 }
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
112pub struct CanonicalClassName<'a> {
113 pub package: Option<&'a str>,
115 pub simple: &'a str,
117 pub form: ReprForm,
119}
120
121impl Display for CanonicalClassName<'_> {
122 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
123 if let Some(pkg) = self.package {
124 write!(f, "{pkg}{}{}", self.form.package_separator(), self.simple)
125 } else {
126 write!(f, "{}", self.simple)
127 }
128 }
129}
130
131impl<'a> Parse<'a> for CanonicalClassName<'a> {
132 type Error = Infallible;
133
134 fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
135 Ok(cursor.advance(|s| {
136 let form = if s.contains('/') {
137 ReprForm::Internal
138 } else {
139 ReprForm::JLS
140 };
141 let (package, c) = s.rsplit_once(form.package_separator()).unzip();
142 (
143 Self {
144 package,
145 simple: c.unwrap_or(s),
146 form,
147 },
148 "",
149 )
150 }))
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use alloc::boxed::Box;
157
158 use crate::{CanonicalClassName, ClassName, ReprForm, parse, validate_rw};
159
160 #[test]
161 fn top_level() {
162 assert_eq!(
163 parse::<'_, ClassName<'_>>("java.lang.String").unwrap(),
164 ClassName::TopLevel(CanonicalClassName {
165 package: Some("java.lang"),
166 simple: "String",
167 form: ReprForm::JLS,
168 })
169 );
170
171 assert_eq!(
172 parse::<'_, ClassName<'_>>("Foo").unwrap(),
173 ClassName::TopLevel(CanonicalClassName {
174 package: None,
175 simple: "Foo",
176 form: ReprForm::JLS,
177 })
178 );
179
180 validate_rw::<'_, ClassName<'_>>("java.lang.String");
181 }
182
183 #[test]
184 fn top_level_jvm() {
185 assert_eq!(
186 parse::<'_, ClassName<'_>>("java/lang/String").unwrap(),
187 ClassName::TopLevel(CanonicalClassName {
188 package: Some("java/lang"),
189 simple: "String",
190 form: ReprForm::Internal
191 })
192 );
193
194 validate_rw::<'_, ClassName<'_>>("java/lang/String");
195 }
196
197 #[test]
198 fn member() {
199 assert_eq!(
200 parse::<'_, ClassName<'_>>("java.util.Map$Entry").unwrap(),
201 ClassName::Member {
202 parent: Box::new(ClassName::TopLevel(CanonicalClassName {
203 package: Some("java.util"),
204 simple: "Map",
205 form: ReprForm::JLS
206 })),
207 simple: "Entry"
208 }
209 );
210
211 validate_rw::<'_, ClassName<'_>>("java.util.Map$Entry");
212 }
213
214 #[test]
215 fn local() {
216 assert_eq!(
217 parse::<'_, ClassName<'_>>("com.example.OuterClass$1LocalClass").unwrap(),
218 ClassName::Local {
219 parent: Box::new(ClassName::TopLevel(CanonicalClassName {
220 package: Some("com.example"),
221 simple: "OuterClass",
222 form: ReprForm::JLS
223 })),
224 index: 1,
225 simple: "LocalClass"
226 }
227 );
228
229 validate_rw::<'_, ClassName<'_>>("com.example.OuterClass$1LocalClass");
230 }
231
232 #[test]
233 fn anonymous() {
234 assert_eq!(
235 parse::<'_, ClassName<'_>>("com.example.OuterClass$1").unwrap(),
236 ClassName::Anonymous {
237 parent: Box::new(ClassName::TopLevel(CanonicalClassName {
238 package: Some("com.example"),
239 simple: "OuterClass",
240 form: ReprForm::JLS
241 })),
242 index: 1,
243 }
244 );
245
246 validate_rw::<'_, ClassName<'_>>("com.example.OuterClass$1");
247 }
248}