Skip to content

Commit db5d9fb

Browse files
Optimize schema with ArcStr (#1247, #819)
- `juniper` crate: - `ast::Type`: - remove lifetime parameters - make it generic over string type - `MetaType`: - remove lifetime parameters - make `name()`, `description()` and `specified_by_url()` methods returning `ArcStr` - `EnumMeta`, `InputObjectMeta`, `InterfaceMeta`, `ListMeta`, `NullableMeta`, `ObjectMeta`, `PlaceholderMeta`, `ScalarMeta` and `UnionMeta`: - remove lifetime parameters - `meta::Field` and `meta::Argument`: - remove lifetime parameters - `meta::EnumValue`: - make `name` and `description` fields using `ArcStr` - `DeprecationStatus`: - make `Deprecated` variant using `ArcStr` - make `reason()` method returning `ArcStr` - `DirectiveType`: - remove lifetime parameters - make `name` and `description` fields using `ArcStr` - `SchemaType`: - remove lifetime parameters - make `is_subtype()` method accepting `DynType` instead of `Type` - `RootNode`: - remove lifetime parameters - `Registry`: - remove lifetime parameters - `types::Name` and `types::NameParseError`: - make fields using `ArcStr` instead of `String` - replace `FromStr` impl of `types::Name` with `new()` constructor - `GraphQLType`: - make `name()` method returning `ArcStr` - `GraphQLValue`: - make `type_name()` method returning `ArcStr` - `juniper_subscriptions` crate: - `Coordinator`: - remove lifetime parameters Co-authored-by: Kai Ren <[email protected]>
1 parent 8f69975 commit db5d9fb

File tree

105 files changed

+1940
-1838
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+1940
-1838
lines changed

benches/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@ impl Query {
9595
}
9696
}
9797

98-
pub fn new_schema() -> RootNode<'static, Query, EmptyMutation<Context>, EmptySubscription<Context>>
99-
{
98+
pub fn new_schema() -> RootNode<Query, EmptyMutation<Context>, EmptySubscription<Context>> {
10099
RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new())
101100
}
102101

book/src/quickstart.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ impl Mutation {
140140

141141
// Root schema consists of a query, a mutation, and a subscription.
142142
// Request queries can be executed against a `RootNode`.
143-
type Schema = juniper::RootNode<'static, Query, Mutation, EmptySubscription<Context>>;
143+
type Schema = juniper::RootNode<Query, Mutation, EmptySubscription<Context>>;
144144
#
145145
# fn main() {
146146
# _ = Schema::new(Query, Mutation, EmptySubscription::new());
@@ -190,7 +190,7 @@ impl Query {
190190
}
191191
}
192192

193-
type Schema = juniper::RootNode<'static, Query, EmptyMutation<Ctx>, EmptySubscription<Ctx>>;
193+
type Schema = juniper::RootNode<Query, EmptyMutation<Ctx>, EmptySubscription<Ctx>>;
194194

195195
fn main() {
196196
// Create a context.

book/src/schema/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl Mutation {
5252
}
5353
}
5454

