Skip to content

Commit e9ea0aa

Browse files
authored
feat: parse descriptions in executable documents (#1182)
1 parent 22aadef commit e9ea0aa

File tree

25 files changed

+3397
-2034
lines changed

25 files changed

+3397
-2034
lines changed

cynic-parser/ast-generator/domain/executable.graphql

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
union ExecutableDefinition
55
@file(name: "definition")
66
@variant(names: ["Operation", "Fragment"]) =
7-
OperationDefinition
7+
| OperationDefinition
88
| FragmentDefinition
99

1010
type OperationDefinition @file(name: "operation") {
11+
description: Description
1112
operation_type: OperationType! @spanned(nullable: true)
1213
name: String @spanned
1314
variable_definitions: [VariableDefinition]
@@ -16,6 +17,7 @@ type OperationDefinition @file(name: "operation") {
1617
}
1718

1819
type FragmentDefinition @file(name: "fragment") {
20+
description: Description
1921
name: String! @spanned
2022
type_condition: String! @spanned
2123
directives: [Directive]
@@ -25,7 +27,7 @@ type FragmentDefinition @file(name: "fragment") {
2527
union Selection
2628
@file(name: "selections")
2729
@variant(names: ["Field", "InlineFragment", "FragmentSpread"]) =
28-
FieldSelection
30+
| FieldSelection
2931
| InlineFragment
3032
| FragmentSpread
3133

@@ -59,12 +61,20 @@ type Argument @file(name: "argument") {
5961
}
6062

6163
type VariableDefinition @file(name: "variable") {
64+
description: Description
6265
name: String! @spanned
6366
ty: Type!
6467
default_value: ConstValue
6568
directives: [Directive]
6669
}
6770

71+
type Description @file(name: "descriptions") {
72+
literal: StringLiteral!
73+
span: Span!
74+
}
75+
76+
scalar Span @inline
77+
6878
# OperationType is marked as @inline which means it just gets
6979
# stored inside records instead of getting IDs & records.
7080
scalar OperationType @inline
@@ -78,3 +88,5 @@ scalar ConstValue @file(name: "value")
7888
# String is built in, but easier to implement stuff if its just in the .graphql file
7989
# It is also special cased a bit in the rust code
8090
scalar String
91+
92+
scalar StringLiteral @file(name: "strings")

cynic-parser/src/common/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
mod id_range;
22
mod int_value;
3+
mod string_literal;
34
mod strings;
45
mod types;
56

67
pub use id_range::{IdOperations, IdRange, IdRangeIter};
78
pub use int_value::IntValue;
9+
pub use string_literal::{StringLiteral, StringLiteralKind};
810
pub use strings::MalformedStringError;
911
pub use types::*;
1012

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use std::{borrow::Cow, fmt};
2+
3+
use crate::common::trim_block_string_whitespace;
4+
5+
#[derive(Clone, Copy, Debug)]
6+
pub struct StringLiteral<'a>(StringLiteralInner<'a>);
7+
8+
impl<'a> StringLiteral<'a> {
9+
pub(crate) fn new_string(inner: &'a str) -> Self {
10+
Self(StringLiteralInner::String(inner))
11+
}
12+
13+
pub(crate) fn new_block(inner: &'a str) -> Self {
14+
Self(StringLiteralInner::BlockString(inner))
15+
}
16+
}
17+
18+
#[derive(Clone, Copy, Debug)]
19+
enum StringLiteralInner<'a> {
20+
String(&'a str),
21+
BlockString(&'a str),
22+
}
23+
24+
#[derive(Clone, Copy, Debug)]
25+
pub enum StringLiteralKind {
26+
String,
27+
Block,
28+
}
29+
30+
impl<'a> StringLiteral<'a> {
31+
pub fn to_cow(&self) -> Cow<'a, str> {
32+
match self.0 {
33+
StringLiteralInner::String(string) => Cow::Borrowed(string),
34+
StringLiteralInner::BlockString(string) => {
35+
// This is more intense - we need to unquote the string
36+
//
37+
// TODO: Look into recording what work needs done at parse time
38+
Cow::Owned(trim_block_string_whitespace(string))
39+
}
40+
}
41+
}
42+
43+
pub fn raw_untrimmed_str(&self) -> &'a str {
44+
match self.0 {
45+
StringLiteralInner::String(string) => string,
46+
StringLiteralInner::BlockString(string) => string,
47+
}
48+
}
49+
50+
pub fn kind(&self) -> StringLiteralKind {
51+
match self.0 {
52+
StringLiteralInner::String(_) => StringLiteralKind::String,
53+
StringLiteralInner::BlockString(_) => StringLiteralKind::Block,
54+
}
55+
}
56+
}
57+
58+
impl fmt::Display for StringLiteral<'_> {
59+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60+
f.write_str(self.to_cow().as_ref())
61+
}
62+
}

