cfsp 1.0.1

A JVM Bytecode Manipulation Framework inspired by ASM.
Documentation
use std::iter::Peekable;
use std::str::Chars;

use crate::node::signature::{
    ArrayType, BaseType, ClassType, FormalTypeParameter, ReferenceType, Signature, SignatureType,
    ThrowsType, TypeArgument, TypeVariable, WildcardIndicator,
};
use crate::parse::error::{ParseError, ParseResult};

const EXCLUDED_IDENTIFIER_CHARACTERS: &'static str = ".;[/<>:";

pub fn class_signature(input: &str) -> ParseResult<Signature> {
    let mut input = input.chars().peekable();
    let formal_type_parameters = formal_types(&mut input)?;
    let super_class = class_type_signature(&mut input)?;
    let mut interfaces = Vec::new();

    while let Some(char) = input.peek() {
        if *char == 'L' {
            interfaces.push(class_type_signature(&mut input)?);
        } else {
            break;
        }
    }

    if input.peek().is_some() {
        let remain = input.collect::<Vec<_>>();

        Err(ParseError::Remains(remain.len()))
    } else {
        Ok(Signature::Class {
            formal_type_parameters,
            super_class,
            interfaces,
        })
    }
}

pub fn field_signature(input: &str) -> ParseResult<Signature> {
    let mut input = input.chars().peekable();
    let field_type = reference_type(&mut input)?;

    if input.peek().is_some() {
        let remain = input.collect::<Vec<_>>();

        Err(ParseError::Remains(remain.len()))
    } else {
        Ok(Signature::Field { field_type })
    }
}

pub fn method_signature(input: &str) -> ParseResult<Signature> {
    let mut input = input.chars().peekable();
    let formal_type_parameters = formal_types(&mut input)?;

    assert_char(input.next(), '(')?;

    let mut parameter_types = Vec::new();

    while let Some(char) = input.peek() {
        if *char != ')' {
            parameter_types.push(java_type_signature(&mut input)?);
        } else {
            break;
        }
    }

    assert_char(input.next(), ')')?;

    let return_type = result(&mut input)?;
    let mut exception_types = Vec::new();

    while let Some(char) = input.peek() {
        if *char == '^' {
            exception_types.push(throws_signature(&mut input)?);
        } else {
            break;
        }
    }

    if input.peek().is_some() {
        let remain = input.collect::<Vec<_>>();

        Err(ParseError::Remains(remain.len()))
    } else {
        Ok(Signature::Method {
            formal_type_parameters,
            parameter_types,
            return_type,
            exception_types,
        })
    }
}

fn formal_types(input: &mut Peekable<Chars>) -> ParseResult<Vec<FormalTypeParameter>> {
    assert_char(input.next(), '<')?;
    let mut formal_types = Vec::new();

    while let Some(char) = input.peek() {
        if *char != '>' {
            formal_types.push(formal_type(input)?);
        } else {
            break;
        }
    }

    assert_char(input.next(), '>')?;

    Ok(formal_types)
}

fn formal_type(input: &mut Peekable<Chars>) -> ParseResult<FormalTypeParameter> {
    let parameter_name = identifier(input)?;
    let class_bound = class_bound(input)?;
    let mut interface_bounds = Vec::new();

    while let Some(char) = input.peek() {
        if *char == ':' {
            interface_bounds.push(interface_bound(input)?);
        } else {
            break;
        }
    }

    Ok(FormalTypeParameter {
        parameter_name,
        class_bound,
        interface_bounds,
    })
}

fn result(input: &mut Peekable<Chars>) -> ParseResult<SignatureType> {
    if let Ok(java_type_signature) = java_type_signature(input) {
        Ok(java_type_signature)
    } else if let Ok(void_descriptor) = void_descriptor(input) {
        Ok(SignatureType::BaseType(void_descriptor))
    } else {
        Err(ParseError::OutOfBound("return type"))
    }
}

fn throws_signature(input: &mut Peekable<Chars>) -> ParseResult<ThrowsType> {
    assert_char(input.next(), '^')?;

    if let Ok(class_type_signature) = class_type_signature(input) {
        Ok(ThrowsType::Class(class_type_signature))
    } else if let Ok(type_variable) = type_variable(input) {
        Ok(ThrowsType::TypeVariable(type_variable))
    } else {
        Err(ParseError::OutOfBound("throws signature"))
    }
}

fn class_bound(input: &mut Peekable<Chars>) -> ParseResult<Option<ClassType>> {
    assert_char(input.next(), ':')?;

    match input.peek() {
        Some(char) if *char == 'L' => Ok(Some(class_type_signature(input)?)),
        _ => Ok(None),
    }
}

fn interface_bound(input: &mut Peekable<Chars>) -> ParseResult<ClassType> {
    assert_char(input.next(), ':')?;

    class_type_signature(input)
}

fn java_type_signature(input: &mut Peekable<Chars>) -> ParseResult<SignatureType> {
    if let Ok(base_type) = base_type(input) {
        Ok(SignatureType::BaseType(base_type))
    } else if let Ok(reference_type) = reference_type(input) {
        Ok(SignatureType::ReferenceType(reference_type))
    } else {
        Err(ParseError::OutOfBound("java type signature"))
    }
}

