Skip to main content

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//! - Type signatures: [`TypeSignature`]
24
25#![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
40/// Types that could be parsed from a borrowed string cursor.
41pub trait Parse<'s>: Sized {
42    /// The error type.
43    type Error;
44
45    /// Parse from the given string cursor.
46    #[allow(clippy::missing_errors_doc)]
47    fn parse_from(cursor: &mut Cursor<'s>) -> Result<Self, Self::Error>;
48}
49
50/// Representation form of a component across different contexts.
51#[non_exhaustive]
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53pub enum ReprForm {
54    /// JLS-specified standard binary name format.
55    JLS,
56    /// Internal representation used by class files.
57    ///
58    /// See [JVMS 4.2.1](https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.2.1)
59    /// for more information about internal form of a class, interface or package.
60    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/// Cursor of a string slice.
74#[derive(Debug, Clone)]
75pub struct Cursor<'a>(&'a str);
76
77impl<'a> Cursor<'a> {
78    /// Creates a new string cursor from slice.
79    #[inline]
80    pub const fn new(s: &'a str) -> Self {
81        Self(s)
82    }
83
84    /// Reads a single character.
85    ///
86    /// # Panics
87    ///
88    /// Panics if there's no valid character left in this slice.
89    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    /// Advances the inner cursor with given function.
96    #[inline]
97    #[allow(clippy::missing_panics_doc)] // infalliable
98    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    /// Advances the inner cursor if successed.
107    ///
108    /// # Errors
109    ///
110    /// Returns an error if the given function returns one.
111    #[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    /// Returns the underlying slice.
122    #[inline]
123    pub const fn get(&self) -> &'a str {
124        self.0
125    }
126
127    /// Overrides the underlying slice.
128    #[inline]
129    pub const fn set(&mut self, s: &'a str) {
130        self.0 = s;
131    }
132
133    /// Advances the inner cursor by `n` bytes.
134    ///
135    /// # Panics
136    ///
137    /// Panics if the inner slice doesn't has `n` remaining bytes.
138    #[inline]
139    pub fn advance_by(&mut self, n: usize) {
140        self.advance(|s| ((), &s[n..]))
141    }
142
143    /// Discards this cursor.
144    #[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/// Parses a name.
161#[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}