1#![no_std]
26
27extern crate alloc;
28extern crate std;
29
30mod class;
31mod method;
32mod ty;
33
34use core::{convert::Infallible, fmt::Debug};
35
36pub use class::*;
37pub use method::*;
38pub use ty::*;
39
40pub trait Parse<'s>: Sized {
42 type Error;
44
45 #[allow(clippy::missing_errors_doc)]
47 fn parse_from(cursor: &mut Cursor<'s>) -> Result<Self, Self::Error>;
48}
49
50#[non_exhaustive]
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53pub enum ReprForm {
54 JLS,
56 Internal,
61}
62
63impl ReprForm {
64 #[inline]
65 const fn package_separator(&self) -> char {
66 match self {
67 Self::JLS => '.',
68 Self::Internal => '/',
69 }
70 }
71}
72
73#[derive(Debug, Clone)]
75pub struct Cursor<'a>(&'a str);
76
77impl<'a> Cursor<'a> {
78 #[inline]
80 pub const fn new(s: &'a str) -> Self {
81 Self(s)
82 }
83
84 pub fn get_char(&mut self) -> char {
90 let c = self.0.chars().next().expect("end of file");
91 self.advance_by(c.len_utf8());
92 c
93 }
94
95 #[inline]
97 #[allow(clippy::missing_panics_doc)] pub fn advance<F, U>(&mut self, f: F) -> U
99 where
100 F: FnOnce(&'a str) -> (U, &'a str),
101 {
102 self.try_advance(|s| Result::<_, Infallible>::Ok(f(s)))
103 .unwrap()
104 }
105
106 #[inline]
112 pub fn try_advance<F, U, Err>(&mut self, f: F) -> Result<U, Err>
113 where
114 F: FnOnce(&'a str) -> Result<(U, &'a str), Err>,
115 {
116 let (ret, leftover) = f(self.0)?;
117 self.0 = leftover;
118 Ok(ret)
119 }
120
121 #[inline]
123 pub const fn get(&self) -> &'a str {
124 self.0
125 }
126
127 #[inline]
129 pub const fn set(&mut self, s: &'a str) {
130 self.0 = s;
131 }
132
133 #[inline]
139 pub fn advance_by(&mut self, n: usize) {
140 self.advance(|s| ((), &s[n..]))
141 }
142
143 #[inline]
145 pub fn clear(&mut self) {
146 self.0 = "";
147 }
148}
149
150fn strip_digits_prefix(s: &str) -> (Option<u32>, &str) {
151 let mut chars = s.char_indices();
152 let (digits, last_index) = chars
153 .by_ref()
154 .map_while(|(index, c)| c.to_digit(10).map(|d| (index, d)))
155 .fold((0, None), |(n, _), (ci, d)| (n * 10 + d, Some(ci)));
156 let leftover = last_index.map_or(s, |i| &s[i + 1..]);
157 (last_index.map(|_| digits), leftover)
158}
159
160#[allow(clippy::missing_errors_doc)]
162#[inline]
163pub fn parse<'a, T>(value: &'a str) -> Result<T, T::Error>
164where
165 T: Parse<'a>,
166{
167 T::parse_from(&mut Cursor(value))
168}
169
170#[cfg(test)]
171fn validate_rw<'a, T>(value: &'a str)
172where
173 T: Parse<'a> + core::fmt::Display,
174 T::Error: Debug,
175{
176 use core::marker::PhantomData;
177
178 use alloc::string::String;
179
180 struct ClearValidator<T>(String, PhantomData<T>);
181
182 impl<'a, T> Parse<'a> for ClearValidator<T>
183 where
184 T: Parse<'a> + core::fmt::Display,
185 {
186 type Error = T::Error;
187
188 fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
189 use alloc::string::ToString as _;
190
191 let val = T::parse_from(cursor)?;
192 assert_eq!(cursor.get(), "", "non-empty string buf left");
193 Ok(Self(val.to_string(), PhantomData))
194 }
195 }
196
197 assert_eq!(
198 parse::<'a, ClearValidator<T>>(value).map(|o| o.0).unwrap(),
199 value
200 );
201}