fn base_type(input: &mut Peekable<Chars>) -> ParseResult<BaseType> {
    let Some(char) = input.peek() else {
        return Err(ParseError::OutOfBound("base type"));
    };

    for base_type_char in "ZBSIJFD".chars() {
        if *char == base_type_char {
            let char = input.next().unwrap();

            return BaseType::try_from(char).map_err(|_| {
                ParseError::MismatchedCharacter(char, vec!['Z', 'B', 'S', 'I', 'J', 'F', 'D'])
            });
        }
    }

    Err(ParseError::MismatchedCharacter(
        *char,
        vec!['Z', 'B', 'S', 'I', 'J', 'F', 'D'],
    ))
}

fn void_descriptor(input: &mut Peekable<Chars>) -> ParseResult<BaseType> {
    assert_char(input.next(), 'V')?;

    Ok(BaseType::Void)
}

fn reference_type(input: &mut Peekable<Chars>) -> ParseResult<ReferenceType> {
    match input.peek() {
        Some(char) if *char == 'L' => Ok(ReferenceType::Class(class_type_signature(input)?)),
        Some(char) if *char == 'T' => Ok(ReferenceType::TypeVariable(type_variable(input)?)),
        Some(char) if *char == '[' => Ok(ReferenceType::Array(array_type(input)?)),
        Some(char) => Err(ParseError::MismatchedCharacter(*char, vec!['L', 'T', '['])),
        None => Err(ParseError::OutOfBound("reference type")),
    }
}

fn class_type_signature(input: &mut Peekable<Chars>) -> ParseResult<ClassType> {
    assert_char(input.next(), 'L')?;

    let (package_path, class_name) = package_specifier_and_class_type(input)?;
    let type_arguments = type_arguments(input)?;
    let mut inner_classes = Vec::new();

    while let Some(char) = input.peek() {
        if *char != ';' {
            inner_classes.push(class_type_signature_suffix(input)?);
        } else {
            break;
        }
    }

    assert_char(input.next(), ';')?;

    Ok(ClassType {
        package_path,
        class_name,
        type_arguments,
        inner_classes,
    })
}

fn package_specifier_and_class_type(input: &mut Peekable<Chars>) -> ParseResult<(String, String)> {
    let mut class_path_builder = Vec::with_capacity(3);

    loop {
        class_path_builder.push(identifier(input)?);

        match input.peek() {
            Some(builder) if *builder == '/' => {
                class_path_builder.push(input.next().unwrap().to_string());
                continue;
            }
            _ => break,
        }
    }

    let (class_type, package_specifier) = if class_path_builder.len() == 1 {
        (String::new(), class_path_builder[0].clone())
    } else if class_path_builder.len() == 0 {
        return Err(ParseError::OutOfBound("package specifier and class type"));
    } else {
        class_path_builder
            .split_last()
            .map(|(class_type, package_specifier)| {
                (class_type.to_string(), package_specifier.join(""))
            })
            .unwrap()
    };

    Ok((package_specifier, class_type))
}

fn class_type_signature_suffix(
    input: &mut Peekable<Chars>,
) -> ParseResult<(String, Vec<TypeArgument>)> {
    assert_char(input.next(), '.')?;

    let class_type = identifier(input)?;
    let type_arguments = type_arguments(input)?;

    Ok((class_type, type_arguments))
}

fn type_arguments(input: &mut Peekable<Chars>) -> ParseResult<Vec<TypeArgument>> {
    match input.peek() {
        Some(char) if *char == '<' => {
            assert_char(input.next(), '<')?;

            let mut type_arguments = Vec::new();

            while let Some(char) = input.peek() {
                if *char != '>' {
                    type_arguments.push(type_argument(input)?);
                } else {
                    break;
                }
            }

            assert_char(input.next(), '>')?;

            Ok(type_arguments)
        }
        _ => Ok(Vec::new()),
    }
}

fn type_argument(input: &mut Peekable<Chars>) -> ParseResult<TypeArgument> {
    match input.peek() {
        Some(char) if *char == '+' || *char == '-' => {
            let wildcard_indicator = WildcardIndicator::try_from(char).unwrap();
            let bounded_type = reference_type(input)?;

            Ok(TypeArgument::Bounded {
                wildcard_indicator,
                bounded_type,
            })
        }
        Some(char) if *char == '*' => Ok(TypeArgument::Wildcard),
        Some(char) => Err(ParseError::MismatchedCharacter(*char, vec!['+', '-', '*'])),
        None => Err(ParseError::OutOfBound("type_argument")),
    }
}

fn type_variable(input: &mut Peekable<Chars>) -> ParseResult<TypeVariable> {
    assert_char(input.next(), 'T')?;

    let identifier = identifier(input)?;

    assert_char(input.next(), ';')?;

    Ok(TypeVariable(identifier))
}

fn array_type(input: &mut Peekable<Chars>) -> ParseResult<ArrayType> {
    assert_char(input.next(), '[')?;

    let inner_type = java_type_signature(input)?;

    Ok(ArrayType(Box::new(inner_type)))
}

fn identifier(input: &mut Peekable<Chars>) -> ParseResult<String> {
    let mut identifier_builder = String::with_capacity(8);

    while let Some(char) = input.peek() {
        if !EXCLUDED_IDENTIFIER_CHARACTERS.contains(*char) {
            identifier_builder.push(input.next().unwrap());
        } else {
            break;
        }
    }

    Ok(identifier_builder)
}

#[inline]
fn assert_char(char: Option<char>, expected: char) -> ParseResult<char> {
    match char {
        Some(char) => {
            if char == expected {
                Ok(char)
            } else {
                Err(ParseError::MismatchedCharacter(char, vec![expected]))
            }
        }
        None => Err(ParseError::OutOfBound("signature")),
    }
}