rusty-javac 0.2.3

A Java compiler written in Rust.
Documentation
use std::fmt;
use ustr::Ustr;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Ty {
    Void,
    Boolean,
    Byte,
    Char,
    Short,
    Int,
    Long,
    Float,
    Double,
    Class(Ustr),
    Array(Box<Ty>),
    TypeVar(Ustr),
    Wildcard(Option<Box<Ty>>),
    Intersection(Vec<Ty>),
}

impl Ty {
    pub fn object() -> Self {
        Ty::Class(Ustr::from("java/lang/Object"))
    }

    pub fn string() -> Self {
        Ty::Class(Ustr::from("java/lang/String"))
    }

    pub fn class(internal_name: impl AsRef<str>) -> Self {
        Ty::Class(Ustr::from(internal_name.as_ref()))
    }

    pub fn is_string(&self) -> bool {
        matches!(self.erasure(), Ty::Class(name) if name.as_str() == "java/lang/String")
    }

    pub fn descriptor(&self) -> String {
        match self {
            Ty::Void => "V".to_string(),
            Ty::Boolean => "Z".to_string(),
            Ty::Byte => "B".to_string(),
            Ty::Char => "C".to_string(),
            Ty::Short => "S".to_string(),
            Ty::Int => "I".to_string(),
            Ty::Long => "J".to_string(),
            Ty::Float => "F".to_string(),
            Ty::Double => "D".to_string(),
            Ty::Class(name) => format!("L{};", name.as_str().replace('.', "/")),
            Ty::Array(elem) => format!("[{}", elem.descriptor()),
            Ty::TypeVar(_) => "Ljava/lang/Object;".to_string(),
            Ty::Wildcard(_) => "Ljava/lang/Object;".to_string(),
            Ty::Intersection(_) => "Ljava/lang/Object;".to_string(),
        }
    }

    pub fn erasure(&self) -> Ty {
        match self {
            Ty::TypeVar(_) => Ty::object(),
            Ty::Wildcard(Some(bound)) => bound.erasure(),
            Ty::Wildcard(None) => Ty::object(),
            Ty::Intersection(types) => types
                .first()
                .map(|t| t.erasure())
                .unwrap_or_else(Ty::object),
            other => other.clone(),
        }
    }

    pub fn is_primitive(&self) -> bool {
        matches!(
            self,
            Ty::Boolean
                | Ty::Byte
                | Ty::Char
                | Ty::Short
                | Ty::Int
                | Ty::Long
                | Ty::Float
                | Ty::Double
        )
    }

    pub fn is_numeric(&self) -> bool {
        matches!(
            self,
            Ty::Byte | Ty::Short | Ty::Int | Ty::Long | Ty::Float | Ty::Double | Ty::Char
        )
    }

    pub fn is_integral(&self) -> bool {
        matches!(self, Ty::Byte | Ty::Short | Ty::Int | Ty::Long | Ty::Char)
    }

    pub fn is_floating(&self) -> bool {
        matches!(self, Ty::Float | Ty::Double)
    }

    pub fn size(&self) -> usize {
        match self {
            Ty::Long | Ty::Double => 2,
            Ty::Void => 0,
            _ => 1,
        }
    }

    pub fn internal_name(&self) -> String {
        match self {
            Ty::Class(name) => name.as_str().to_string(),
            Ty::Array(elem) => elem.descriptor(),
            _ => self.erasure().internal_name(),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TypeParam {
    pub name: Ustr,
    pub bounds: Vec<Ty>,
}

impl fmt::Display for Ty {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Ty::Void => write!(f, "void"),
            Ty::Boolean => write!(f, "boolean"),
            Ty::Byte => write!(f, "byte"),
            Ty::Char => write!(f, "char"),
            Ty::Short => write!(f, "short"),
            Ty::Int => write!(f, "int"),
            Ty::Long => write!(f, "long"),
            Ty::Float => write!(f, "float"),
            Ty::Double => write!(f, "double"),
            Ty::Class(name) => write!(f, "{}", name.as_str().replace('/', ".")),
            Ty::Array(elem) => write!(f, "{}[]", elem),
            Ty::TypeVar(name) => write!(f, "{}", name.as_str()),
            Ty::Wildcard(Some(bound)) => write!(f, "? extends {}", bound),
            Ty::Wildcard(None) => write!(f, "?"),
            Ty::Intersection(types) => {
                let parts: Vec<String> = types.iter().map(|t| t.to_string()).collect();
                write!(f, "{}", parts.join(" & "))
            }
        }
    }
}