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
use crate::ast::{FromTokens, IsMatch, ParseError, Tokens, TryFromTokens};
use crate::lexical_token::{Name, PunctuatorType};
use crate::Span;
use bluejay_core::{
    ListTypeReference as CoreListTypeReference, NamedTypeReference as CoreNamedTypeReference,
    TypeReference as CoreTypeReference,
};

pub type TypeReference<'a> = CoreTypeReference<NamedTypeReference<'a>, ListTypeReference<'a>>;

impl<'a> FromTokens<'a> for TypeReference<'a> {
    fn from_tokens(tokens: &mut impl Tokens<'a>) -> Result<Self, ParseError> {
        if let Some(ltr) = ListTypeReference::try_from_tokens(tokens) {
            ltr.map(Self::ListType)
        } else if let Some(ntr) = NamedTypeReference::try_from_tokens(tokens) {
            ntr.map(Self::NamedType)
        } else {
            Err(tokens.unexpected_token())
        }
    }
}

#[derive(Debug)]
pub struct NamedTypeReference<'a> {
    name: Name<'a>,
    bang_span: Option<Span>,
}

impl<'a> IsMatch<'a> for NamedTypeReference<'a> {
    fn is_match(tokens: &mut impl Tokens<'a>) -> bool {
        tokens.peek_name(0).is_some()
    }
}

impl<'a> FromTokens<'a> for NamedTypeReference<'a> {
    fn from_tokens(tokens: &mut impl Tokens<'a>) -> Result<Self, ParseError> {
        let name = tokens.expect_name()?;
        let bang_span = tokens.next_if_punctuator(PunctuatorType::Bang);
        Ok(Self { name, bang_span })
    }
}

impl<'a> CoreNamedTypeReference for NamedTypeReference<'a> {
    fn name(&self) -> &str {
        self.name.as_ref()
    }

    fn required(&self) -> bool {
        self.bang_span.is_some()
    }
}

#[derive(Debug)]
pub struct ListTypeReference<'a> {
    inner: Box<TypeReference<'a>>,
    _square_bracket_span: Span,
    bang_span: Option<Span>,
}

impl<'a> FromTokens<'a> for ListTypeReference<'a> {
    fn from_tokens(tokens: &mut impl Tokens<'a>) -> Result<Self, ParseError> {
        let open_span = tokens.expect_punctuator(PunctuatorType::OpenSquareBracket)?;
        let inner = Box::new(TypeReference::from_tokens(tokens)?);
        let close_span = tokens.expect_punctuator(PunctuatorType::CloseSquareBracket)?;
        let square_bracket_span = open_span.merge(&close_span);
        let bang_span = tokens.next_if_punctuator(PunctuatorType::Bang);
        Ok(Self {
            inner,
            _square_bracket_span: square_bracket_span,
            bang_span,
        })
    }
}

impl<'a> IsMatch<'a> for ListTypeReference<'a> {
    fn is_match(tokens: &mut impl Tokens<'a>) -> bool {
        tokens.peek_punctuator_matches(0, PunctuatorType::OpenSquareBracket)
    }
}

impl<'a> CoreListTypeReference for ListTypeReference<'a> {
    type NamedTypeReference = NamedTypeReference<'a>;

    fn inner(&self) -> &TypeReference<'a> {
        &self.inner
    }

    fn required(&self) -> bool {
        self.bang_span.is_some()
    }
}