1use std::fmt::{Debug, Display};
2
3use crate::{Cursor, Parse, class::ClassName};
4
5#[derive(Debug, Clone)]
7pub struct UnknownFieldType(char);
8
9impl std::error::Error for UnknownFieldType {}
10
11impl Display for UnknownFieldType {
12 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
13 write!(f, "unknown field type '{}'", self.0)
14 }
15}
16
17#[derive(Clone, PartialEq, Eq, Hash)]
21#[allow(missing_docs)] pub enum FieldType<'a> {
23 Byte,
24 Char,
25 Double,
26 Float,
27 Int,
28 Long,
29 Short,
30 Boolean,
31 Class(Box<ClassName<'a>>),
32 Array(Box<Self>),
33}
34
35impl Display for FieldType<'_> {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 match self {
38 Self::Byte => write!(f, "B"),
39 Self::Char => write!(f, "C"),
40 Self::Double => write!(f, "D"),
41 Self::Float => write!(f, "F"),
42 Self::Int => write!(f, "I"),
43 Self::Long => write!(f, "J"),
44 Self::Short => write!(f, "S"),
45 Self::Boolean => write!(f, "Z"),
46 Self::Class(class_name) => write!(f, "L{class_name};"),
47 Self::Array(field_ty) => write!(f, "[{field_ty}"),
48 }
49 }
50}
51
52impl<'a> Parse<'a> for FieldType<'a> {
53 type Error = UnknownFieldType;
54
55 fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
56 let leading = cursor.get_char();
57 match leading {
58 'B' => Ok(Self::Byte),
59 'C' => Ok(Self::Char),
60 'D' => Ok(Self::Double),
61 'F' => Ok(Self::Float),
62 'I' => Ok(Self::Int),
63 'J' => Ok(Self::Long),
64 'S' => Ok(Self::Short),
65 'Z' => Ok(Self::Boolean),
66 'L' => cursor
67 .try_advance(|s| s.split_once(';').ok_or(UnknownFieldType('L')))
68 .map(|s| {
69 Self::Class(Box::new(
70 ClassName::parse_from(&mut Cursor::new(s)).unwrap(),
71 ))
72 }),
73 '[' => Self::parse_from(cursor).map(|t| Self::Array(Box::new(t))),
74 _ => Err(UnknownFieldType(leading)),
75 }
76 }
77}
78
79impl Debug for FieldType<'_> {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 match self {
82 Self::Byte => write!(f, "byte"),
83 Self::Char => write!(f, "char"),
84 Self::Double => write!(f, "double"),
85 Self::Float => write!(f, "float"),
86 Self::Int => write!(f, "int"),
87 Self::Long => write!(f, "long"),
88 Self::Short => write!(f, "short"),
89 Self::Boolean => write!(f, "boolean"),
90 Self::Class(arg0) => Debug::fmt(arg0, f),
91 Self::Array(arg0) => {
92 Debug::fmt(arg0, f)?;
93 write!(f, "[]")
94 }
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use crate::{CanonicalClassName, ClassName, FieldType, ReprForm, parse, validate_rw};
102
103 #[test]
104 fn primitives() {
105 macro_rules! primitives {
106 ($($v:ident,$l:expr),*$(,)?) => {
107 $(
108 assert_eq!(parse::<'_, FieldType<'_>>($l).unwrap(), FieldType::$v);
109 validate_rw::<'_, FieldType<'_>>($l);
110 )*
111 };
112 }
113
114 primitives! {
115 Byte, "B",
116 Char, "C",
117 Double, "D",
118 Float, "F",
119 Int, "I",
120 Long, "J",
121 Short, "S",
122 Boolean, "Z",
123 }
124 }
125
126 #[test]
127 fn class() {
128 assert_eq!(
129 parse::<'_, FieldType<'_>>("Ljava/lang/Object;").unwrap(),
130 FieldType::Class(Box::new(ClassName::TopLevel(CanonicalClassName {
131 package: Some("java/lang"),
132 simple: "Object",
133 form: ReprForm::Internal,
134 })))
135 );
136 validate_rw::<'_, FieldType<'_>>("Ljava/lang/Object;");
137 }
138
139 #[test]
140 fn array_primitive() {
141 assert_eq!(
142 parse::<'_, FieldType<'_>>("[I").unwrap(),
143 FieldType::Array(Box::new(FieldType::Int))
144 );
145 validate_rw::<'_, FieldType<'_>>("[I");
146 }
147
148 #[test]
149 fn array_primitive_2d() {
150 assert_eq!(
151 parse::<'_, FieldType<'_>>("[[I").unwrap(),
152 FieldType::Array(Box::new(FieldType::Array(Box::new(FieldType::Int))))
153 );
154 validate_rw::<'_, FieldType<'_>>("[[I");
155 }
156
157 #[test]
158 fn array_class() {
159 assert_eq!(
160 parse::<'_, FieldType<'_>>("[Ljava/lang/Object;").unwrap(),
161 FieldType::Array(Box::new(FieldType::Class(Box::new(ClassName::TopLevel(
162 CanonicalClassName {
163 package: Some("java/lang"),
164 simple: "Object",
165 form: ReprForm::Internal,
166 }
167 )))))
168 );
169 validate_rw::<'_, FieldType<'_>>("[Ljava/lang/Object;");
170 }
171
172 #[test]
173 fn array_class_2d() {
174 assert_eq!(
175 parse::<'_, FieldType<'_>>("[[Ljava/lang/Object;").unwrap(),
176 FieldType::Array(Box::new(FieldType::Array(Box::new(FieldType::Class(
177 Box::new(ClassName::TopLevel(CanonicalClassName {
178 package: Some("java/lang"),
179 simple: "Object",
180 form: ReprForm::Internal,
181 }))
182 )))))
183 );
184 validate_rw::<'_, FieldType<'_>>("[[Ljava/lang/Object;");
185 }
186}