#![no_std]
extern crate alloc;
mod class;
mod method;
mod ty;
use core::{convert::Infallible, fmt::Debug};
pub use class::*;
pub use method::*;
pub use ty::*;
pub type FieldSignature<'a> = TypeSignature<'a>;
pub trait Parse<'s>: Sized {
type Error;
#[allow(clippy::missing_errors_doc)]
fn parse_from(cursor: &mut Cursor<'s>) -> Result<Self, Self::Error>;
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ReprForm {
JLS,
Internal,
}
impl ReprForm {
#[inline]
const fn package_separator(&self) -> char {
match self {
Self::JLS => '.',
Self::Internal => '/',
}
}
}
#[derive(Debug, Clone)]
pub struct Cursor<'a>(&'a str);
impl<'a> Cursor<'a> {
#[inline]
pub const fn new(s: &'a str) -> Self {
Self(s)
}
pub fn get_char(&mut self) -> char {
let c = self.0.chars().next().expect("end of file");
self.advance_by(c.len_utf8());
c
}
#[inline]
#[allow(clippy::missing_panics_doc)] pub fn advance<F, U>(&mut self, f: F) -> U
where
F: FnOnce(&'a str) -> (U, &'a str),
{
self.try_advance(|s| Result::<_, Infallible>::Ok(f(s)))
.unwrap()
}
#[inline]
pub fn try_advance<F, U, Err>(&mut self, f: F) -> Result<U, Err>
where
F: FnOnce(&'a str) -> Result<(U, &'a str), Err>,
{
let (ret, leftover) = f(self.0)?;
self.0 = leftover;
Ok(ret)
}
#[inline]
pub const fn get(&self) -> &'a str {
self.0
}
#[inline]
pub const fn set(&mut self, s: &'a str) {
self.0 = s;
}
#[inline]
pub fn advance_by(&mut self, n: usize) {
self.advance(|s| ((), &s[n..]))
}
#[inline]
pub fn clear(&mut self) {
self.0 = "";
}
}
fn strip_digits_prefix(s: &str) -> (Option<u32>, &str) {
let mut chars = s.char_indices();
let (digits, last_index) = chars
.by_ref()
.map_while(|(index, c)| c.to_digit(10).map(|d| (index, d)))
.fold((0, None), |(n, _), (ci, d)| (n * 10 + d, Some(ci)));
let leftover = last_index.map_or(s, |i| &s[i + 1..]);
(last_index.map(|_| digits), leftover)
}
fn angle_safe_offset(src: &str, sep: &[char]) -> usize {
if let Some((a, b)) = src.split_once('<')
&& !a.contains(sep)
{
let mut rem = b;
let mut layers = 1usize;
while layers > 0 {
let Some(pos) = rem.find(['<', '>']) else {
return 0;
};
match rem.as_bytes()[pos] {
b'<' => layers += 1,
b'>' => layers -= 1,
_ => unreachable!(),
}
rem = &rem[pos + 1..];
}
unsafe { rem.as_ptr().byte_offset_from_unsigned(src.as_ptr()) }
} else {
0
}
}
fn angle_safe_split<'a>(src: &'a str, sep: &[char]) -> Option<(&'a str, &'a str)> {
let offset = angle_safe_offset(src, sep);
Some(src.split_at(offset + src[offset..].find(sep)?))
}
fn angle_safe_rsplit<'a>(src: &'a str, sep: &[char]) -> Option<(&'a str, &'a str)> {
let offset = angle_safe_offset(src, sep);
Some(src.split_at(offset + src[offset..].find(sep)? + 1))
}
#[allow(clippy::missing_errors_doc)]
#[inline]
pub fn parse<'a, T>(value: &'a str) -> Result<T, T::Error>
where
T: Parse<'a>,
{
T::parse_from(&mut Cursor(value))
}
#[cfg(test)]
fn validate_rw<'a, T>(value: &'a str)
where
T: Parse<'a> + core::fmt::Display,
T::Error: Debug,
{
use core::marker::PhantomData;
use alloc::string::String;
struct ClearValidator<T>(String, PhantomData<T>);
impl<'a, T> Parse<'a> for ClearValidator<T>
where
T: Parse<'a> + core::fmt::Display,
{
type Error = T::Error;
fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
use alloc::string::ToString as _;
let val = T::parse_from(cursor)?;
assert_eq!(cursor.get(), "", "non-empty string buf left");
Ok(Self(val.to_string(), PhantomData))
}
}
assert_eq!(
parse::<'a, ClearValidator<T>>(value).map(|o| o.0).unwrap(),
value
);
}