bluejay_parser/ast/definition/
directive_definition.rs1use 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()).unwrap();
45
46 definition.is_builtin = true;
47 definition
48 }
49}
50
51#[derive(Debug)]
52pub struct DirectiveDefinition<'a, C: Context> {
53 description: Option<StringValue<'a>>,
54 name: Name<'a>,
55 arguments_definition: Option<ArgumentsDefinition<'a, C>>,
56 is_repeatable: bool,
57 locations: DirectiveLocations,
58 is_builtin: bool,
59}
60
61impl<'a, C: Context> CoreDirectiveDefinition for DirectiveDefinition<'a, C> {
62 type ArgumentsDefinition = ArgumentsDefinition<'a, C>;
63 type DirectiveLocations = DirectiveLocations;
64
65 fn description(&self) -> Option<&str> {
66 self.description.as_ref().map(AsRef::as_ref)
67 }
68
69 fn name(&self) -> &str {
70 self.name.as_ref()
71 }
72
73 fn arguments_definition(&self) -> Option<&Self::ArgumentsDefinition> {
74 self.arguments_definition.as_ref()
75 }
76
77 fn is_repeatable(&self) -> bool {
78 self.is_repeatable
79 }
80
81 fn locations(&self) -> &Self::DirectiveLocations {
82 &self.locations
83 }
84
85 fn is_builtin(&self) -> bool {
86 self.is_builtin
87 }
88}
89
90impl<'a, C: Context> DirectiveDefinition<'a, C> {
91 pub(crate) const DIRECTIVE_IDENTIFIER: &'static str = "directive";
92 const REPEATABLE_IDENTIFIER: &'static str = "repeatable";
93 const ON_IDENTIFIER: &'static str = "on";
94
95 pub(crate) fn name_token(&self) -> &Name<'a> {
96 &self.name
97 }
98
99 pub(crate) fn name(&self) -> &'a str {
100 self.name.as_str()
101 }
102}
103
104impl<'a, C: Context> FromTokens<'a> for DirectiveDefinition<'a, C> {
105 fn from_tokens(
106 tokens: &mut impl Tokens<'a>,
107 depth_limiter: DepthLimiter,
108 ) -> Result<Self, ParseError> {
109 let description = tokens.next_if_string_value();
110 tokens.expect_name_value(Self::DIRECTIVE_IDENTIFIER)?;
111 tokens.expect_punctuator(PunctuatorType::At)?;
112 let name = tokens.expect_name()?;
113 let arguments_definition =
114 ArgumentsDefinition::try_from_tokens(tokens, depth_limiter.bump()?).transpose()?;
115 let is_repeatable = tokens
116 .next_if_name_matches(Self::REPEATABLE_IDENTIFIER)
117 .is_some();
118 tokens.expect_name_value(Self::ON_IDENTIFIER)?;
119 let locations = DirectiveLocations::from_tokens(tokens, depth_limiter.bump()?)?;
120 Ok(Self {
121 description,
122 name,
123 arguments_definition,
124 is_repeatable,
125 locations,
126 is_builtin: false,
127 })
128 }
129}
130
131#[derive(Debug)]
132pub struct DirectiveLocation {
133 inner: CoreDirectiveLocation,
134 _span: Span,
135}
136
137impl<'a> FromTokens<'a> for DirectiveLocation {
138 fn from_tokens(tokens: &mut impl Tokens<'a>, _: DepthLimiter) -> Result<Self, ParseError> {
139 tokens.expect_name().and_then(
140 |name| match CoreDirectiveLocation::from_str(name.as_ref()) {
141 Ok(inner) => Ok(Self {
142 inner,
143 _span: name.into(),
144 }),
145 Err(_) => Err(ParseError::ExpectedOneOf {
146 span: name.into(),
147 values: CoreDirectiveLocation::POSSIBLE_VALUES,
148 }),
149 },
150 )
151 }
152}
153
154impl AsRef<CoreDirectiveLocation> for DirectiveLocation {
155 fn as_ref(&self) -> &CoreDirectiveLocation {
156 &self.inner
157 }
158}
159
160#[derive(Debug)]
161#[repr(transparent)]
162pub struct DirectiveLocations(Vec<DirectiveLocation>);
163
164impl AsIter for DirectiveLocations {
165 type Item = CoreDirectiveLocation;
166 type Iterator<'a> = std::iter::Map<
167 std::slice::Iter<'a, DirectiveLocation>,
168 fn(&'a DirectiveLocation) -> &'a CoreDirectiveLocation,
169 >;
170
171 fn iter(&self) -> Self::Iterator<'_> {
172 self.0.iter().map(AsRef::as_ref)
173 }
174}
175
176impl<'a> FromTokens<'a> for DirectiveLocations {
177 fn from_tokens(
178 tokens: &mut impl Tokens<'a>,
179 depth_limiter: DepthLimiter,
180 ) -> Result<Self, ParseError> {
181 tokens.next_if_punctuator(PunctuatorType::Pipe);
182 let mut directive_locations: Vec<DirectiveLocation> = vec![DirectiveLocation::from_tokens(
183 tokens,
184 depth_limiter.bump()?,
185 )?];
186 while tokens.next_if_punctuator(PunctuatorType::Pipe).is_some() {
187 directive_locations.push(DirectiveLocation::from_tokens(
188 tokens,
189 depth_limiter.bump()?,
190 )?);
191 }
192 Ok(Self(directive_locations))
193 }
194}