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())
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}