bluejay_parser/ast/definition/
directive_definition.rs1use crate::ast::definition::{ArgumentsDefinition, Context};
2use crate::ast::{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<'a, C: Context> From<BuiltinDirectiveDefinition> for DirectiveDefinition<'a, 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(tokens: &mut impl Tokens<'a>) -> Result<Self, ParseError> {
106 let description = tokens.next_if_string_value();
107 tokens.expect_name_value(Self::DIRECTIVE_IDENTIFIER)?;
108 tokens.expect_punctuator(PunctuatorType::At)?;
109 let name = tokens.expect_name()?;
110 let arguments_definition = ArgumentsDefinition::try_from_tokens(tokens).transpose()?;
111 let is_repeatable = tokens
112 .next_if_name_matches(Self::REPEATABLE_IDENTIFIER)
113 .is_some();
114 tokens.expect_name_value(Self::ON_IDENTIFIER)?;
115 let locations = DirectiveLocations::from_tokens(tokens)?;
116 Ok(Self {
117 description,
118 name,
119 arguments_definition,
120 is_repeatable,
121 locations,
122 is_builtin: false,
123 })
124 }
125}
126
127#[derive(Debug)]
128pub struct DirectiveLocation {
129 inner: CoreDirectiveLocation,
130 _span: Span,
131}
132
133impl<'a> FromTokens<'a> for DirectiveLocation {
134 fn from_tokens(tokens: &mut impl Tokens<'a>) -> Result<Self, ParseError> {
135 tokens.expect_name().and_then(
136 |name| match CoreDirectiveLocation::from_str(name.as_ref()) {
137 Ok(inner) => Ok(Self {
138 inner,
139 _span: name.into(),
140 }),
141 Err(_) => Err(ParseError::ExpectedOneOf {
142 span: name.into(),
143 values: CoreDirectiveLocation::POSSIBLE_VALUES,
144 }),
145 },
146 )
147 }
148}
149
150impl AsRef<CoreDirectiveLocation> for DirectiveLocation {
151 fn as_ref(&self) -> &CoreDirectiveLocation {
152 &self.inner
153 }
154}
155
156#[derive(Debug)]
157#[repr(transparent)]
158pub struct DirectiveLocations(Vec<DirectiveLocation>);
159
160impl AsIter for DirectiveLocations {
161 type Item = CoreDirectiveLocation;
162 type Iterator<'a> = std::iter::Map<
163 std::slice::Iter<'a, DirectiveLocation>,
164 fn(&'a DirectiveLocation) -> &'a CoreDirectiveLocation,
165 >;
166
167 fn iter(&self) -> Self::Iterator<'_> {
168 self.0.iter().map(AsRef::as_ref)
169 }
170}
171
172impl<'a> FromTokens<'a> for DirectiveLocations {
173 fn from_tokens(tokens: &mut impl Tokens<'a>) -> Result<Self, ParseError> {
174 tokens.next_if_punctuator(PunctuatorType::Pipe);
175 let mut directive_locations: Vec<DirectiveLocation> =
176 vec![DirectiveLocation::from_tokens(tokens)?];
177 while tokens.next_if_punctuator(PunctuatorType::Pipe).is_some() {
178 directive_locations.push(DirectiveLocation::from_tokens(tokens)?);
179 }
180 Ok(Self(directive_locations))
181 }
182}