java_bin_name/
class.rs

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