mod display;
mod internal;
use std::fmt::Display;
#[derive(Debug)]
pub struct FieldSignature<'a>(pub ReferenceType<'a>);
#[derive(Debug)]
pub struct ClassSignature<'a> {
pub type_params: Vec<TypeParameter<'a>>,
pub super_class: ClassType<'a>,
pub super_ifaces: Vec<ClassType<'a>>,
}
pub struct MethodSignature<'a> {
pub type_params: Vec<TypeParameter<'a>>,
pub parameters: Vec<JavaType<'a>>,
pub result: ResultType<'a>,
pub throws: Vec<ThrowsType<'a>>,
}
pub use internal::BaseType;
#[derive(Debug)]
pub enum JavaType<'a> {
Base(BaseType),
Reference(ReferenceType<'a>),
}
impl<'a> JavaType<'a> {
fn from_internal(s: &'a str, internal: internal::JavaType) -> Self {
match internal {
internal::JavaType::Base(b) => JavaType::Base(b),
internal::JavaType::Reference(r) => {
JavaType::Reference(ReferenceType::from_internal(s, r))
}
}
}
}
#[derive(Debug)]
pub enum TypeArgument<'a> {
Unbounded,
Default(ReferenceType<'a>),
Extends(ReferenceType<'a>),
Super(ReferenceType<'a>),
}
#[derive(Debug)]
pub struct SimpleClassType<'a> {
pub name: &'a str,
pub type_args: Vec<TypeArgument<'a>>,
}
impl<'a> SimpleClassType<'a> {
fn from_internal(s: &'a str, internal: internal::SimpleClassType) -> Self {
Self {
name: internal.0.apply(s),
type_args: internal
.1
.into_iter()
.map(|ta| match ta {
internal::TypeArgument::Unbounded => TypeArgument::Unbounded,
internal::TypeArgument::Default(ty) => {
TypeArgument::Default(ReferenceType::from_internal(s, ty))
}
internal::TypeArgument::Extends(ty) => {
TypeArgument::Extends(ReferenceType::from_internal(s, ty))
}
internal::TypeArgument::Super(ty) => {
TypeArgument::Super(ReferenceType::from_internal(s, ty))
}
})
.collect(),
}
}
}
#[derive(Debug)]
pub struct ClassType<'a> {
pub base: SimpleClassType<'a>,
pub nested: Vec<SimpleClassType<'a>>,
}
impl<'a> ClassType<'a> {
fn from_internal(s: &'a str, internal: internal::ClassType) -> Self {
Self {
base: SimpleClassType::from_internal(s, internal.0),
nested: internal
.1
.into_iter()
.map(|ty| SimpleClassType::from_internal(s, ty))
.collect(),
}
}
}
#[derive(Debug)]
pub struct ArrayType<'a> {
pub dimension: usize,
pub ty: JavaType<'a>,
}
#[derive(Debug)]
pub enum ReferenceType<'a> {
Class(ClassType<'a>),
Variable(&'a str),
Array(Box<ArrayType<'a>>),
}
impl<'a> ReferenceType<'a> {
fn from_internal(s: &'a str, internal: internal::ReferenceType) -> Self {
match internal {
internal::ReferenceType::Class(ty) => {
ReferenceType::Class(ClassType::from_internal(s, ty))
}
internal::ReferenceType::Variable(r) => ReferenceType::Variable(r.apply(s)),
internal::ReferenceType::Array { dimension, ty } => {
ReferenceType::Array(Box::new(ArrayType {
dimension,
ty: JavaType::from_internal(s, *ty),
}))
}
}
}
}
#[derive(Debug)]
pub struct TypeParameter<'a> {
pub name: &'a str,
pub class_bound: Option<ReferenceType<'a>>,
pub iface_bounds: Vec<ReferenceType<'a>>,
}
impl<'a> TypeParameter<'a> {
fn from_internal(s: &'a str, internal: internal::TypeParameter) -> Self {
Self {
name: internal.name.apply(s),
class_bound: internal
.class_bound
.map(|bound| ReferenceType::from_internal(s, bound)),
iface_bounds: internal
.iface_bounds
.into_iter()
.map(|bound| ReferenceType::from_internal(s, bound))
.collect(),
}
}
}
pub enum ResultType<'a> {
VoidType,
ValueType(JavaType<'a>),
}
impl<'a> ResultType<'a> {
fn from_internal(s: &'a str, internal: internal::ResultType) -> Self {
match internal {
internal::ResultType::VoidType => ResultType::VoidType,
internal::ResultType::ValueType(ty) => {
ResultType::ValueType(JavaType::from_internal(s, ty))
}
}
}
}
pub enum ThrowsType<'a> {
ClassType(ClassType<'a>),
TypeVariable(&'a str),
}
impl<'a> ThrowsType<'a> {
fn from_internal(s: &'a str, internal: internal::ThrowsType) -> Self {
match internal {
internal::ThrowsType::ClassType(ty) => {
ThrowsType::ClassType(ClassType::from_internal(s, ty))
}
internal::ThrowsType::TypeVariable(name) => ThrowsType::TypeVariable(name.apply(s)),
}
}
}
#[derive(Debug)]
pub struct ParseError<'a> {
signature: &'a str,
internal: internal::ParseError,
}
impl<'a> Display for ParseError<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.internal.error)
}
}
impl<'a> ParseError<'a> {
fn new(signature: &'a str, internal: internal::ParseError) -> Self {
Self {
signature,
internal,
}
}
pub fn position(&self) -> usize {
self.internal.position
}
pub fn message(&self) -> &str {
&self.internal.error
}
pub fn context(&self) -> &str {
self.internal.context
}
pub fn signature(&self) -> &'a str {
self.signature
}
pub fn unconsumed(&self) -> &'a str {
&self.signature[self.internal.position..]
}
pub fn consumed(&self) -> &'a str {
&self.signature[..self.internal.position]
}
}
pub type Result<'a, T> = std::result::Result<T, ParseError<'a>>;
pub fn parse_field_signature(s: &str) -> Result<'_, FieldSignature<'_>> {
internal::parse(
"FieldSignature",
internal::consume_reference_type_signature,
s,
str::char_indices,
)
.map(|ty| FieldSignature(ReferenceType::from_internal(s, ty)))
.map_err(|e| ParseError::new(s, e))
}
pub fn is_field_signature(s: &str) -> bool {
parse_field_signature(s).is_ok()
}
pub fn parse_class_signature(s: &str) -> Result<ClassSignature<'_>> {
internal::parse(
"ClassSignature",
internal::consume_class_signature,
s,
str::char_indices,
)
.map(|parsed| ClassSignature {
type_params: parsed
.type_params
.into_iter()
.map(|p| TypeParameter::from_internal(s, p))
.collect(),
super_class: ClassType::from_internal(s, parsed.super_class),
super_ifaces: parsed
.super_ifaces
.into_iter()
.map(|ty| ClassType::from_internal(s, ty))
.collect(),
})
.map_err(|e| ParseError::new(s, e))
}
pub fn is_class_signature(s: &str) -> bool {
parse_class_signature(s).is_ok()
}
pub fn parse_method_signature(s: &str) -> Result<MethodSignature<'_>> {
internal::parse(
"MethodSignature",
internal::consume_method_signature,
s,
str::char_indices,
)
.map(|parsed| MethodSignature {
type_params: parsed
.type_params
.into_iter()
.map(|p| TypeParameter::from_internal(s, p))
.collect(),
parameters: parsed
.parameters
.into_iter()
.map(|p| JavaType::from_internal(s, p))
.collect(),
result: ResultType::from_internal(s, parsed.result),
throws: parsed
.throws
.into_iter()
.map(|ty| ThrowsType::from_internal(s, ty))
.collect(),
})
.map_err(|e| ParseError::new(s, e))
}
pub fn is_method_signature(s: &str) -> bool {
parse_method_signature(s).is_ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_field_signature() {
assert!(!is_field_signature(""));
assert!(!is_field_signature(" "));
assert!(!is_field_signature(";"));
assert!(!is_field_signature("<TT;>"));
assert!(!is_field_signature("B"));
for s in &[
"TT;", "[[TT;", ] {
assert!(!is_field_signature(&s[..s.len() - 1]));
assert!(!is_field_signature(&format!("{} ", s)));
assert!(
is_field_signature(s),
"expected valid signature (but failed): {}",
s
);
}
}
#[test]
fn test_is_class_signature() {
for s in &[
"Ljava/lang/Enum<Lcom/google/common/base/CaseFormat;>;", "<T::Ljava/io/Serializable;:Ljava/lang/Comparable<TT;>;>Ljava/lang/Object;", "<K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;", "Ljava/lang/Object;Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>;", "<D:Ljava/lang/Object;N::Lcom/sun/tools/javac/util/GraphUtils$DottableNode<TD;TN;>;>Lcom/sun/tools/javac/util/GraphUtils$NodeVisitor<TD;TN;Ljava/lang/StringBuilder;>;", "<OP::Ljdk/incubator/vector/VectorOperators$Operator;T:Ljava/lang/Object;>Ljava/lang/Object;", "<K:Ljava/lang/Object;>Ljdk/internal/loader/AbstractClassLoaderValue<Ljdk/internal/loader/AbstractClassLoaderValue<TCLV;TV;>.Sub<TK;>;TV;>;", "Ljava/lang/invoke/ClassSpecializer<Ljava/lang/invoke/BoundMethodHandle;Ljava/lang/String;Ljava/lang/invoke/BoundMethodHandle$SpeciesData;>.Factory;", ] {
{
let s = &s[..s.len() - 1];
assert!(!is_class_signature(s), "failed to reject: `{}`", s);
}
{
let s = &format!("{} ", s);
assert!(!is_class_signature(s), "failed to reject: `{}`", s);
}
if let Err(e) = parse_class_signature(s) {
panic!("failed to recognize `{s}` as class signature: {e}");
}
}
}
#[test]
fn test_is_method_signature() {
for s in &[
"()TE;", "(TE;)V", "(Ljava/util/function/Consumer<-TE;>;)V", "<T:Ljava/lang/Object;>([TT;)[TT;", ] {
{
let s = &s[..s.len() - 1];
assert!(!is_method_signature(s), "failed to reject: `{}`", s);
}
{
let s = &format!("{} ", s);
assert!(!is_method_signature(s), "failed to reject: `{}`", s);
}
assert!(is_method_signature(s), "failed to recognize: `{}`", s);
}
}
}