cynic-parser/src/executable/generated.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ mod prelude {
2121

2222
pub mod argument;
2323
pub mod definition;
24+
pub mod descriptions;
2425
pub mod directive;
2526
pub mod fragment;
2627
pub mod operation;
@@ -33,3 +34,7 @@ mod value {
3334

3435
pub use crate::values::{ConstValue, Value};
3536
}
37+
38+
mod strings {
39+
pub(super) use crate::common::StringLiteral;
40+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use super::prelude::*;
2+
use super::{
3+
ExecutableId,
4+
ids::{DescriptionId, StringLiteralId},
5+
strings::StringLiteral,
6+
};
7+
#[allow(unused_imports)]
8+
use std::fmt::{self, Write};
9+
10+
pub struct DescriptionRecord {
11+
pub literal: StringLiteralId,
12+
pub span: Span,
13+
}
14+
15+
#[derive(Clone, Copy)]
16+
pub struct Description<'a>(pub(in super::super) ReadContext<'a, DescriptionId>);
17+
18+
impl<'a> Description<'a> {
19+
pub fn literal(&self) -> StringLiteral<'a> {
20+
let document = self.0.document;
21+
document.read(document.lookup(self.0.id).literal)
22+
}
23+
pub fn span(&self) -> Span {
24+
let document = self.0.document;
25+
document.lookup(self.0.id).span
26+
}
27+
}
28+
29+
impl Description<'_> {
30+
pub fn id(&self) -> DescriptionId {
31+
self.0.id
32+
}
33+
}
34+
35+
impl fmt::Debug for Description<'_> {
36+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37+
f.debug_struct("Description")
38+
.field("literal", &self.literal())
39+
.field("span", &self.span())
40+
.finish()
41+
}
42+
}
43+
44+
impl ExecutableId for DescriptionId {
45+
type Reader<'a> = Description<'a>;
46+
fn read(self, document: &ExecutableDocument) -> Self::Reader<'_> {
47+
Description(ReadContext { id: self, document })
48+
}
49+
}
50+
51+
impl IdReader for Description<'_> {
52+
type Id = DescriptionId;
53+
type Reader<'a> = Description<'a>;
54+
fn new(id: Self::Id, document: &'_ ExecutableDocument) -> Self::Reader<'_> {
55+
document.read(id)
56+
}
57+
}

cynic-parser/src/executable/generated/fragment.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
use super::prelude::*;
22
use super::{
33
ExecutableId,
4+
descriptions::Description,
45
directive::Directive,
5-
ids::{DirectiveId, FragmentDefinitionId, SelectionId},
6+
ids::{DescriptionId, DirectiveId, FragmentDefinitionId, SelectionId},
67
selections::Selection,
78
};
89
#[allow(unused_imports)]
910
use std::fmt::{self, Write};
1011

