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