jaust 0.1.2

Java ecosystem tools in rust a learning project
Documentation
use super::attributes::AttStart;

use crate::class_file::{constant_pool::ConstantPool, file_reader::FileReader};

use anyhow::Result;

#[derive(Debug)]
pub struct RuntimeVisibleAnnotationsAttribute {
    attribute_name_index: u16,
    attribute_length: u32,
    annotations: Vec<Annotation>,
}

#[derive(Debug)]
pub struct Annotation {
    type_index: u16,
    element_value_pairs: Vec<ElementValuePair>,
}

#[derive(Debug)]
pub struct ElementValuePair {
    element_name_index: u16,
    value: ElementValue,
}

#[derive(Debug)]
pub enum ElementValue {
    ConstValueIndex(u16),
    EnumConstValue {
        type_name_index: u16,
        const_name_index: u16,
    },
    ClassInfoIndex(u16),
    AnnotationValue(Annotation),
    ArrayValue(Vec<ElementValue>),
}

impl RuntimeVisibleAnnotationsAttribute {
    pub fn parse(
        file: &mut FileReader,
        att_start: &AttStart,
    ) -> Result<RuntimeVisibleAnnotationsAttribute> {
        let num_annotations = file.read_u2_to_u16()?;
        let mut annotations = Vec::new();
        for _i in 0..num_annotations {
            let annotation = Annotation::parse(file)?;
            annotations.push(annotation);
        }
        Ok(RuntimeVisibleAnnotationsAttribute {
            attribute_name_index: att_start.attribute_name_index,
            attribute_length: att_start.attribute_length,
            annotations,
        })
    }

    pub fn to_string(&self, cp: &ConstantPool) -> String {
        let mut s = String::new();
        s.push_str("RuntimeVisibleAnnotations: ");
        for annotation in &self.annotations {
            s.push_str(&annotation.to_string(cp));
            s.push_str(", ");
        }
        s
    }
}

impl Annotation {
    pub fn parse(file: &mut FileReader) -> Result<Annotation> {
        let type_index = file.read_u2_to_u16()?;
        let num_pairs = file.read_u2_to_u16()?;
        let mut element_value_pairs = Vec::new();
        for _i in 0..num_pairs {
            let element_name_index = file.read_u2_to_u16()?;
            let value = ElementValue::parse(file)?;
            element_value_pairs.push(ElementValuePair {
                element_name_index,
                value,
            });
        }
        Ok(Annotation {
            type_index,
            element_value_pairs,
        })
    }

    pub fn to_string(&self, cp: &ConstantPool) -> String {
        let mut s = String::new();
        s.push_str("@");
        s.push_str(&cp.get_to_string(self.type_index));
        s.push_str("(");
        for pair in &self.element_value_pairs {
            s.push_str(&cp.get_to_string(pair.element_name_index));
            s.push_str(" = ");
            s.push_str(&pair.value.to_string(cp));
            s.push_str(", ");
        }
        s.push_str(")");
        s
    }
}

impl ElementValue {
    pub fn parse(file: &mut FileReader) -> Result<ElementValue> {
        let tag = file.read_u1()?;
        match tag {
            b'B' => Ok(ElementValue::ConstValueIndex(file.read_u2_to_u16()?)),
            b'C' => Ok(ElementValue::ConstValueIndex(file.read_u2_to_u16()?)),
            b'D' => Ok(ElementValue::ConstValueIndex(file.read_u2_to_u16()?)),
            b'F' => Ok(ElementValue::ConstValueIndex(file.read_u2_to_u16()?)),
            b'I' => Ok(ElementValue::ConstValueIndex(file.read_u2_to_u16()?)),
            b'J' => Ok(ElementValue::ConstValueIndex(file.read_u2_to_u16()?)),
            b'S' => Ok(ElementValue::ConstValueIndex(file.read_u2_to_u16()?)),
            b'Z' => Ok(ElementValue::ConstValueIndex(file.read_u2_to_u16()?)),
            b's' => Ok(ElementValue::ConstValueIndex(file.read_u2_to_u16()?)),
            b'e' => {
                let type_name_index = file.read_u2_to_u16()?;
                let const_name_index = file.read_u2_to_u16()?;
                Ok(ElementValue::EnumConstValue {
                    type_name_index,
                    const_name_index,
                })
            }
            b'c' => Ok(ElementValue::ClassInfoIndex(file.read_u2_to_u16()?)),
            b'@' => {
                let annotation = Annotation::parse(file)?;
                Ok(ElementValue::AnnotationValue(annotation))
            }
            b'[' => {
                let num_values = file.read_u2_to_u16()?;
                let mut values = Vec::new();
                for _i in 0..num_values {
                    let value = ElementValue::parse(file)?;
                    values.push(value);
                }
                Ok(ElementValue::ArrayValue(values))
            }
            _ => anyhow::bail!("Invalid tag: {}", tag),
        }
    }

    pub fn to_string(&self, cp: &ConstantPool) -> String {
        match self {
            ElementValue::ConstValueIndex(index) => cp.get_to_string(*index),
            ElementValue::EnumConstValue {
                type_name_index,
                const_name_index,
            } => {
                format!(
                    "{}::{}",
                    cp.get_to_string(*type_name_index),
                    cp.get_to_string(*const_name_index)
                )
            }
            ElementValue::ClassInfoIndex(index) => cp.get_to_string(*index),
            ElementValue::AnnotationValue(annotation) => annotation.to_string(cp),
            ElementValue::ArrayValue(values) => {
                let mut s = String::new();
                s.push_str("{");
                for value in values {
                    s.push_str(&value.to_string(cp));
                    s.push_str(", ");
                }
                s.push_str("}");
                s
            }
        }
    }
}