1112
pub struct FragmentDefinitionRecord {
13+
pub description: Option<DescriptionId>,
1214
pub name: StringId,
1315
pub name_span: Span,
1416
pub type_condition: StringId,
@@ -22,6 +24,13 @@ pub struct FragmentDefinitionRecord {
2224
pub struct FragmentDefinition<'a>(pub(in super::super) ReadContext<'a, FragmentDefinitionId>);
2325

2426
impl<'a> FragmentDefinition<'a> {
27+
pub fn description(&self) -> Option<Description<'a>> {
28+
let document = self.0.document;
29+
document
30+
.lookup(self.0.id)
31+
.description
32+
.map(|id| document.read(id))
33+
}
2534
pub fn name(&self) -> &'a str {
2635
let document = &self.0.document;
2736
document.lookup(document.lookup(self.0.id).name)
@@ -61,6 +70,7 @@ impl FragmentDefinition<'_> {
6170
impl fmt::Debug for FragmentDefinition<'_> {
6271
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6372
f.debug_struct("FragmentDefinition")
73+
.field("description", &self.description())
6474
.field("name", &self.name())
6575
.field("type_condition", &self.type_condition())
6676
.field("directives", &self.directives())

cynic-parser/src/executable/generated/operation.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
use super::prelude::*;
22
use super::{
33
ExecutableId,
4+
descriptions::Description,
45
directive::Directive,
5-
ids::{DirectiveId, OperationDefinitionId, SelectionId, VariableDefinitionId},
6+
ids::{DescriptionId, DirectiveId, OperationDefinitionId, SelectionId, VariableDefinitionId},
67
selections::Selection,
78
variable::VariableDefinition,
89
};
910
#[allow(unused_imports)]
1011
use std::fmt::{self, Write};
1112

1213
pub struct OperationDefinitionRecord {
14+
pub description: Option<DescriptionId>,
1315
pub operation_type: OperationType,
1416
pub operation_type_span: Option<Span>,
1517
pub name: Option<StringId>,
@@ -24,6 +26,13 @@ pub struct OperationDefinitionRecord {
2426
pub struct OperationDefinition<'a>(pub(in super::super) ReadContext<'a, OperationDefinitionId>);
2527

2628
impl<'a> OperationDefinition<'a> {
29+
pub fn description(&self) -> Option<Description<'a>> {
30+
let document = self.0.document;
31+
document
32+
.lookup(self.0.id)
33+
.description
34+
.map(|id| document.read(id))
35+
}
2736
pub fn operation_type(&self) -> OperationType {
2837
let document = self.0.document;
2938
document.lookup(self.0.id).operation_type
@@ -70,6 +79,7 @@ impl OperationDefinition<'_> {
7079
impl fmt::Debug for OperationDefinition<'_> {
7180
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7281
f.debug_struct("OperationDefinition")
82+
.field("description", &self.description())
7383
.field("operation_type", &self.operation_type())
7484
.field("name", &self.name())
7585
.field("variable_definitions", &self.variable_definitions())

cynic-parser/src/executable/generated/variable.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
use super::prelude::*;
22
use super::{
33
ExecutableId,
4+
descriptions::Description,
45
directive::Directive,
5-
ids::{ConstValueId, DirectiveId, TypeId, VariableDefinitionId},
6+
ids::{ConstValueId, DescriptionId, DirectiveId, TypeId, VariableDefinitionId},
67
types::Type,
78
value::ConstValue,
89
};
910
#[allow(unused_imports)]
1011
use std::fmt::{self, Write};
1112

1213
pub struct VariableDefinitionRecord {
14+
pub description: Option<DescriptionId>,
1315
pub name: StringId,
1416
pub name_span: Span,
1517
pub ty: TypeId,
@@ -21,6 +23,13 @@ pub struct VariableDefinitionRecord {
2123
pub struct VariableDefinition<'a>(pub(in super::super) ReadContext<'a, VariableDefinitionId>);
2224

2325
impl<'a> VariableDefinition<'a> {
26+
pub fn description(&self) -> Option<Description<'a>> {
27+
let document = self.0.document;
28+
document
29+
.lookup(self.0.id)
30+
.description
31+
.map(|id| document.read(id))
32+
}
2433
pub fn name(&self) -> &'a str {
2534
let document = &self.0.document;
2635
document.lookup(document.lookup(self.0.id).name)
@@ -55,6 +64,7 @@ impl VariableDefinition<'_> {
5564
impl fmt::Debug for VariableDefinition<'_> {
5665
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5766
f.debug_struct("VariableDefinition")
67+
.field("description", &self.description())
5868
.field("name", &self.name())
5969
.field("ty", &self.ty())
6070
.field("default_value", &self.default_value())

cynic-parser/src/executable/ids.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ make_id!(OperationDefinitionId, OperationDefinitionRecord, operations);
6262

6363
make_id!(FragmentDefinitionId, FragmentDefinitionRecord, fragments);
6464

65+
make_id!(DescriptionId, DescriptionRecord, descriptions);
66+
6567
make_id!(SelectionId, SelectionRecord, selections);
6668
impl_id_range_ops!(SelectionId);
6769

@@ -106,3 +108,9 @@ impl AstLookup<StringId> for ExecutableDocument {
106108
&self.strings[(index.0.get() - 1) as usize]
107109
}
108110
}
111+
112+
#[derive(Clone, Copy)]
113+
pub enum StringLiteralId {
114+
String(StringId),
115+
Block(BlockStringLiteralId),
116+
}

cynic-parser/src/executable/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub mod writer;
88

99
mod extensions;
1010
mod generated;
11+
mod string_literal;
1112
mod types;
1213
mod values;
1314

@@ -17,6 +18,7 @@ pub use self::{
1718
generated::{
1819
argument::Argument,
1920
definition::ExecutableDefinition,
21+
descriptions::Description,
2022
directive::Directive,
2123
fragment::FragmentDefinition,
2224
operation::OperationDefinition,
@@ -46,6 +48,7 @@ pub struct ExecutableDocument {
4648
directives: Vec<storage::DirectiveRecord>,
4749
arguments: Vec<storage::ArgumentRecord>,
4850
variables: Vec<storage::VariableDefinitionRecord>,
51+
descriptions: Vec<storage::DescriptionRecord>,
4952

5053
types: Vec<types::TypeRecord>,
5154

@@ -105,6 +108,7 @@ pub mod storage {
105108
generated::{
106109
argument::ArgumentRecord,
107110
definition::ExecutableDefinitionRecord,
111+
descriptions::DescriptionRecord,
108112
directive::DirectiveRecord,
109113
fragment::FragmentDefinitionRecord,
110114
operation::OperationDefinitionRecord,

0 commit comments

Comments
 (0)