55-
type Schema = RootNode<'static, Query, Mutation, EmptySubscription>;
55+
type Schema = RootNode<Query, Mutation, EmptySubscription>;
5656
#
5757
# fn main() {}
5858
```
@@ -138,7 +138,7 @@ impl Query {
138138
}
139139
}
140140

141-
type Schema = RootNode<'static, Query, EmptyMutation, EmptySubscription>;
141+
type Schema = RootNode<Query, EmptyMutation, EmptySubscription>;
142142

143143
fn main() {
144144
// Run the built-in introspection query.

book/src/schema/introspection.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ impl Query {
4545
}
4646
}
4747

48-
type Schema = RootNode<'static, Query, EmptyMutation, EmptySubscription>;
48+
type Schema = RootNode<Query, EmptyMutation, EmptySubscription>;
4949

5050
fn main() {
5151
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new())

book/src/schema/subscriptions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ While we can implement [`SubscriptionCoordinator`] ourselves, [Juniper] contains
113113
# }
114114
# }
115115
#
116-
type Schema = RootNode<'static, Query, EmptyMutation<Database>, Subscription>;
116+
type Schema = RootNode<Query, EmptyMutation<Database>, Subscription>;
117117

118118
fn schema() -> Schema {
119119
Schema::new(Query, EmptyMutation::new(), Subscription)

juniper/CHANGELOG.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,39 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
3131
- `uuid::Uuid`.
3232
- Renamed `ObjectId` scalar to `ObjectID` in types: ([#1277])
3333
- `bson::oid::ObjectId`.
34+
- Optimized schema implementation with [`arcstr` crate]: ([#1247], [#819])
35+
- `ast::Type`:
36+
- Removed lifetime parameters.
37+
- Made it generic over string type.
38+
- `MetaType`:
39+
- Removed lifetime parameters.
40+
- Made `name()`, `description()` and `specified_by_url()` methods returning `ArcStr`.
41+
- `EnumMeta`, `InputObjectMeta`, `InterfaceMeta`, `ListMeta`, `NullableMeta`, `ObjectMeta`, `PlaceholderMeta`, `ScalarMeta` and `UnionMeta`:
42+
- Removed lifetime parameters.
43+
- `meta::Field` and `meta::Argument`:
44+
- Removed lifetime parameters.
45+
- `meta::EnumValue`:
46+
- Made `name` and `description` fields using `ArcStr`.
47+
- `DeprecationStatus`:
48+
- Made `Deprecated` variant using `ArcStr`.
49+
- Made `reason()` method returning `ArcStr`.
50+
- `DirectiveType`:
51+
- Removed lifetime parameters.
52+
- Made `name` and `description` fields using `ArcStr`.
53+
- `SchemaType`:
54+
- Removed lifetime parameters.
55+
- Made `is_subtype()` method accepting `DynType` instead of `Type`.
56+
- `RootNode`:
57+
- Removed lifetime parameters.
58+
- `Registry`:
59+
- Removed lifetime parameters.
60+
- `types::Name` and `types::NameParseError`:
61+
- Made fields using `ArcStr` instead of `String`.
62+
- Replaced `FromStr` impl of `types::Name` with `new()` constructor.
63+
- `GraphQLType`:
64+
- Made `name()` method returning `ArcStr`.
65+
- `GraphQLValue`:
66+
- Made `type_name()` method returning `ArcStr`.
3467

3568
### Added
3669

@@ -53,6 +86,8 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
5386

5487
- Incorrect error propagation inside fragments. ([#1318], [#1287])
5588

89+
[#819]: /../../issues/819
90+
[#1247]: /../../pull/1247
5691
[#1252]: /../../pull/1252
5792
[#1270]: /../../issues/1270
5893
[#1271]: /../../pull/1271
@@ -264,6 +299,7 @@ See [old CHANGELOG](/../../blob/juniper-v0.15.12/juniper/CHANGELOG.md).
264299

265300

266301
[`anyhow` crate]: https://docs.rs/anyhow
302+
[`arcstr` crate]: https://docs.rs/arcstr
267303
[`bigdecimal` crate]: https://docs.rs/bigdecimal
268304
[`bson` crate]: https://docs.rs/bson
269305
[`chrono` crate]: https://docs.rs/chrono

juniper/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ uuid = ["dep:uuid"]
4343

4444
[dependencies]
4545
anyhow = { version = "1.0.47", optional = true }
46+
arcstr = { version = "1.1", default-features = false }
4647
async-trait = "0.1.39"
4748
auto_enums = "0.8"
4849
bigdecimal = { version = "0.4", optional = true }

juniper/src/ast.rs

Lines changed: 77 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::{borrow::Cow, fmt, hash::Hash, slice, vec};
22

3+
use arcstr::ArcStr;
4+
35
use indexmap::IndexMap;
46

57
use crate::{
@@ -8,24 +10,70 @@ use crate::{
810
value::{DefaultScalarValue, ScalarValue},
911
};
1012

11-
/// A type literal in the syntax tree
13+
/// Type literal in a syntax tree.
1214
///
13-
/// This enum carries no semantic information and might refer to types that do
14-
/// not exist.
15-
#[derive(Clone, Eq, PartialEq, Debug)]
16-
pub enum Type<'a> {
17-
/// A nullable named type, e.g. `String`
18-
Named(Cow<'a, str>),
19-
/// A nullable list type, e.g. `[String]`
15+
/// This enum carries no semantic information and might refer to types that do not exist.
16+
#[derive(Clone, Debug, Eq, PartialEq)]
17+
pub enum Type<N = ArcStr> {
18+
/// `null`able named type, e.g. `String`.
19+
Named(N),
20+
21+
/// `null`able list type, e.g. `[String]`.
2022
///
21-
/// The list itself is what's nullable, the containing type might be non-null.
22-
List(Box<Type<'a>>, Option<usize>),
23-
/// A non-null named type, e.g. `String!`
24-
NonNullNamed(Cow<'a, str>),
25-
/// A non-null list type, e.g. `[String]!`.
23+
/// The list itself is `null`able, the containing [`Type`] might be non-`null`.
24+
List(Box<Type<N>>, Option<usize>),
25+
26+
/// Non-`null` named type, e.g. `String!`.
27+
NonNullNamed(N),
28+
29+
/// Non-`null` list type, e.g. `[String]!`.
2630
///
27-
/// The list itself is what's non-null, the containing type might be null.
28-
NonNullList(Box<Type<'a>>, Option<usize>),
31+
/// The list itself is non-`null`, the containing [`Type`] might be `null`able.
32+
NonNullList(Box<Type<N>>, Option<usize>),
33+
}
34+
35+
impl<N: fmt::Display> fmt::Display for Type<N> {
36+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37+
match self {
38+
Self::Named(n) => write!(f, "{n}"),
39+
Self::NonNullNamed(n) => write!(f, "{n}!"),
40+
Self::List(t, _) => write!(f, "[{t}]"),
41+
Self::NonNullList(t, _) => write!(f, "[{t}]!"),
42+
}
43+
}
44+
}
45+
46+
impl<N: AsRef<str>> Type<N> {
47+
/// Returns the name of this named [`Type`].
48+
///
49+
/// Only applies to named [`Type`]s. Lists will return [`None`].
50+
#[must_use]
51+
pub fn name(&self) -> Option<&str> {
52+
match self {
53+
Self::Named(n) | Self::NonNullNamed(n) => Some(n.as_ref()),
54+
Self::List(..) | Self::NonNullList(..) => None,
55+
}
56+
}
57+
58+
/// Returns the innermost name of this [`Type`] by unpacking lists.
59+
///
60+
/// All [`Type`] literals contain exactly one named type.
61+
#[must_use]
62+
pub fn innermost_name(&self) -> &str {
63+
match self {
64+
Self::Named(n) | Self::NonNullNamed(n) => n.as_ref(),
65+
Self::List(l, ..) | Self::NonNullList(l, ..) => l.innermost_name(),
66+
}
67+
}
68+
69+
/// Indicates whether this [`Type`] can only represent non-`null` values.
70+
#[must_use]
71+
pub fn is_non_null(&self) -> bool {
72+
match self {
73+
Self::NonNullList(..) | Self::NonNullNamed(..) => true,
74+
Self::List(..) | Self::Named(..) => false,
75+
}
76+
}
2977
}
3078

3179
/// A JSON-like value that can be passed into the query execution, either
@@ -34,8 +82,8 @@ pub enum Type<'a> {
3482
///
3583
/// Lists and objects variants are _spanned_, i.e. they contain a reference to
3684
/// their position in the source file, if available.
37-
#[derive(Clone, Debug, PartialEq)]
3885
#[expect(missing_docs, reason = "self-explanatory")]
86+
#[derive(Clone, Debug, PartialEq)]
3987
pub enum InputValue<S = DefaultScalarValue> {
4088
Null,
4189
Scalar(S),
@@ -45,24 +93,24 @@ pub enum InputValue<S = DefaultScalarValue> {
4593
Object(Vec<(Spanning<String>, Spanning<InputValue<S>>)>),
4694
}
4795

48-
#[derive(Clone, PartialEq, Debug)]
96+
#[derive(Clone, Debug, PartialEq)]
4997
pub struct VariableDefinition<'a, S> {
50-
pub var_type: Spanning<Type<'a>>,
98+
pub var_type: Spanning<Type<&'a str>>,
5199
pub default_value: Option<Spanning<InputValue<S>>>,
52100
pub directives: Option<Vec<Spanning<Directive<'a, S>>>>,
53101
}
54102

55-
#[derive(Clone, PartialEq, Debug)]
103+
#[derive(Clone, Debug, PartialEq)]
56104
pub struct Arguments<'a, S> {
57105
pub items: Vec<(Spanning<&'a str>, Spanning<InputValue<S>>)>,
58106
}
59107

60-
#[derive(Clone, PartialEq, Debug)]
108+
#[derive(Clone, Debug, PartialEq)]
61109
pub struct VariableDefinitions<'a, S> {
62110
pub items: Vec<(Spanning<&'a str>, VariableDefinition<'a, S>)>,
63111
}
64112

65-
#[derive(Clone, PartialEq, Debug)]
113+
#[derive(Clone, Debug, PartialEq)]
66114
pub struct Field<'a, S> {
67115
pub alias: Option<Spanning<&'a str>>,
68116
pub name: Spanning<&'a str>,
@@ -71,13 +119,13 @@ pub struct Field<'a, S> {
71119
pub selection_set: Option<Vec<Selection<'a, S>>>,
72120
}
73121

74-
#[derive(Clone, PartialEq, Debug)]
122+
#[derive(Clone, Debug, PartialEq)]
75123
pub struct FragmentSpread<'a, S> {
76124
pub name: Spanning<&'a str>,
77125
pub directives: Option<Vec<Spanning<Directive<'a, S>>>>,
78126
}
79127

80-
#[derive(Clone, PartialEq, Debug)]
128+
#[derive(Clone, Debug, PartialEq)]
81129
pub struct InlineFragment<'a, S> {
82130
pub type_condition: Option<Spanning<&'a str>>,
83131
pub directives: Option<Vec<Spanning<Directive<'a, S>>>>,
@@ -99,15 +147,15 @@ pub struct InlineFragment<'a, S> {
99147
/// }
100148
/// }
101149
/// ```
102-
#[derive(Clone, PartialEq, Debug)]
103150
#[expect(missing_docs, reason = "self-explanatory")]
151+
#[derive(Clone, Debug, PartialEq)]
104152
pub enum Selection<'a, S = DefaultScalarValue> {
105153
Field(Spanning<Field<'a, S>>),
106154
FragmentSpread(Spanning<FragmentSpread<'a, S>>),
107155
InlineFragment(Spanning<InlineFragment<'a, S>>),
108156
}
109157

110-
#[derive(Clone, PartialEq, Debug)]
158+
#[derive(Clone, Debug, PartialEq)]
111159
pub struct Directive<'a, S> {
112160
pub name: Spanning<&'a str>,
113161
pub arguments: Option<Spanning<Arguments<'a, S>>>,
@@ -122,7 +170,7 @@ pub enum OperationType {
122170
}
123171

124172
#[expect(missing_docs, reason = "self-explanatory")]
125-
#[derive(Clone, PartialEq, Debug)]
173+
#[derive(Clone, Debug, PartialEq)]
126174
pub struct Operation<'a, S> {
127175
pub operation_type: OperationType,
128176
pub name: Option<Spanning<&'a str>>,
@@ -131,7 +179,7 @@ pub struct Operation<'a, S> {
131179
pub selection_set: Vec<Selection<'a, S>>,
132180
}
133181

134-
#[derive(Clone, PartialEq, Debug)]
182+
#[derive(Clone, Debug, PartialEq)]
135183
pub struct Fragment<'a, S> {
136184
pub name: Spanning<&'a str>,
137185
pub type_condition: Spanning<&'a str>,
@@ -140,7 +188,7 @@ pub struct Fragment<'a, S> {
140188
}
141189

