1use std::fmt;
6use std::fmt::Formatter;
7
8pub mod types;
9
10#[derive(Clone, Copy)]
21pub struct TypeHint {
22 inner: TypeHintExpr,
23}
24
25#[derive(Clone, Copy)]
26enum TypeHintExpr {
27 Local { id: &'static str },
29 Builtin { id: &'static str },
31 ModuleAttribute {
33 module: &'static str,
34 attr: &'static str,
35 },
36 Union { elts: &'static [TypeHint] },
38 Subscript {
40 value: &'static TypeHint,
41 slice: &'static [TypeHint],
42 },
43}
44
45impl TypeHint {
46 pub const fn builtin(name: &'static str) -> Self {
55 Self {
56 inner: TypeHintExpr::Builtin { id: name },
57 }
58 }
59
60 pub const fn module_attr(module: &'static str, attr: &'static str) -> Self {
69 Self {
70 inner: if matches!(module.as_bytes(), b"builtins") {
71 TypeHintExpr::Builtin { id: attr }
72 } else {
73 TypeHintExpr::ModuleAttribute { module, attr }
74 },
75 }
76 }
77
78 #[doc(hidden)]
80 pub const fn local(name: &'static str) -> Self {
81 Self {
82 inner: TypeHintExpr::Local { id: name },
83 }
84 }
85
86 pub const fn union(elts: &'static [TypeHint]) -> Self {
95 Self {
96 inner: TypeHintExpr::Union { elts },
97 }
98 }
99
100 pub const fn subscript(value: &'static Self, slice: &'static [Self]) -> Self {
109 Self {
110 inner: TypeHintExpr::Subscript { value, slice },
111 }
112 }
113}
114
115#[doc(hidden)]
119pub const fn serialize_for_introspection(hint: &TypeHint, mut output: &mut [u8]) -> usize {
120 let original_len = output.len();
121 match &hint.inner {
122 TypeHintExpr::Local { id } => {
123 output = write_slice_and_move_forward(b"{\"type\":\"local\",\"id\":\"", output);
124 output = write_slice_and_move_forward(id.as_bytes(), output);
125 output = write_slice_and_move_forward(b"\"}", output);
126 }
127 TypeHintExpr::Builtin { id } => {
128 output = write_slice_and_move_forward(b"{\"type\":\"builtin\",\"id\":\"", output);
129 output = write_slice_and_move_forward(id.as_bytes(), output);
130 output = write_slice_and_move_forward(b"\"}", output);
131 }
132 TypeHintExpr::ModuleAttribute { module, attr } => {
133 output = write_slice_and_move_forward(b"{\"type\":\"attribute\",\"module\":\"", output);
134 output = write_slice_and_move_forward(module.as_bytes(), output);
135 output = write_slice_and_move_forward(b"\",\"attr\":\"", output);
136 output = write_slice_and_move_forward(attr.as_bytes(), output);
137 output = write_slice_and_move_forward(b"\"}", output);
138 }
139 TypeHintExpr::Union { elts } => {
140 output = write_slice_and_move_forward(b"{\"type\":\"union\",\"elts\":[", output);
141 let mut i = 0;
142 while i < elts.len() {
143 if i > 0 {
144 output = write_slice_and_move_forward(b",", output);
145 }
146 output = write_type_hint_and_move_forward(&elts[i], output);
147 i += 1;
148 }
149 output = write_slice_and_move_forward(b"]}", output);
150 }
151 TypeHintExpr::Subscript { value, slice } => {
152 output = write_slice_and_move_forward(b"{\"type\":\"subscript\",\"value\":", output);
153 output = write_type_hint_and_move_forward(value, output);
154 output = write_slice_and_move_forward(b",\"slice\":[", output);
155 let mut i = 0;
156 while i < slice.len() {
157 if i > 0 {
158 output = write_slice_and_move_forward(b",", output);
159 }
160 output = write_type_hint_and_move_forward(&slice[i], output);
161 i += 1;
162 }
163 output = write_slice_and_move_forward(b"]}", output);
164 }
165 }
166 original_len - output.len()
167}
168
169#[doc(hidden)]
171pub const fn serialized_len_for_introspection(hint: &TypeHint) -> usize {
172 match &hint.inner {
173 TypeHintExpr::Local { id } => 24 + id.len(),
174 TypeHintExpr::Builtin { id } => 26 + id.len(),
175 TypeHintExpr::ModuleAttribute { module, attr } => 42 + module.len() + attr.len(),
176 TypeHintExpr::Union { elts } => {
177 let mut count = 26;
178 let mut i = 0;
179 while i < elts.len() {
180 if i > 0 {
181 count += 1;
182 }
183 count += serialized_len_for_introspection(&elts[i]);
184 i += 1;
185 }
186 count
187 }
188 TypeHintExpr::Subscript { value, slice } => {
189 let mut count = 40 + serialized_len_for_introspection(value);
190 let mut i = 0;
191 while i < slice.len() {
192 if i > 0 {
193 count += 1;
194 }
195 count += serialized_len_for_introspection(&slice[i]);
196 i += 1;
197 }
198 count
199 }
200 }
201}
202
203impl fmt::Display for TypeHint {
204 #[inline]
205 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
206 match &self.inner {
207 TypeHintExpr::Builtin { id } | TypeHintExpr::Local { id } => id.fmt(f),
208 TypeHintExpr::ModuleAttribute { module, attr } => {
209 module.fmt(f)?;
210 f.write_str(".")?;
211 attr.fmt(f)
212 }
213 TypeHintExpr::Union { elts } => {
214 for (i, elt) in elts.iter().enumerate() {
215 if i > 0 {
216 f.write_str(" | ")?;
217 }
218 elt.fmt(f)?;
219 }
220 Ok(())
221 }
222 TypeHintExpr::Subscript { value, slice } => {
223 value.fmt(f)?;
224 f.write_str("[")?;
225 for (i, elt) in slice.iter().enumerate() {
226 if i > 0 {
227 f.write_str(", ")?;
228 }
229 elt.fmt(f)?;
230 }
231 f.write_str("]")
232 }
233 }
234 }
235}
236
237const fn write_slice_and_move_forward<'a>(value: &[u8], output: &'a mut [u8]) -> &'a mut [u8] {
238 let mut i = 0;
240 while i < value.len() {
241 output[i] = value[i];
242 i += 1;
243 }
244 output.split_at_mut(value.len()).1
245}
246
247const fn write_type_hint_and_move_forward<'a>(
248 value: &TypeHint,
249 output: &'a mut [u8],
250) -> &'a mut [u8] {
251 let written = serialize_for_introspection(value, output);
252 output.split_at_mut(written).1
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 #[test]
260 fn test_to_string() {
261 const T: TypeHint = TypeHint::subscript(
262 &TypeHint::builtin("dict"),
263 &[
264 TypeHint::union(&[
265 TypeHint::builtin("int"),
266 TypeHint::module_attr("builtins", "float"),
267 TypeHint::local("weird"),
268 ]),
269 TypeHint::module_attr("datetime", "time"),
270 ],
271 );
272 assert_eq!(T.to_string(), "dict[int | float | weird, datetime.time]")
273 }
274
275 #[test]
276 fn test_serialize_for_introspection() {
277 const T: TypeHint = TypeHint::subscript(
278 &TypeHint::builtin("dict"),
279 &[
280 TypeHint::union(&[TypeHint::builtin("int"), TypeHint::local("weird")]),
281 TypeHint::module_attr("datetime", "time"),
282 ],
283 );
284 const SER_LEN: usize = serialized_len_for_introspection(&T);
285 const SER: [u8; SER_LEN] = {
286 let mut out: [u8; SER_LEN] = [0; SER_LEN];
287 serialize_for_introspection(&T, &mut out);
288 out
289 };
290 assert_eq!(
291 std::str::from_utf8(&SER).unwrap(),
292 r#"{"type":"subscript","value":{"type":"builtin","id":"dict"},"slice":[{"type":"union","elts":[{"type":"builtin","id":"int"},{"type":"local","id":"weird"}]},{"type":"attribute","module":"datetime","attr":"time"}]}"#
293 )
294 }
295}