Skip to main content

bluejay_parser/ast/definition/
directive_definition.rs

1use crate::ast::definition::{ArgumentsDefinition, Context};
2use crate::ast::{DepthLimiter, FromTokens, Parse, ParseError, Tokens, TryFromTokens};
3use crate::lexical_token::{Name, PunctuatorType, StringValue};
4use crate::Span;
5use bluejay_core::definition::{
6    DirectiveDefinition as CoreDirectiveDefinition, DirectiveLocation as CoreDirectiveLocation,
7};
8use bluejay_core::AsIter;
9use std::str::FromStr;
10use strum::{EnumIter, IntoStaticStr};
11
12#[derive(IntoStaticStr, EnumIter, Clone, Copy, Debug, PartialEq)]
13#[strum(serialize_all = "camelCase")]
14pub enum BuiltinDirectiveDefinition {
15    Deprecated,
16    Include,
17    OneOf,
18    Skip,
19    SpecifiedBy,
20}
21
22impl BuiltinDirectiveDefinition {
23    const SKIP_DEFINITION: &'static str =
24        "directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT";
25    const INCLUDE_DEFINITION: &'static str =
26        "directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT";
27    const DEPRECATED_DEFINITION: &'static str = "directive @deprecated(reason: String = \"No longer supported\") on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE";
28    const SPECIFIED_BY_DEFINITION: &'static str = "directive @specifiedBy(url: String!) on SCALAR";
29    const ONE_OF_DEFINITION: &'static str = "directive @oneOf on INPUT_OBJECT";
30
31    fn definition(&self) -> &'static str {
32        match self {
33            Self::Deprecated => Self::DEPRECATED_DEFINITION,
34            Self::Include => Self::INCLUDE_DEFINITION,
35            Self::OneOf => Self::ONE_OF_DEFINITION,
36            Self::Skip => Self::SKIP_DEFINITION,
37            Self::SpecifiedBy => Self::SPECIFIED_BY_DEFINITION,
38        }
39    }
40}
41
42impl<C: Context> From<BuiltinDirectiveDefinition> for DirectiveDefinition<'_, C> {
43    fn from(value: BuiltinDirectiveDefinition) -> Self {
44        let mut definition = DirectiveDefinition::parse(value.definition())
45            .result
46            .unwrap();
47
48        definition.is_builtin = true;
49        definition
50    }
51}
52
53#[derive(Debug)]
54pub struct DirectiveDefinition<'a, C: Context> {
55    description: Option<StringValue<'a>>,
56    name: Name<'a>,
57    arguments_definition: Option<ArgumentsDefinition<'a, C>>,
58    is_repeatable: bool,
59    locations: DirectiveLocations,
60    is_builtin: bool,
61}
62
63impl<'a, C: Context> CoreDirectiveDefinition for DirectiveDefinition<'a, C> {
64    type ArgumentsDefinition = ArgumentsDefinition<'a, C>;
65    type DirectiveLocations = DirectiveLocations;
66
67    fn description(&self) -> Option<&str> {
68        self.description.as_ref().map(AsRef::as_ref)
69    }
70
71    fn name(&self) -> &str {
72        self.name.as_ref()
73    }
74
75    fn arguments_definition(&self) -> Option<&Self::ArgumentsDefinition> {
76        self.arguments_definition.as_ref()
77    }
78
79    fn is_repeatable(&self) -> bool {
80        self.is_repeatable
81    }
82
83    fn locations(&self) -> &Self::DirectiveLocations {
84        &self.locations
85    }
86
87    fn is_builtin(&self) -> bool {
88        self.is_builtin
89    }
90}
91
92impl<'a, C: Context> DirectiveDefinition<'a, C> {
93    pub(crate) const DIRECTIVE_IDENTIFIER: &'static str = "directive";
94    const REPEATABLE_IDENTIFIER: &'static str = "repeatable";
95    const ON_IDENTIFIER: &'static str = "on";
96
97    pub(crate) fn name_token(&self) -> &Name<'a> {
98        &self.name
99    }
100
101    pub(crate) fn name(&self) -> &'a str {
102        self.name.as_str()
103    }
104}
105
106impl<'a, C: Context> FromTokens<'a> for DirectiveDefinition<'a, C> {
107    fn from_tokens(
108        tokens: &mut impl Tokens<'a>,
109        depth_limiter: DepthLimiter,
110    ) -> Result<Self, ParseError> {
111        let description = tokens.next_if_string_value();
112        tokens.expect_name_value(Self::DIRECTIVE_IDENTIFIER)?;
113        tokens.expect_punctuator(PunctuatorType::At)?;
114        let name = tokens.expect_name()?;
115        let arguments_definition =
116            ArgumentsDefinition::try_from_tokens(tokens, depth_limiter.bump()?)?;
117        let is_repeatable = tokens
118            .next_if_name_matches(Self::REPEATABLE_IDENTIFIER)
119            .is_some();
120        tokens.expect_name_value(Self::ON_IDENTIFIER)?;
121        let locations = DirectiveLocations::from_tokens(tokens, depth_limiter.bump()?)?;
122        Ok(Self {
123            description,
124            name,
125            arguments_definition,
126            is_repeatable,
127            locations,
128            is_builtin: false,
129        })
130    }
131}
132
133#[derive(Debug)]
134pub struct DirectiveLocation {
135    inner: CoreDirectiveLocation,
136    _span: Span,
137}
138
139impl<'a> FromTokens<'a> for DirectiveLocation {
140    fn from_tokens(tokens: &mut impl Tokens<'a>, _: DepthLimiter) -> Result<Self, ParseError> {
141        tokens.expect_name().and_then(
142            |name| match CoreDirectiveLocation::from_str(name.as_ref()) {
143                Ok(inner) => Ok(Self {
144                    inner,
145                    _span: name.into(),
146                }),
147                Err(_) => Err(ParseError::ExpectedOneOf {
148                    span: name.into(),
149                    values: CoreDirectiveLocation::POSSIBLE_VALUES,
150                }),
151            },
152        )
153    }
154}
155
156impl AsRef<CoreDirectiveLocation> for DirectiveLocation {
157    fn as_ref(&self) -> &CoreDirectiveLocation {
158        &self.inner
159    }
160}
161
162#[derive(Debug)]
163#[repr(transparent)]
164pub struct DirectiveLocations(Vec<DirectiveLocation>);
165
166impl AsIter for DirectiveLocations {
167    type Item = CoreDirectiveLocation;
168    type Iterator<'a> = std::iter::Map<
169        std::slice::Iter<'a, DirectiveLocation>,
170        fn(&'a DirectiveLocation) -> &'a CoreDirectiveLocation,
171    >;
172
173    fn iter(&self) -> Self::Iterator<'_> {
174        self.0.iter().map(AsRef::as_ref)
175    }
176}
177
178impl<'a> FromTokens<'a> for DirectiveLocations {
179    fn from_tokens(
180        tokens: &mut impl Tokens<'a>,
181        depth_limiter: DepthLimiter,
182    ) -> Result<Self, ParseError> {
183        tokens.next_if_punctuator(PunctuatorType::Pipe);
184        let mut directive_locations: Vec<DirectiveLocation> = vec![DirectiveLocation::from_tokens(
185            tokens,
186            depth_limiter.bump()?,
187        )?];
188        while tokens.next_if_punctuator(PunctuatorType::Pipe).is_some() {
189            directive_locations.push(DirectiveLocation::from_tokens(
190                tokens,
191                depth_limiter.bump()?,
192            )?);
193        }
194        Ok(Self(directive_locations))
195    }
196}