use std::collections::HashMap;
use castle_shared_parser::parse_inputs::{parse_inputs, consume_optional_separator};
use castle_tokenizer::{
extensions::{ExpectIdentifier, ExpectKeyword, ExpectPunctuator, PeekKeyword, IsPunctuator},
Keyword, Punctuator, TokenKind, Tokenizable,
};
use castle_types::{Field, CastleError, FieldKind};
pub fn parse_projection_inner(
tokenizer: &mut impl Tokenizable,
) -> Result<HashMap<Box<str>, Field>, CastleError> {
let mut projections = HashMap::new();
loop {
match tokenizer.peek(true)?.map(|t| (&t.kind, &t.span)) {
Some((TokenKind::Identifier(_), ..)) => {
let field = parse_field(tokenizer)?;
projections.insert(field.ident.clone(), field);
consume_optional_separator(tokenizer)?;
},
_ => break, }
}
Ok(projections)
}
fn parse_field(tokenizer: &mut impl Tokenizable) -> Result<Field, CastleError> {
Ok(Field {
ident: tokenizer.expect_identifier(true)?,
inputs: if tokenizer.peek_is_punctuator(Punctuator::OpenParen, true)? {
parse_inputs(tokenizer)?
} else {
HashMap::new()
},
rename: parse_rename_optional(tokenizer)?,
kind: parse_field_kind(tokenizer)?,
})
}
fn parse_rename_optional(
tokenizer: &mut impl Tokenizable,
) -> Result<Option<Box<str>>, CastleError> {
if let Some(Keyword::As) = tokenizer.peek_keyword(false)? {
tokenizer.expect_keyword(Keyword::As, true)?;
Ok(Some(tokenizer.expect_identifier(true)?))
} else {
Ok(None)
}
}
pub(crate) fn parse_projection(
tokenizer: &mut impl Tokenizable,
opening: Punctuator,
closing: Punctuator,
) -> Result<HashMap<Box<str>, Field>, CastleError> {
tokenizer.expect_punctuator(opening, true)?;
let projections = parse_projection_inner(tokenizer)?;
tokenizer.expect_punctuator(closing, true)?;
Ok(projections)
}
fn parse_field_kind(tokenizer: &mut impl Tokenizable) -> Result<FieldKind, CastleError> {
match tokenizer.peek_token_kind(false)? {
Some(TokenKind::Punctuator(Punctuator::OpenBlock)) => Ok(FieldKind::Object(
parse_projection(tokenizer, Punctuator::OpenBlock, Punctuator::CloseBlock)?,
)),
Some(TokenKind::Punctuator(Punctuator::OpenBracket)) => Ok(FieldKind::List(
parse_projection(tokenizer, Punctuator::OpenBracket, Punctuator::CloseBracket)?,
)),
_ => Ok(FieldKind::Field),
}
}