1#![doc = include_str!("./../README.md")]
2
3use mago_span::Span;
4use mago_syntax_core::input::Input;
5
6use crate::ast::Type;
7use crate::error::ParseError;
8use crate::lexer::TypeLexer;
9
10pub mod ast;
11pub mod error;
12pub mod lexer;
13pub mod parser;
14pub mod token;
15
16pub fn parse_str(span: Span, input: &str) -> Result<Type<'_>, ParseError> {
33 let input = Input::anchored_at(input.as_bytes(), span.start);
35 let lexer = TypeLexer::new(input);
37 parser::construct(lexer)
39}
40
41#[cfg(test)]
42mod tests {
43 use mago_source::SourceIdentifier;
44 use mago_span::Position;
45 use mago_span::Span;
46
47 use crate::ast::*;
48
49 use super::*;
50
51 fn do_parse(input: &str) -> Result<Type<'_>, ParseError> {
52 let source = SourceIdentifier::dummy();
53 let span = Span::new(Position::new(source, 0), Position::new(source, input.len()));
54 parse_str(span, input)
55 }
56
57 #[test]
58 fn test_parse_simple_keyword() {
59 let result = do_parse("int");
60 assert!(result.is_ok());
61 match result.unwrap() {
62 Type::Int(k) => assert_eq!(k.value, "int"),
63 _ => panic!("Expected Type::Int"),
64 }
65 }
66
67 #[test]
68 fn test_parse_composite_keyword() {
69 let result = do_parse("non-empty-string");
70 assert!(result.is_ok());
71 match result.unwrap() {
72 Type::NonEmptyString(k) => assert_eq!(k.value, "non-empty-string"),
73 _ => panic!("Expected Type::NonEmptyString"),
74 }
75 }
76
77 #[test]
78 fn test_parse_literal_ints() {
79 let assert_parsed_literal_int = |input: &str, expected_value: u64| {
80 let result = do_parse(input);
81 assert!(result.is_ok());
82 match result.unwrap() {
83 Type::LiteralInt(LiteralIntType { value, .. }) => assert_eq!(
84 value, expected_value,
85 "Expected value to be {expected_value} for input {input}, but got {value}"
86 ),
87 _ => panic!("Expected Type::LiteralInt"),
88 }
89 };
90
91 assert_parsed_literal_int("0", 0);
92 assert_parsed_literal_int("1", 1);
93 assert_parsed_literal_int("123_345", 123345);
94 assert_parsed_literal_int("0b1", 1);
95 assert_parsed_literal_int("0o10", 8);
96 assert_parsed_literal_int("0x1", 1);
97 assert_parsed_literal_int("0x10", 16);
98 assert_parsed_literal_int("0xFF", 255);
99 }
100
101 #[test]
102 fn test_parse_literal_floats() {
103 let assert_parsed_literal_float = |input: &str, expected_value: f64| {
104 let result = do_parse(input);
105 assert!(result.is_ok());
106 match result.unwrap() {
107 Type::LiteralFloat(LiteralFloatType { value, .. }) => assert_eq!(
108 value, expected_value,
109 "Expected value to be {expected_value} for input {input}, but got {value}"
110 ),
111 _ => panic!("Expected Type::LiteralInt"),
112 }
113 };
114
115 assert_parsed_literal_float("0.0", 0.0);
116 assert_parsed_literal_float("1.0", 1.0);
117 assert_parsed_literal_float("0.1e1", 1.0);
118 assert_parsed_literal_float("0.1e-1", 0.01);
119 assert_parsed_literal_float("0.1E1", 1.0);
120 assert_parsed_literal_float("0.1E-1", 0.01);
121 assert_parsed_literal_float("0.1e+1", 1.0);
122 assert_parsed_literal_float(".1e+1", 1.0);
123 }
124
125 #[test]
126 fn test_parse_simple_union() {
127 match do_parse("int|string") {
128 Ok(ty) => match ty {
129 Type::Union(u) => {
130 assert!(matches!(*u.left, Type::Int(_)));
131 assert!(matches!(*u.right, Type::String(_)));
132 }
133 _ => panic!("Expected Type::Union"),
134 },
135 Err(err) => {
136 panic!("Failed to parse union type: {err:?}");
137 }
138 }
139 }
140
141 #[test]
142 fn test_parse_variable_union() {
143 match do_parse("$a|$b") {
144 Ok(ty) => match ty {
145 Type::Union(u) => {
146 assert!(matches!(*u.left, Type::Variable(_)));
147 assert!(matches!(*u.right, Type::Variable(_)));
148 }
149 _ => panic!("Expected Type::Union"),
150 },
151 Err(err) => {
152 panic!("Failed to parse union type: {err:?}");
153 }
154 }
155 }
156
157 #[test]
158 fn test_parse_nullable() {
159 let result = do_parse("?string");
160 assert!(result.is_ok());
161 match result.unwrap() {
162 Type::Nullable(n) => {
163 assert!(matches!(*n.inner, Type::String(_)));
164 }
165 _ => panic!("Expected Type::Nullable"),
166 }
167 }
168
169 #[test]
170 fn test_parse_generic_array() {
171 let result = do_parse("array<int, bool>");
172 assert!(result.is_ok());
173 match result.unwrap() {
174 Type::Array(a) => {
175 assert!(a.parameters.is_some());
176 let params = a.parameters.unwrap();
177 assert_eq!(params.entries.len(), 2);
178 assert!(matches!(params.entries[0].inner, Type::Int(_)));
179 assert!(matches!(params.entries[1].inner, Type::Bool(_)));
180 }
181 _ => panic!("Expected Type::Array"),
182 }
183 }
184
185 #[test]
186 fn test_parse_generic_array_one_param() {
187 match do_parse("array<string>") {
188 Ok(Type::Array(a)) => {
189 let params = a.parameters.expect("Expected generic parameters");
190 assert_eq!(params.entries.len(), 1);
191 assert!(matches!(params.entries[0].inner, Type::String(_)));
192 }
193 res => panic!("Expected Ok(Type::Array), got {res:?}"),
194 }
195 }
196
197 #[test]
198 fn test_parse_generic_list() {
199 match do_parse("list<string>") {
200 Ok(Type::List(l)) => {
201 let params = l.parameters.expect("Expected generic parameters");
202 assert_eq!(params.entries.len(), 1);
203 assert!(matches!(params.entries[0].inner, Type::String(_)));
204 }
205 res => panic!("Expected Ok(Type::List), got {res:?}"),
206 }
207 }
208
209 #[test]
210 fn test_parse_non_empty_array() {
211 match do_parse("non-empty-array<int, bool>") {
212 Ok(Type::NonEmptyArray(a)) => {
213 let params = a.parameters.expect("Expected generic parameters");
214 assert_eq!(params.entries.len(), 2);
215 assert!(matches!(params.entries[0].inner, Type::Int(_)));
216 assert!(matches!(params.entries[1].inner, Type::Bool(_)));
217 }
218 res => panic!("Expected Ok(Type::NonEmptyArray), got {res:?}"),
219 }
220 }
221
222 #[test]
223 fn test_parse_nested_generics() {
224 match do_parse("list<array<int, string>>") {
225 Ok(Type::List(l)) => {
226 let params = l.parameters.expect("Expected generic parameters");
227 assert_eq!(params.entries.len(), 1);
228 match ¶ms.entries[0].inner {
229 Type::Array(inner_array) => {
230 let inner_params = inner_array.parameters.as_ref().expect("Inner array needs params");
231 assert_eq!(inner_params.entries.len(), 2);
232 assert!(matches!(inner_params.entries[0].inner, Type::Int(_)));
233 assert!(matches!(inner_params.entries[1].inner, Type::String(_)));
234 }
235 _ => panic!("Expected inner type to be Type::Array"),
236 }
237 }
238 res => panic!("Expected Ok(Type::List), got {res:?}"),
239 }
240 }
241
242 #[test]
243 fn test_parse_simple_shape() {
244 let result = do_parse("array{'name': string}");
245 assert!(matches!(result, Ok(Type::Shape(_))));
246 let Ok(Type::Shape(shape)) = result else {
247 panic!("Expected Type::Shape");
248 };
249
250 assert_eq!(shape.kind, ShapeTypeKind::Array);
251 assert_eq!(shape.keyword.value, "array");
252 assert_eq!(shape.fields.len(), 1);
253 assert!(shape.additional_fields.is_none());
254
255 let field = &shape.fields[0];
256 assert!(matches!(
257 field.key.as_ref().map(|k| k.name.as_ref()),
258 Some(Type::LiteralString(LiteralStringType { raw: "'name'", value: "name", .. }))
259 ));
260 assert!(matches!(field.value.as_ref(), Type::String(_)));
261 }
262
263 #[test]
264 fn test_parse_int_key_shape() {
265 match do_parse("array{0: string, 1: bool}") {
266 Ok(Type::Shape(shape)) => {
267 assert_eq!(shape.fields.len(), 2);
268 let first_field = &shape.fields[0];
269 assert!(matches!(
270 first_field.key.as_ref().map(|k| k.name.as_ref()),
271 Some(Type::LiteralInt(LiteralIntType { value: 0, .. }))
272 ));
273 assert!(matches!(first_field.value.as_ref(), Type::String(_)));
274 let second_field = &shape.fields[1];
275 assert!(matches!(
276 second_field.key.as_ref().map(|k| k.name.as_ref()),
277 Some(Type::LiteralInt(LiteralIntType { value: 1, .. }))
278 ));
279 assert!(matches!(second_field.value.as_ref(), Type::Bool(_)));
280 }
281 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
282 }
283 }
284
285 #[test]
286 fn test_parse_optional_field_shape() {
287 match do_parse("array{name: string, age?: int, address: string}") {
288 Ok(Type::Shape(shape)) => {
289 assert_eq!(shape.fields.len(), 3);
290 assert!(!shape.fields[0].is_optional());
291 assert!(shape.fields[1].is_optional());
292 assert!(!shape.fields[2].is_optional());
293 }
294 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
295 }
296 }
297
298 #[test]
299 fn test_parse_unsealed_shape() {
300 match do_parse("array{name: string, ...}") {
301 Ok(Type::Shape(shape)) => {
302 assert_eq!(shape.fields.len(), 1);
303 assert!(shape.additional_fields.is_some());
304 assert!(shape.additional_fields.unwrap().parameters.is_none()); }
306 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
307 }
308 }
309
310 #[test]
311 fn test_parse_shape_with_keys_containing_special_chars() {
312 match do_parse("array{key-with-dash: int, key-with---multiple-dashes?: int}") {
313 Ok(Type::Shape(shape)) => {
314 assert_eq!(shape.fields.len(), 2);
315
316 if let Some(Type::Reference(r)) = shape.fields[0].key.as_ref().map(|k| k.name.as_ref()) {
317 assert_eq!(r.identifier.value, "key-with-dash");
318 } else {
319 panic!("Expected key to be a Type::Reference");
320 }
321
322 if let Some(Type::Reference(r)) = shape.fields[1].key.as_ref().map(|k| k.name.as_ref()) {
323 assert_eq!(r.identifier.value, "key-with---multiple-dashes");
324 } else {
325 panic!("Expected key to be a Type::Reference");
326 }
327 }
328 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
329 }
330 }
331
332 #[test]
333 fn test_parse_shape_with_keys_after_types() {
334 match do_parse("array{list: list<int>, int?: int, string: string, bool: bool}") {
335 Ok(Type::Shape(shape)) => {
336 assert_eq!(shape.fields.len(), 4);
337
338 if let Some(Type::Reference(r)) = shape.fields[0].key.as_ref().map(|k| k.name.as_ref()) {
339 assert_eq!(r.identifier.value, "list");
340 } else {
341 panic!("Expected key to be a Type::Reference");
342 }
343
344 if let Some(Type::Reference(r)) = shape.fields[1].key.as_ref().map(|k| k.name.as_ref()) {
345 assert_eq!(r.identifier.value, "int");
346 } else {
347 panic!("Expected key to be a Type::Reference");
348 }
349
350 if let Some(Type::Reference(r)) = shape.fields[2].key.as_ref().map(|k| k.name.as_ref()) {
351 assert_eq!(r.identifier.value, "string");
352 } else {
353 panic!("Expected key to be a Type::Reference");
354 }
355
356 if let Some(Type::Reference(r)) = shape.fields[3].key.as_ref().map(|k| k.name.as_ref()) {
357 assert_eq!(r.identifier.value, "bool");
358 } else {
359 panic!("Expected key to be a Type::Reference");
360 }
361 }
362 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
363 }
364 }
365
366 #[test]
367 fn test_parse_unsealed_shape_with_fallback() {
368 match do_parse(
369 "array{
370 name: string, // This is a comment
371 ...<string, string>
372 }",
373 ) {
374 Ok(Type::Shape(shape)) => {
375 assert_eq!(shape.fields.len(), 1);
376 assert!(shape.additional_fields.as_ref().is_some_and(|a| a.parameters.is_some()));
377 let params = shape.additional_fields.unwrap().parameters.unwrap();
378 assert_eq!(params.entries.len(), 2);
379 assert!(matches!(params.entries[0].inner, Type::String(_)));
380 assert!(matches!(params.entries[1].inner, Type::String(_)));
381 }
382 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
383 }
384 }
385
386 #[test]
387 fn test_parse_empty_shape() {
388 match do_parse("array{}") {
389 Ok(Type::Shape(shape)) => {
390 assert_eq!(shape.fields.len(), 0);
391 assert!(shape.additional_fields.is_none());
392 }
393 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
394 }
395 }
396
397 #[test]
398 fn test_parse_error_unexpected_token() {
399 let result = do_parse("int|>");
400 assert!(result.is_err());
401 assert!(matches!(result.unwrap_err(), ParseError::UnexpectedToken { .. }));
402 }
403
404 #[test]
405 fn test_parse_error_eof() {
406 let result = do_parse("array<int");
407 assert!(result.is_err());
408 assert!(matches!(result.unwrap_err(), ParseError::UnexpectedEndOfFile { .. }));
409 }
410
411 #[test]
412 fn test_parse_error_trailing_token() {
413 let result = do_parse("int|string&");
414 assert!(result.is_err());
415 assert!(matches!(result.unwrap_err(), ParseError::UnexpectedEndOfFile { .. }));
416 }
417
418 #[test]
419 fn test_parse_intersection() {
420 match do_parse("Countable&Traversable") {
421 Ok(Type::Intersection(i)) => {
422 assert!(matches!(*i.left, Type::Reference(_)));
423 assert!(matches!(*i.right, Type::Reference(_)));
424
425 if let Type::Reference(r) = *i.left {
426 assert_eq!(r.identifier.value, "Countable");
427 } else {
428 panic!();
429 }
430
431 if let Type::Reference(r) = *i.right {
432 assert_eq!(r.identifier.value, "Traversable");
433 } else {
434 panic!();
435 }
436 }
437 res => panic!("Expected Ok(Type::Intersection), got {res:?}"),
438 }
439 }
440
441 #[test]
442 fn test_parse_member_ref() {
443 match do_parse("MyClass::MY_CONST") {
444 Ok(Type::MemberReference(m)) => {
445 assert_eq!(m.class.value, "MyClass");
446 assert_eq!(m.member.to_string(), "MY_CONST");
447 }
448 res => panic!("Expected Ok(Type::MemberReference), got {res:?}"),
449 }
450
451 match do_parse("\\Fully\\Qualified::class") {
452 Ok(Type::MemberReference(m)) => {
453 assert_eq!(m.class.value, "\\Fully\\Qualified"); assert_eq!(m.member.to_string(), "class");
455 }
456 res => panic!("Expected Ok(Type::MemberReference), got {res:?}"),
457 }
458 }
459
460 #[test]
461 fn test_parse_iterable() {
462 match do_parse("iterable<int, string>") {
463 Ok(Type::Iterable(i)) => {
464 let params = i.parameters.expect("Expected generic parameters");
465 assert_eq!(params.entries.len(), 2);
466 assert!(matches!(params.entries[0].inner, Type::Int(_)));
467 assert!(matches!(params.entries[1].inner, Type::String(_)));
468 }
469 res => panic!("Expected Ok(Type::Iterable), got {res:?}"),
470 }
471
472 match do_parse("iterable<bool>") {
473 Ok(Type::Iterable(i)) => {
475 let params = i.parameters.expect("Expected generic parameters");
476 assert_eq!(params.entries.len(), 1);
477 assert!(matches!(params.entries[0].inner, Type::Bool(_)));
478 }
479 res => panic!("Expected Ok(Type::Iterable), got {res:?}"),
480 }
481
482 match do_parse("iterable") {
483 Ok(Type::Iterable(i)) => {
484 assert!(i.parameters.is_none());
485 }
486 res => panic!("Expected Ok(Type::Iterable), got {res:?}"),
487 }
488 }
489
490 #[test]
491 fn test_parse_negated_int() {
492 let assert_negated_int = |input: &str, expected_value: u64| {
493 let result = do_parse(input);
494 assert!(result.is_ok());
495 match result.unwrap() {
496 Type::Negated(n) => {
497 assert!(matches!(*n.inner, Type::LiteralInt(_)));
498 if let Type::LiteralInt(lit) = *n.inner {
499 assert_eq!(lit.value, expected_value);
500 } else {
501 panic!()
502 }
503 }
504 _ => panic!("Expected Type::Negated"),
505 }
506 };
507
508 assert_negated_int("-0", 0);
509 assert_negated_int("-1", 1);
510 assert_negated_int(
511 "-
512 // This is a comment
513 123_345",
514 123345,
515 );
516 assert_negated_int("-0b1", 1);
517 }
518
519 #[test]
520 fn test_parse_callable_no_spec() {
521 match do_parse("callable") {
522 Ok(Type::Callable(c)) => {
523 assert!(c.specification.is_none());
524 assert_eq!(c.kind, CallableTypeKind::Callable);
525 }
526 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
527 }
528 }
529
530 #[test]
531 fn test_parse_callable_params_only() {
532 match do_parse("callable(int, ?string)") {
533 Ok(Type::Callable(c)) => {
534 let spec = c.specification.expect("Expected callable specification");
535 assert!(spec.return_type.is_none());
536 assert_eq!(spec.parameters.entries.len(), 2);
537 assert!(matches!(spec.parameters.entries[0].parameter_type, Some(Type::Int(_))));
538 assert!(matches!(spec.parameters.entries[1].parameter_type, Some(Type::Nullable(_))));
539 assert!(spec.parameters.entries[0].ellipsis.is_none());
540 assert!(spec.parameters.entries[0].equals.is_none());
541 }
542 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
543 }
544 }
545
546 #[test]
547 fn test_parse_callable_return_only() {
548 match do_parse("callable(): void") {
549 Ok(Type::Callable(c)) => {
550 let spec = c.specification.expect("Expected callable specification");
551 assert!(spec.parameters.entries.is_empty());
552 assert!(spec.return_type.is_some());
553 assert!(matches!(*spec.return_type.unwrap().return_type, Type::Void(_)));
554 }
555 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
556 }
557 }
558
559 #[test]
560 fn test_parse_pure_callable_full() {
561 match do_parse("pure-callable(bool): int") {
562 Ok(Type::Callable(c)) => {
563 assert_eq!(c.kind, CallableTypeKind::PureCallable);
564 let spec = c.specification.expect("Expected callable specification");
565 assert_eq!(spec.parameters.entries.len(), 1);
566 assert!(matches!(spec.parameters.entries[0].parameter_type, Some(Type::Bool(_))));
567 assert!(spec.return_type.is_some());
568 assert!(matches!(*spec.return_type.unwrap().return_type, Type::Int(_)));
569 }
570 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
571 }
572 }
573
574 #[test]
575 fn test_parse_closure_via_identifier() {
576 match do_parse("Closure(string): bool") {
577 Ok(Type::Callable(c)) => {
578 assert_eq!(c.kind, CallableTypeKind::Closure);
579 assert_eq!(c.keyword.value, "Closure");
580 let spec = c.specification.expect("Expected callable specification");
581 assert_eq!(spec.parameters.entries.len(), 1);
582 assert!(matches!(spec.parameters.entries[0].parameter_type, Some(Type::String(_))));
583 assert!(spec.return_type.is_some());
584 assert!(matches!(*spec.return_type.unwrap().return_type, Type::Bool(_)));
585 }
586 res => panic!("Expected Ok(Type::Callable) for Closure, got {res:?}"),
587 }
588 }
589
590 #[test]
591 fn test_parse_complex_pure_callable() {
592 match do_parse("pure-callable(list<int>, ?Closure(): void=, int...): ((Simple&Iter<T>)|null)") {
593 Ok(Type::Callable(c)) => {
594 assert_eq!(c.kind, CallableTypeKind::PureCallable);
595 let spec = c.specification.expect("Expected callable specification");
596 assert_eq!(spec.parameters.entries.len(), 3);
597 assert!(spec.return_type.is_some());
598
599 let first_param = &spec.parameters.entries[0];
600 assert!(matches!(first_param.parameter_type, Some(Type::List(_))));
601 assert!(first_param.ellipsis.is_none());
602 assert!(first_param.equals.is_none());
603
604 let second_param = &spec.parameters.entries[1];
605 assert!(matches!(second_param.parameter_type, Some(Type::Nullable(_))));
606 assert!(second_param.ellipsis.is_none());
607 assert!(second_param.equals.is_some());
608
609 let third_param = &spec.parameters.entries[2];
610 assert!(matches!(third_param.parameter_type, Some(Type::Int(_))));
611 assert!(third_param.ellipsis.is_some());
612 assert!(third_param.equals.is_none());
613
614 if let Type::Parenthesized(p) = *spec.return_type.unwrap().return_type {
615 assert!(matches!(*p.inner, Type::Union(_)));
616 if let Type::Union(u) = *p.inner {
617 assert!(matches!(u.left.as_ref(), Type::Parenthesized(_)));
618 assert!(matches!(u.right.as_ref(), Type::Null(_)));
619 }
620 } else {
621 panic!("Expected Type::CallableReturnType");
622 }
623 }
624 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
625 }
626 }
627
628 #[test]
629 fn test_parse_conditional_type() {
630 match do_parse("int is not string ? array : int") {
631 Ok(Type::Conditional(c)) => {
632 assert!(matches!(*c.subject, Type::Int(_)));
633 assert!(c.not.is_some());
634 assert!(matches!(*c.target, Type::String(_)));
635 assert!(matches!(*c.then, Type::Array(_)));
636 assert!(matches!(*c.otherwise, Type::Int(_)));
637 }
638 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
639 }
640
641 match do_parse("$input is string ? array : int") {
642 Ok(Type::Conditional(c)) => {
643 assert!(matches!(*c.subject, Type::Variable(_)));
644 assert!(c.not.is_none());
645 assert!(matches!(*c.target, Type::String(_)));
646 assert!(matches!(*c.then, Type::Array(_)));
647 assert!(matches!(*c.otherwise, Type::Int(_)));
648 }
649 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
650 }
651
652 match do_parse("int is string ? array : (int is not $bar ? string : $baz)") {
653 Ok(Type::Conditional(c)) => {
654 assert!(matches!(*c.subject, Type::Int(_)));
655 assert!(c.not.is_none());
656 assert!(matches!(*c.target, Type::String(_)));
657 assert!(matches!(*c.then, Type::Array(_)));
658
659 let Type::Parenthesized(p) = *c.otherwise else {
660 panic!("Expected Type::Parenthesized");
661 };
662
663 if let Type::Conditional(inner_conditional) = *p.inner {
664 assert!(matches!(*inner_conditional.subject, Type::Int(_)));
665 assert!(inner_conditional.not.is_some());
666 assert!(matches!(*inner_conditional.target, Type::Variable(_)));
667 assert!(matches!(*inner_conditional.then, Type::String(_)));
668 assert!(matches!(*inner_conditional.otherwise, Type::Variable(_)));
669 } else {
670 panic!("Expected Type::Conditional");
671 }
672 }
673 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
674 }
675 }
676
677 #[test]
678 fn test_keyof() {
679 match do_parse("key-of<MyArray>") {
680 Ok(Type::KeyOf(k)) => {
681 assert_eq!(k.keyword.value, "key-of");
682 match &k.parameter.entry.inner {
683 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
684 _ => panic!("Expected Type::Reference"),
685 }
686 }
687 res => panic!("Expected Ok(Type::KeyOf), got {res:?}"),
688 }
689 }
690
691 #[test]
692 fn test_valueof() {
693 match do_parse("value-of<MyArray>") {
694 Ok(Type::ValueOf(v)) => {
695 assert_eq!(v.keyword.value, "value-of");
696 match &v.parameter.entry.inner {
697 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
698 _ => panic!("Expected Type::Reference"),
699 }
700 }
701 res => panic!("Expected Ok(Type::ValueOf), got {res:?}"),
702 }
703 }
704
705 #[test]
706 fn test_indexed_access() {
707 match do_parse("MyArray[MyKey]") {
708 Ok(Type::IndexAccess(i)) => {
709 match *i.target {
710 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
711 _ => panic!("Expected Type::Reference"),
712 }
713 match *i.index {
714 Type::Reference(r) => assert_eq!(r.identifier.value, "MyKey"),
715 _ => panic!("Expected Type::Reference"),
716 }
717 }
718 res => panic!("Expected Ok(Type::IndexAccess), got {res:?}"),
719 }
720 }
721
722 #[test]
723 fn test_slice_type() {
724 match do_parse("string[]") {
725 Ok(Type::Slice(s)) => {
726 assert!(matches!(*s.inner, Type::String(_)));
727 }
728 res => panic!("Expected Ok(Type::Slice), got {res:?}"),
729 }
730 }
731
732 #[test]
733 fn test_slice_of_slice_of_slice_type() {
734 match do_parse("string[][][]") {
735 Ok(Type::Slice(s)) => {
736 assert!(matches!(*s.inner, Type::Slice(_)));
737 if let Type::Slice(inner_slice) = *s.inner {
738 assert!(matches!(*inner_slice.inner, Type::Slice(_)));
739 if let Type::Slice(inner_inner_slice) = *inner_slice.inner {
740 assert!(matches!(*inner_inner_slice.inner, Type::String(_)));
741 } else {
742 panic!("Expected inner slice to be a Slice");
743 }
744 } else {
745 panic!("Expected outer slice to be a Slice");
746 }
747 }
748 res => panic!("Expected Ok(Type::Slice), got {res:?}"),
749 }
750 }
751
752 #[test]
753 fn test_int_range() {
754 match do_parse("int<0, 100>") {
755 Ok(Type::IntRange(r)) => {
756 assert_eq!(r.keyword.value, "int");
757
758 match r.min {
759 IntOrKeyword::Int(literal_int_type) => {
760 assert_eq!(literal_int_type.value, 0);
761 }
762 _ => {
763 panic!("Expected min to be a LiteralIntType, got `{}`", r.min)
764 }
765 };
766
767 match r.max {
768 IntOrKeyword::Int(literal_int_type) => {
769 assert_eq!(literal_int_type.value, 100);
770 }
771 _ => {
772 panic!("Expected max to be a LiteralIntType, got `{}`", r.max)
773 }
774 };
775 }
776 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
777 }
778
779 match do_parse("int<min, 0>") {
780 Ok(Type::IntRange(r)) => {
781 match r.min {
782 IntOrKeyword::Keyword(keyword) => {
783 assert_eq!(keyword.value, "min");
784 }
785 _ => {
786 panic!("Expected min to be a Keyword, got `{}`", r.min)
787 }
788 };
789
790 match r.max {
791 IntOrKeyword::Int(literal_int_type) => {
792 assert_eq!(literal_int_type.value, 0);
793 }
794 _ => {
795 panic!("Expected max to be a LiteralIntType, got `{}`", r.max)
796 }
797 };
798 }
799 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
800 }
801
802 match do_parse("int<min, max>") {
803 Ok(Type::IntRange(r)) => {
804 match r.min {
805 IntOrKeyword::Keyword(keyword) => {
806 assert_eq!(keyword.value, "min");
807 }
808 _ => {
809 panic!("Expected min to be a Keyword, got `{}`", r.min)
810 }
811 };
812
813 match r.max {
814 IntOrKeyword::Keyword(keyword) => {
815 assert_eq!(keyword.value, "max");
816 }
817 _ => {
818 panic!("Expected max to be a Keyword, got `{}`", r.max)
819 }
820 };
821 }
822 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
823 }
824 }
825
826 #[test]
827 fn test_properties_of() {
828 match do_parse("properties-of<MyClass>") {
829 Ok(Type::PropertiesOf(p)) => {
830 assert_eq!(p.keyword.value, "properties-of");
831 assert_eq!(p.filter, PropertiesOfFilter::All);
832 match &p.parameter.entry.inner {
833 Type::Reference(r) => assert_eq!(r.identifier.value, "MyClass"),
834 _ => panic!(),
835 }
836 }
837 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
838 }
839
840 match do_parse("protected-properties-of<T>") {
841 Ok(Type::PropertiesOf(p)) => {
842 assert_eq!(p.keyword.value, "protected-properties-of");
843 assert_eq!(p.filter, PropertiesOfFilter::Protected);
844 match &p.parameter.entry.inner {
845 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
846 _ => panic!(),
847 }
848 }
849 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
850 }
851
852 match do_parse("private-properties-of<T>") {
853 Ok(Type::PropertiesOf(p)) => {
854 assert_eq!(p.keyword.value, "private-properties-of");
855 assert_eq!(p.filter, PropertiesOfFilter::Private);
856 match &p.parameter.entry.inner {
857 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
858 _ => panic!(),
859 }
860 }
861 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
862 }
863
864 match do_parse("public-properties-of<T>") {
865 Ok(Type::PropertiesOf(p)) => {
866 assert_eq!(p.keyword.value, "public-properties-of");
867 assert_eq!(p.filter, PropertiesOfFilter::Public);
868 match &p.parameter.entry.inner {
869 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
870 _ => panic!(),
871 }
872 }
873 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
874 }
875 }
876
877 #[test]
878 fn test_variable() {
879 match do_parse("$myVar") {
880 Ok(Type::Variable(v)) => {
881 assert_eq!(v.value, "$myVar");
882 }
883 res => panic!("Expected Ok(Type::Variable), got {res:?}"),
884 }
885 }
886
887 #[test]
888 fn test_nullable_intersection() {
889 match do_parse("Countable&?Traversable") {
891 Ok(Type::Intersection(i)) => {
892 assert!(matches!(*i.left, Type::Reference(r) if r.identifier.value == "Countable"));
893 assert!(matches!(*i.right, Type::Nullable(_)));
894 if let Type::Nullable(n) = *i.right {
895 assert!(matches!(*n.inner, Type::Reference(r) if r.identifier.value == "Traversable"));
896 } else {
897 panic!();
898 }
899 }
900 res => panic!("Expected Ok(Type::Intersection), got {res:?}"),
901 }
902 }
903
904 #[test]
905 fn test_parenthesized_nullable() {
906 match do_parse("?(Countable&Traversable)") {
907 Ok(Type::Nullable(n)) => {
908 assert!(matches!(*n.inner, Type::Parenthesized(_)));
909 if let Type::Parenthesized(p) = *n.inner {
910 assert!(matches!(*p.inner, Type::Intersection(_)));
911 } else {
912 panic!()
913 }
914 }
915 res => panic!("Expected Ok(Type::Nullable), got {res:?}"),
916 }
917 }
918
919 #[test]
920 fn test_positive_negative_int() {
921 match do_parse("positive-int|negative-int") {
922 Ok(Type::Union(u)) => {
923 assert!(matches!(*u.left, Type::PositiveInt(_)));
924 assert!(matches!(*u.right, Type::NegativeInt(_)));
925 }
926 res => panic!("Expected Ok(Type::Union), got {res:?}"),
927 }
928 }
929
930 #[test]
931 fn test_parse_float_alias() {
932 match do_parse("double") {
933 Ok(Type::Float(f)) => {
934 assert_eq!(f.value, "double");
935 }
936 res => panic!("Expected Ok(Type::Float), got {res:?}"),
937 }
938
939 match do_parse("real") {
940 Ok(Type::Float(f)) => {
941 assert_eq!(f.value, "real");
942 }
943 res => panic!("Expected Ok(Type::Float), got {res:?}"),
944 }
945
946 match do_parse("float") {
947 Ok(Type::Float(f)) => {
948 assert_eq!(f.value, "float");
949 }
950 res => panic!("Expected Ok(Type::Float), got {res:?}"),
951 }
952 }
953
954 #[test]
955 fn test_parse_bool_alias() {
956 match do_parse("boolean") {
957 Ok(Type::Bool(b)) => {
958 assert_eq!(b.value, "boolean");
959 }
960 res => panic!("Expected Ok(Type::Bool), got {res:?}"),
961 }
962
963 match do_parse("bool") {
964 Ok(Type::Bool(b)) => {
965 assert_eq!(b.value, "bool");
966 }
967 res => panic!("Expected Ok(Type::Bool), got {res:?}"),
968 }
969 }
970
971 #[test]
972 fn test_parse_integer_alias() {
973 match do_parse("integer") {
974 Ok(Type::Int(i)) => {
975 assert_eq!(i.value, "integer");
976 }
977 res => panic!("Expected Ok(Type::Int), got {res:?}"),
978 }
979
980 match do_parse("int") {
981 Ok(Type::Int(i)) => {
982 assert_eq!(i.value, "int");
983 }
984 res => panic!("Expected Ok(Type::Int), got {res:?}"),
985 }
986 }
987
988 #[test]
989 fn test_parse_callable_with_variables() {
990 match do_parse("callable(string ...$names)") {
991 Ok(Type::Callable(callable)) => {
992 assert_eq!(callable.keyword.value, "callable");
993 assert!(callable.specification.is_some());
994
995 let specification = callable.specification.unwrap();
996
997 assert!(specification.return_type.is_none());
998 assert_eq!(specification.parameters.entries.len(), 1);
999
1000 let first_parameter = specification.parameters.entries.first().unwrap();
1001 assert!(first_parameter.variable.is_some());
1002 assert!(first_parameter.ellipsis.is_some());
1003
1004 let variable = first_parameter.variable.unwrap();
1005 assert_eq!(variable.value, "$names");
1006 }
1007 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
1008 }
1009 }
1010
1011 #[test]
1012 fn test_parse_string_or_lowercase_string_union() {
1013 match do_parse("string|lowercase-string") {
1014 Ok(Type::Union(u)) => {
1015 assert!(matches!(*u.left, Type::String(_)));
1016 assert!(matches!(*u.right, Type::LowercaseString(_)));
1017 }
1018 res => panic!("Expected Ok(Type::Union), got {res:?}"),
1019 }
1020 }
1021
1022 #[test]
1023 fn test_parse_optional_literal_string_shape_field() {
1024 match do_parse("array{'salt'?: int, 'cost'?: int, ...}") {
1025 Ok(Type::Shape(shape)) => {
1026 assert_eq!(shape.fields.len(), 2);
1027 assert!(shape.additional_fields.is_some());
1028
1029 let first_field = &shape.fields[0];
1030 assert!(first_field.is_optional());
1031 assert!(matches!(
1032 first_field.key.as_ref().map(|k| k.name.as_ref()),
1033 Some(Type::LiteralString(LiteralStringType { raw: "'salt'", value: "salt", .. }))
1034 ));
1035 assert!(matches!(first_field.value.as_ref(), Type::Int(_)));
1036
1037 let second_field = &shape.fields[1];
1038 assert!(second_field.is_optional());
1039 assert!(matches!(
1040 second_field.key.as_ref().map(|k| k.name.as_ref()),
1041 Some(Type::LiteralString(LiteralStringType { raw: "'cost'", value: "cost", .. }))
1042 ));
1043 assert!(matches!(second_field.value.as_ref(), Type::Int(_)));
1044 }
1045 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1046 }
1047 }
1048}