use core::{
convert::Infallible,
fmt::{Debug, Display},
};
use alloc::{boxed::Box, vec::Vec};
use smallvec::SmallVec;
use crate::{
Cursor, Parse, ReprForm, TypeParameter, TypeSignature, method::MethodDescriptor,
strip_digits_prefix,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ClassName<'a> {
TopLevel(CanonicalClassName<'a>),
#[doc(alias = "Generic")]
Member {
parent: Box<Self>,
simple: &'a str,
},
Local {
parent: Box<Self>,
simple: &'a str,
index: u32,
},
Anonymous {
parent: Box<Self>,
index: u32,
},
#[doc(alias = "ConstructorGeneric")]
MethodGeneric {
class: Box<Self>,
method: MethodDescriptor<'a>,
simple: &'a str,
},
}
impl Display for ClassName<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ClassName::TopLevel(canonical_class_name) => write!(f, "{canonical_class_name}"),
ClassName::Member { parent, simple } => write!(f, "{parent}${simple}"),
ClassName::Local {
parent,
simple,
index,
} => write!(f, "{parent}${index}{simple}"),
ClassName::Anonymous { parent, index } => write!(f, "{parent}${index}"),
ClassName::MethodGeneric {
class,
method,
simple,
} => write!(f, "{class}${method}${simple}"),
}
}
}
impl<'a> Parse<'a> for ClassName<'a> {
type Error = Infallible;
fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
let s = cursor.get();
if let Some((parent, simple)) = s.rsplit_once('$') {
if let Some((parent, method)) = parent.rsplit_once('$')
&& method.chars().next().is_some_and(|c| c == '(')
&& let Ok(method) = MethodDescriptor::parse_from(&mut Cursor::new(method))
{
return Ok(Self::MethodGeneric {
class: Box::new(Self::parse_from(&mut Cursor::new(parent)).unwrap()),
method,
simple,
});
}
let parent = Box::new(Self::parse_from(&mut Cursor::new(parent))?);
let (digits, simple) = strip_digits_prefix(simple);
cursor.clear();
Ok(match (digits, simple.is_empty()) {
(None, _) => Self::Member { parent, simple },
(Some(index), true) => Self::Anonymous { parent, index },
(Some(index), false) => Self::Local {
parent,
simple,
index,
},
})
} else {
CanonicalClassName::parse_from(cursor).map(Self::TopLevel)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CanonicalClassName<'a> {
pub package: Option<&'a str>,
pub simple: &'a str,
pub form: ReprForm,
}
impl Display for CanonicalClassName<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if let Some(pkg) = self.package {
write!(f, "{pkg}{}{}", self.form.package_separator(), self.simple)
} else {
write!(f, "{}", self.simple)
}
}
}
impl<'a> Parse<'a> for CanonicalClassName<'a> {
type Error = Infallible;
fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
Ok(cursor.advance(|s| {
let form = if s.contains('/') {
ReprForm::Internal
} else {
ReprForm::JLS
};
let (package, c) = s.rsplit_once(form.package_separator()).unzip();
(
Self {
package,
simple: c.unwrap_or(s),
form,
},
"",
)
}))
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct ClassSignature<'a> {
pub params: SmallVec<[TypeParameter<'a>; 1]>,
pub extends: TypeSignature<'a>,
pub impls: Box<[TypeSignature<'a>]>,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum InvalidClassSignature {
UnclosedAngles,
UnknownTypeTag(crate::UnknownTypeTag),
ExpectedReference,
ExpectedClass,
}
impl<'a> Parse<'a> for ClassSignature<'a> {
type Error = InvalidClassSignature;
fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
let mut params = SmallVec::new();
if cursor.0.starts_with('<') {
cursor.get_char();
let contents = cursor.try_advance(|s| {
crate::angle_safe_split(s, &['>']).ok_or(InvalidClassSignature::UnclosedAngles)
})?;
cursor.get_char();
let mut contents = Cursor(contents);
crate::parse_type_params(&mut contents, |param| params.push(param))?;
}
Ok(Self {
params,
extends: {
let sig = TypeSignature::parse_from(cursor)?;
if !matches!(sig, TypeSignature::Class { .. }) {
return Err(InvalidClassSignature::ExpectedClass);
}
sig
},
impls: {
let mut buf = Vec::new();
while !cursor.0.is_empty() {
let sig = TypeSignature::parse_from(cursor)?;
if !matches!(sig, TypeSignature::Class { .. }) {
return Err(InvalidClassSignature::ExpectedClass);
}
buf.push(sig);
}
buf.into()
},
})
}
}
impl Display for ClassSignature<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if !self.params.is_empty() {
crate::display_type_params(&self.params, f)?;
}
write!(f, "{}", self.extends)?;
for sig in &self.impls {
write!(f, "{}", sig)?;
}
Ok(())
}
}
impl Display for InvalidClassSignature {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::UnclosedAngles => write!(f, "unclosed angles"),
Self::UnknownTypeTag(err) => write!(f, "{err}"),
Self::ExpectedReference => {
write!(f, "expected type signature to be `ReferenceTypeSignature`")
}
Self::ExpectedClass => write!(f, "expected type signature to be `ClassTypeSignature`"),
}
}
}
impl core::error::Error for InvalidClassSignature {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::UnknownTypeTag(err) => Some(err),
_ => None,
}
}
}
impl From<crate::UnknownTypeTag> for InvalidClassSignature {
fn from(value: crate::UnknownTypeTag) -> Self {
Self::UnknownTypeTag(value)
}
}
impl From<crate::ParseTypeParamsError> for InvalidClassSignature {
fn from(value: crate::ParseTypeParamsError) -> Self {
match value {
crate::ParseTypeParamsError::UnknownTypeTag(err) => Self::UnknownTypeTag(err),
crate::ParseTypeParamsError::ExpectedReference => Self::ExpectedReference,
}
}
}
impl Debug for ClassSignature<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if !self.params.is_empty() {
let mut iter = self.params.iter().peekable();
write!(f, "<")?;
while let Some(param) = iter.next() {
write!(f, "{param:?}")?;
if iter.peek().is_some() {
write!(f, ", ")?;
}
}
write!(f, ">")?;
}
write!(f, " extends {:?}", self.extends)?;
if !self.impls.is_empty() {
let mut iter = self.impls.iter().peekable();
write!(f, " implements ")?;
while let Some(mom) = iter.next() {
write!(f, "{mom:?}")?;
if iter.peek().is_some() {
write!(f, ", ")?;
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use alloc::boxed::Box;
use smallvec::SmallVec;
use crate::{
CanonicalClassName, ClassName, ClassSignature, ReducedClassTypeSignature, ReprForm,
TypeParameter, TypeSignature, parse, validate_rw,
};
#[test]
fn top_level() {
assert_eq!(
parse::<'_, ClassName<'_>>("java.lang.String").unwrap(),
ClassName::TopLevel(CanonicalClassName {
package: Some("java.lang"),
simple: "String",
form: ReprForm::JLS,
})
);
assert_eq!(
parse::<'_, ClassName<'_>>("Foo").unwrap(),
ClassName::TopLevel(CanonicalClassName {
package: None,
simple: "Foo",
form: ReprForm::JLS,
})
);
validate_rw::<'_, ClassName<'_>>("java.lang.String");
}
#[test]
fn top_level_jvm() {
assert_eq!(
parse::<'_, ClassName<'_>>("java/lang/String").unwrap(),
ClassName::TopLevel(CanonicalClassName {
package: Some("java/lang"),
simple: "String",
form: ReprForm::Internal
})
);
validate_rw::<'_, ClassName<'_>>("java/lang/String");
}
#[test]
fn member() {
assert_eq!(
parse::<'_, ClassName<'_>>("java.util.Map$Entry").unwrap(),
ClassName::Member {
parent: Box::new(ClassName::TopLevel(CanonicalClassName {
package: Some("java.util"),
simple: "Map",
form: ReprForm::JLS
})),
simple: "Entry"
}
);
validate_rw::<'_, ClassName<'_>>("java.util.Map$Entry");
}
#[test]
fn local() {
assert_eq!(
parse::<'_, ClassName<'_>>("com.example.OuterClass$1LocalClass").unwrap(),
ClassName::Local {
parent: Box::new(ClassName::TopLevel(CanonicalClassName {
package: Some("com.example"),
simple: "OuterClass",
form: ReprForm::JLS
})),
index: 1,
simple: "LocalClass"
}
);
validate_rw::<'_, ClassName<'_>>("com.example.OuterClass$1LocalClass");
}
#[test]
fn anonymous() {
assert_eq!(
parse::<'_, ClassName<'_>>("com.example.OuterClass$1").unwrap(),
ClassName::Anonymous {
parent: Box::new(ClassName::TopLevel(CanonicalClassName {
package: Some("com.example"),
simple: "OuterClass",
form: ReprForm::JLS
})),
index: 1,
}
);
validate_rw::<'_, ClassName<'_>>("com.example.OuterClass$1");
}
#[test]
fn class_sig() {
assert_eq!(
parse::<'_, ClassSignature<'_>>(
"Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Cloneable;"
)
.unwrap(),
ClassSignature {
params: SmallVec::new(),
extends: TypeSignature::Class {
sig: ReducedClassTypeSignature {
name: "java/lang/Object",
args: SmallVec::new()
},
suffix: None
},
impls: Box::new([
TypeSignature::Class {
sig: ReducedClassTypeSignature {
name: "java/io/Serializable",
args: SmallVec::new()
},
suffix: None
},
TypeSignature::Class {
sig: ReducedClassTypeSignature {
name: "java/lang/Cloneable",
args: SmallVec::new()
},
suffix: None
},
])
}
);
validate_rw::<'_, ClassSignature<'_>>(
"Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Cloneable;",
);
assert_eq!(
parse::<'_, ClassSignature<'_>>("<T:Ljava/lang/Object;K:V:>Ljava/lang/Object;")
.unwrap(),
ClassSignature {
params: smallvec::smallvec![
TypeParameter {
name: "T",
bound_class: Some(TypeSignature::Class {
sig: ReducedClassTypeSignature {
name: "java/lang/Object",
args: SmallVec::new()
},
suffix: None
}),
bound_interface: Box::new([])
},
TypeParameter {
name: "K",
bound_class: None,
bound_interface: Box::new([])
},
TypeParameter {
name: "V",
bound_class: None,
bound_interface: Box::new([])
}
],
extends: TypeSignature::Class {
sig: ReducedClassTypeSignature {
name: "java/lang/Object",
args: SmallVec::new()
},
suffix: None
},
impls: Box::new([])
}
);
validate_rw::<'_, ClassSignature<'_>>("<T:Ljava/lang/Object;K:V:>Ljava/lang/Object;");
}
}