java_bin_name/
lib.rs

1//! Java binary name parser and writer.
2//!
3//! # Parsing
4//!
5//! Types who implemented `Parse` trait could be parsed from a string slice using [`parse`] function.
6//!
7//! Differences between JLS and class file representation will be handled automatically.
8//!
9//! ## Error Handling
10//!
11//! This crate won't handle naming errors unless necessary.
12//! Validate the string slices after parse if desired.
13//!
14//! # Writing
15//!
16//! Use `Display` or `ToString` for writing.
17//!
18//! # Supported Items
19//!
20//! - Classes or interfaces: [`ClassName`]
21//! - Methods: [`MethodDescriptor`]
22//! - Field types: [`FieldType`]
23
24mod class;
25mod method;
26mod ty;
27
28use std::convert::Infallible;
29
30pub use class::*;
31pub use method::*;
32pub use ty::*;
33
34/// Types that could be parsed from a borrowed string cursor.
35pub trait Parse<'s>: Sized {
36    /// The error type.
37    type Error;
38
39    /// Parse from the given string cursor.
40    #[allow(clippy::missing_errors_doc)]
41    fn parse_from(cursor: &mut Cursor<'s>) -> Result<Self, Self::Error>;
42}
43
44/// Representation form of a component across different contexts.
45#[non_exhaustive]
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
47pub enum ReprForm {
48    /// JLS-specified standard binary name format.
49    JLS,
50    /// Internal representation used by class files.
51    ///
52    /// See [JVMS 4.2.1](https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.2.1)
53    /// for more information about internal form of a class, interface or package.
54    Internal,
55}
56
57impl ReprForm {
58    const fn package_separator(&self) -> char {
59        match self {
60            Self::JLS => '.',
61            Self::Internal => '/',
62        }
63    }
64}
65
66/// Cursor of a string slice.
67#[derive(Debug, Clone)]
68pub struct Cursor<'a>(&'a str);
69
70impl<'a> Cursor<'a> {
71    /// Creates a new string cursor from slice.
72    #[inline]
73    pub const fn new(s: &'a str) -> Self {
74        Self(s)
75    }
76
77    /// Reads a single character.
78    ///
79    /// # Panics
80    ///
81    /// Panics if there's no valid character left in this slice.
82    pub fn get_char(&mut self) -> char {
83        let c = self.0.chars().next().expect("end of file");
84        self.advance_by(c.len_utf8());
85        c
86    }
87
88    /// Advances the inner cursor with given function.
89    #[inline]
90    #[allow(clippy::missing_panics_doc)] // infalliable
91    pub fn advance<F, U>(&mut self, f: F) -> U
92    where
93        F: FnOnce(&'a str) -> (U, &'a str),
94    {
95        self.try_advance(|s| Result::<_, Infallible>::Ok(f(s)))
96            .unwrap()
97    }
98
99    /// Advances the inner cursor if successed.
100    ///
101    /// # Errors
102    ///
103    /// Returns an error if the given function returns one.
104    #[inline]
105    pub fn try_advance<F, U, Err>(&mut self, f: F) -> Result<U, Err>
106    where
107        F: FnOnce(&'a str) -> Result<(U, &'a str), Err>,
108    {
109        let (ret, leftover) = f(self.0)?;
110        self.0 = leftover;
111        Ok(ret)
112    }
113
114    /// Returns the underlying slice.
115    #[inline]
116    pub const fn get(&self) -> &'a str {
117        self.0
118    }
119
120    /// Overrides the underlying slice.
121    #[inline]
122    pub const fn set(&mut self, s: &'a str) {
123        self.0 = s;
124    }
125
126    /// Advances the inner cursor by `n` bytes.
127    ///
128    /// # Panics
129    ///
130    /// Panics if the inner slice doesn't has `n` remaining bytes.
131    #[inline]
132    pub fn advance_by(&mut self, n: usize) {
133        self.advance(|s| ((), &s[n..]))
134    }
135
136    /// Discards this cursor.
137    #[inline]
138    pub fn clear(&mut self) {
139        self.0 = "";
140    }
141}
142
143fn strip_digits_prefix(s: &str) -> (Option<u32>, &str) {
144    let mut chars = s.char_indices();
145    let (digits, last_index) = chars
146        .by_ref()
147        .map_while(|(index, c)| c.to_digit(10).map(|d| (index, d)))
148        .fold((0, None), |(n, _), (ci, d)| (n * 10 + d, Some(ci)));
149    let leftover = last_index.map_or(s, |i| &s[i + 1..]);
150    (last_index.map(|_| digits), leftover)
151}
152
153/// Parses a name.
154#[allow(clippy::missing_errors_doc)]
155#[inline]
156pub fn parse<'a, T>(value: &'a str) -> Result<T, T::Error>
157where
158    T: Parse<'a>,
159{
160    T::parse_from(&mut Cursor(value))
161}
162
163#[cfg(test)]
164fn validate_rw<'a, T>(value: &'a str)
165where
166    T: Parse<'a> + std::fmt::Display,
167    T::Error: std::fmt::Debug,
168{
169    use std::marker::PhantomData;
170
171    struct ClearValidator<T>(String, PhantomData<T>);
172
173    impl<'a, T> Parse<'a> for ClearValidator<T>
174    where
175        T: Parse<'a> + std::fmt::Display,
176    {
177        type Error = T::Error;
178
179        fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
180            let val = T::parse_from(cursor)?;
181            assert_eq!(cursor.get(), "", "non-empty string buf left");
182            Ok(Self(val.to_string(), PhantomData))
183        }
184    }
185
186    assert_eq!(
187        parse::<'a, ClearValidator<T>>(value).map(|o| o.0).unwrap(),
188        value
189    );
190}