142190
#[doc(hidden)]
143-
#[derive(Clone, PartialEq, Debug)]
191+
#[derive(Clone, Debug, PartialEq)]
144192
pub enum Definition<'a, S> {
145193
Operation(Spanning<Operation<'a, S>>),
146194
Fragment(Spanning<Fragment<'a, S>>),
@@ -194,44 +242,6 @@ pub trait ToInputValue<S = DefaultScalarValue>: Sized {
194242
fn to_input_value(&self) -> InputValue<S>;
195243
}
196244

197-
impl Type<'_> {
198-
/// Get the name of a named type.
199-
///
200-
/// Only applies to named types; lists will return `None`.
201-
pub fn name(&self) -> Option<&str> {
202-
match *self {
203-
Type::Named(ref n) | Type::NonNullNamed(ref n) => Some(n),
204-
_ => None,
205-
}
206-
}
207-
208-
/// Get the innermost name by unpacking lists
209-
///
210-
/// All type literals contain exactly one named type.
211-
pub fn innermost_name(&self) -> &str {
212-
match *self {
213-
Type::Named(ref n) | Type::NonNullNamed(ref n) => n,
214-
Type::List(ref l, _) | Type::NonNullList(ref l, _) => l.innermost_name(),
215-
}
216-
}
217-
218-
/// Determines if a type only can represent non-null values.
219-
pub fn is_non_null(&self) -> bool {
220-
matches!(*self, Type::NonNullNamed(_) | Type::NonNullList(..))
221-
}
222-
}
223-
224-
impl fmt::Display for Type<'_> {
225-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226-
match self {
227-
Self::Named(n) => write!(f, "{n}"),
228-
Self::NonNullNamed(n) => write!(f, "{n}!"),
229-
Self::List(t, _) => write!(f, "[{t}]"),
230-
Self::NonNullList(t, _) => write!(f, "[{t}]!"),
231-
}
232-
}
233-
}
234-
235245
impl<S> InputValue<S> {
236246
/// Construct a `null` value.
237247
pub fn null() -> Self {
@@ -574,7 +584,7 @@ impl<'a, S> Arguments<'a, S> {
574584
}
575585

576586
impl<'a, S> VariableDefinitions<'a, S> {
577-
pub fn iter(&self) -> slice::Iter<(Spanning<&'a str>, VariableDefinition<S>)> {
587+
pub fn iter(&self) -> slice::Iter<(Spanning<&'a str>, VariableDefinition<'a, S>)> {
578588
self.items.iter()
579589
}
580590
}

0 commit comments

Comments
 (0)