1use std::{fmt, str::FromStr};
2
3use combine::{
4 between, many, many1, parser, satisfy, token, ParseError, Parser, StdParseResult, Stream,
5};
6
7use crate::errors::*;
8
9#[allow(missing_docs)]
12#[derive(Eq, PartialEq, Debug, Clone, Copy)]
13pub enum Primitive {
14 Boolean, Byte, Char, Double, Float, Int, Long, Short, Void, }
24
25impl fmt::Display for Primitive {
26 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27 match *self {
28 Primitive::Boolean => write!(f, "Z"),
29 Primitive::Byte => write!(f, "B"),
30 Primitive::Char => write!(f, "C"),
31 Primitive::Double => write!(f, "D"),
32 Primitive::Float => write!(f, "F"),
33 Primitive::Int => write!(f, "I"),
34 Primitive::Long => write!(f, "J"),
35 Primitive::Short => write!(f, "S"),
36 Primitive::Void => write!(f, "V"),
37 }
38 }
39}
40
41#[allow(missing_docs)]
43#[derive(Eq, PartialEq, Debug, Clone)]
44pub enum JavaType {
45 Primitive(Primitive),
46 Object(String),
47 Array(Box<JavaType>),
48 Method(Box<TypeSignature>),
49}
50
51impl FromStr for JavaType {
52 type Err = Error;
53
54 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
55 parser(parse_type)
56 .parse(s)
57 .map(|res| res.0)
58 .map_err(|e| Error::ParseFailed(e, s.to_owned()))
59 }
60}
61
62impl fmt::Display for JavaType {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 match *self {
65 JavaType::Primitive(ref ty) => ty.fmt(f),
66 JavaType::Object(ref name) => write!(f, "L{name};"),
67 JavaType::Array(ref ty) => write!(f, "[{ty}"),
68 JavaType::Method(ref m) => m.fmt(f),
69 }
70 }
71}
72
73#[allow(missing_docs)]
79#[derive(Eq, PartialEq, Debug, Clone)]
80pub enum ReturnType {
81 Primitive(Primitive),
82 Object,
83 Array,
84}
85
86impl FromStr for ReturnType {
87 type Err = Error;
88
89 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
90 parser(parse_return)
91 .parse(s)
92 .map(|res| res.0)
93 .map_err(|e| Error::ParseFailed(e, s.to_owned()))
94 }
95}
96
97impl fmt::Display for ReturnType {
98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 match *self {
100 ReturnType::Primitive(ref ty) => ty.fmt(f),
101 ReturnType::Object => write!(f, "L;"),
102 ReturnType::Array => write!(f, "["),
103 }
104 }
105}
106
107#[allow(missing_docs)]
111#[derive(Eq, PartialEq, Debug, Clone)]
112pub struct TypeSignature {
113 pub args: Vec<JavaType>,
114 pub ret: ReturnType,
115}
116
117impl TypeSignature {
118 #[allow(clippy::should_implement_trait)]
121 pub fn from_str<S: AsRef<str>>(s: S) -> Result<TypeSignature> {
122 Ok(match parser(parse_sig).parse(s.as_ref()).map(|res| res.0) {
123 Ok(JavaType::Method(sig)) => *sig,
124 Err(e) => return Err(Error::ParseFailed(e, s.as_ref().to_owned())),
125 _ => unreachable!(),
126 })
127 }
128}
129
130impl fmt::Display for TypeSignature {
131 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
132 write!(f, "(")?;
133 for a in &self.args {
134 write!(f, "{a}")?;
135 }
136 write!(f, ")")?;
137 write!(f, "{}", self.ret)?;
138 Ok(())
139 }
140}
141
142fn parse_primitive<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<Primitive, S>
143where
144 S::Error: ParseError<char, S::Range, S::Position>,
145{
146 let boolean = token('Z').map(|_| Primitive::Boolean);
147 let byte = token('B').map(|_| Primitive::Byte);
148 let char_type = token('C').map(|_| Primitive::Char);
149 let double = token('D').map(|_| Primitive::Double);
150 let float = token('F').map(|_| Primitive::Float);
151 let int = token('I').map(|_| Primitive::Int);
152 let long = token('J').map(|_| Primitive::Long);
153 let short = token('S').map(|_| Primitive::Short);
154 let void = token('V').map(|_| Primitive::Void);
155
156 (boolean
157 .or(byte)
158 .or(char_type)
159 .or(double)
160 .or(float)
161 .or(int)
162 .or(long)
163 .or(short)
164 .or(void))
165 .parse_stream(input)
166 .into()
167}
168
169fn parse_array<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S>
170where
171 S::Error: ParseError<char, S::Range, S::Position>,
172{
173 let marker = token('[');
174 (marker, parser(parse_type))
175 .map(|(_, ty)| JavaType::Array(Box::new(ty)))
176 .parse_stream(input)
177 .into()
178}
179
180fn parse_object<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S>
181where
182 S::Error: ParseError<char, S::Range, S::Position>,
183{
184 let marker = token('L');
185 let end = token(';');
186 let obj = between(marker, end, many1(satisfy(|c| c != ';')));
187
188 obj.map(JavaType::Object).parse_stream(input).into()
189}
190
191fn parse_type<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S>
192where
193 S::Error: ParseError<char, S::Range, S::Position>,
194{
195 parser(parse_primitive)
196 .map(JavaType::Primitive)
197 .or(parser(parse_array))
198 .or(parser(parse_object))
199 .or(parser(parse_sig))
200 .parse_stream(input)
201 .into()
202}
203
204fn parse_return<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<ReturnType, S>
205where
206 S::Error: ParseError<char, S::Range, S::Position>,
207{
208 parser(parse_primitive)
209 .map(ReturnType::Primitive)
210 .or(parser(parse_array).map(|_| ReturnType::Array))
211 .or(parser(parse_object).map(|_| ReturnType::Object))
212 .parse_stream(input)
213 .into()
214}
215
216fn parse_args<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<Vec<JavaType>, S>
217where
218 S::Error: ParseError<char, S::Range, S::Position>,
219{
220 between(token('('), token(')'), many(parser(parse_type)))
221 .parse_stream(input)
222 .into()
223}
224
225fn parse_sig<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S>
226where
227 S::Error: ParseError<char, S::Range, S::Position>,
228{
229 (parser(parse_args), parser(parse_return))
230 .map(|(a, r)| TypeSignature { args: a, ret: r })
231 .map(|sig| JavaType::Method(Box::new(sig)))
232 .parse_stream(input)
233 .into()
234}
235
236#[cfg(test)]
237mod test {
238 use super::*;
239
240 #[test]
241 fn test_parser() {
242 let inputs = [
243 "(Ljava/lang/String;I)V",
244 "[Lherp;",
245 ];
248
249 for each in inputs.iter() {
250 let res = JavaType::from_str(each).unwrap();
251 println!("{res:#?}");
252 let s = format!("{res}");
253 assert_eq!(s, *each);
254 let res2 = JavaType::from_str(each).unwrap();
255 println!("{res2:#?}");
256 assert_eq!(res2, res);
257 }
258 }
259
260 #[test]
261 fn test_parser_invalid_signature() {
262 let signature = "()Ljava/lang/List"; let res = JavaType::from_str(signature);
264
265 match res {
266 Ok(any) => {
267 panic!("Unexpected result: {}", any);
268 }
269 Err(err) => {
270 assert!(err.to_string().contains("input: ()Ljava/lang/List"));
271 }
272 }
273 }
274}