1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use mago_database::file::HasFileId;
use crate::ast::Keyword;
use crate::ast::ShapeField;
use crate::ast::ShapeFieldKey;
use crate::ast::Type;
use crate::ast::object::ObjectProperties;
use crate::ast::object::ObjectType;
use crate::error::ParseError;
use crate::parser::internal::array_like::parse_shape_field_key;
use crate::parser::internal::parse_type;
use crate::parser::internal::stream::TypeTokenStream;
use crate::token::TypeTokenKind;
#[inline]
pub fn parse_object_type<'input>(stream: &mut TypeTokenStream<'input>) -> Result<Type<'input>, ParseError> {
let keyword = Keyword::from_token(stream.eat(TypeTokenKind::Object)?, stream.file_id());
if !stream.is_at(TypeTokenKind::LeftBrace)? {
return Ok(Type::Object(ObjectType { keyword, properties: None }));
}
Ok(Type::Object(ObjectType {
keyword,
properties: Some(ObjectProperties {
left_brace: stream.eat(TypeTokenKind::LeftBrace)?.span_for(stream.file_id()),
fields: {
let mut fields = Vec::new();
while !stream.is_at(TypeTokenKind::RightBrace)? && !stream.is_at(TypeTokenKind::Ellipsis)? {
let has_key = {
let mut found_key = false;
// Scan ahead to determine if a key is present before the value type.
for i in 0.. {
let Some(token) = stream.lookahead(i)? else {
// Reached the end of the stream, so no key was found.
break;
};
match token.kind {
// If we find a colon, we know a key is present.
TypeTokenKind::Colon => {
found_key = true;
break;
}
// If we find a question mark, it could indicate a key,
// if the following token is a colon.
TypeTokenKind::Question
if stream.lookahead(i + 1)?.is_some_and(|t| t.kind == TypeTokenKind::Colon) =>
{
found_key = true;
break;
}
// If we find any of these tokens, what came before must have
// been a full value type, not a key.
TypeTokenKind::Comma
| TypeTokenKind::RightBrace
| TypeTokenKind::LeftBrace
| TypeTokenKind::LeftParenthesis
| TypeTokenKind::RightParenthesis
| TypeTokenKind::LeftBracket
| TypeTokenKind::RightBracket
| TypeTokenKind::Ellipsis => {
break;
}
// Any other token is part of a potential key, so keep scanning.
_ => {}
}
}
found_key
};
let field = ShapeField {
key: if has_key {
Some(ShapeFieldKey {
key: parse_shape_field_key(stream)?,
question_mark: if stream.is_at(TypeTokenKind::Question)? {
Some(stream.consume()?.span_for(stream.file_id()))
} else {
None
},
colon: stream.eat(TypeTokenKind::Colon)?.span_for(stream.file_id()),
})
} else {
None
},
value: Box::new(parse_type(stream)?),
comma: if stream.is_at(TypeTokenKind::Comma)? {
Some(stream.consume()?.span_for(stream.file_id()))
} else {
None
},
};
if field.comma.is_none() {
fields.push(field);
break;
}
fields.push(field);
}
fields
},
ellipsis: if stream.is_at(TypeTokenKind::Ellipsis)? {
Some(stream.consume()?.span_for(stream.file_id()))
} else {
None
},
right_brace: stream.eat(TypeTokenKind::RightBrace)?.span_for(stream.file_id()),
}),
}))
}