Skip to content

Commit

Permalink
Add some documentation to graphql_query/ in trustfall_core (#138)
Browse files Browse the repository at this point in the history
* Add some documentation to graphql_query/

* Fix rustfmt

* Ignore doctests for directive examples

* Apply suggestions from code review

Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com>

* Update trustfall_core/src/graphql_query/directives.rs

Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com>

* Fix rustfmt

This is what you get for merging on GitHub...

---------

Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com>
  • Loading branch information
ginger51011 and obi1kenobi authored Feb 11, 2023
1 parent 4fdf5af commit 83575e6
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 0 deletions.
79 changes: 79 additions & 0 deletions trustfall_core/src/graphql_query/directives.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Directives in Trustfall can be identified by their prefix: `@`.
//! This module contains the logic for parsing Trustfall query directives.
use std::{collections::HashSet, convert::TryFrom, num::NonZeroUsize, sync::Arc};

use async_graphql_parser::{types::Directive, Positioned};
Expand All @@ -9,14 +11,39 @@ use crate::ir::{Operation, TransformationKind};

use super::error::ParseError;

/// A value passed as an operator argument in a Trustfall query, for example as in
/// the `value` array of the `@filter` directive (see [FilterDirective]).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum OperatorArgument {
/// Reference to a variable provided to the query. Variable names are always
/// prefixed with `$`.
VariableRef(Arc<str>),

/// Reference to a tagged value encountered elsewhere
/// in the query and marked with the `@tag` directive -- see [TagDirective].
/// Tag names are always prefixed with `%`.
TagRef(Arc<str>),
}

/// A Trustfall `@filter` directive.
///
/// The following Trustfall filter directive and Rust value would be
/// equivalent:
///
/// ```graphql
/// @filter(op: ">=", value: ["$some_value"])
/// ```
///
/// and
///
/// ```ignore
/// FilterDirective {
/// operation: Operation::GreaterThanOrEqual((), OperatorArgument::VariableRef(Arc::new("$some_value")))
/// }
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub(crate) struct FilterDirective {
/// Describes which operation should be made by the filter
pub operation: Operation<(), OperatorArgument>,
}

Expand Down Expand Up @@ -152,8 +179,21 @@ impl TryFrom<&Positioned<Directive>> for FilterDirective {
}
}

/// A Trustfall `@output` directive.
///
/// For example, the following Trustfall and Rust would be equivalent:
/// ```graphql
/// @output(name: "betterName")
/// ```
///
/// and
///
/// ```ignore
/// OutputDirective { name: Some(Arc::new("betterName"))}
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub(crate) struct OutputDirective {
/// The name that should be used for this field when it is given as output
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<Arc<str>>,
}
Expand Down Expand Up @@ -214,8 +254,21 @@ impl TryFrom<&Positioned<Directive>> for OutputDirective {
}
}

/// A Trustfall `@transform` directive.
///
/// For example, the following Trustfall and Rust would be equivalent:
/// ```graphql
/// @transform(op: "count")
/// ```
///
/// and
///
/// ```ignore
/// TransformDirective { kind: TransformationKind::Count }
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub(crate) struct TransformDirective {
/// The `op` in a GraphQL `@transform`
pub kind: TransformationKind,
}

Expand Down Expand Up @@ -277,6 +330,18 @@ impl TryFrom<&Positioned<Directive>> for TransformDirective {
}
}

/// A Trustfall `@tag` directive.
///
/// For example, the following Trustfall and Rust would be equivalent:
/// ```graphql
/// @tag(name: "%tag_name")
/// ```
///
/// and
///
/// ```ignore
/// TagDirective { name: Some(Arc::new("%tag_name"))}
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub(crate) struct TagDirective {
#[serde(default, skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -337,6 +402,7 @@ impl TryFrom<&Positioned<Directive>> for TagDirective {
}
}

/// A Trustfall `@optional` directive.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub(crate) struct OptionalDirective {}

Expand All @@ -357,6 +423,7 @@ impl TryFrom<&Positioned<Directive>> for OptionalDirective {
}
}

/// A Trustfall `@fold` directive.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub(crate) struct FoldDirective {}

Expand All @@ -377,6 +444,18 @@ impl TryFrom<&Positioned<Directive>> for FoldDirective {
}
}

/// A Trustfall `@recurse` directive.
///
/// For example, the following Trustfall and Rust would be equivalent:
/// ```graphql
/// @recurse(depth: 1)
/// ```
///
/// and
///
/// ```ignore
/// RecurseDirective { depth: NonZeroUsize::new(1usize)}
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub(crate) struct RecurseDirective {
pub depth: NonZeroUsize,
Expand Down
1 change: 1 addition & 0 deletions trustfall_core/src/graphql_query/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Errors from parsing Trustfall queries
use async_graphql_parser::Pos;
use async_graphql_value::Value;
use serde::{ser::Error as SerError, Deserialize, Serialize, Serializer};
Expand Down
2 changes: 2 additions & 0 deletions trustfall_core/src/graphql_query/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Parsing Trustfall queries into Rust types,
//! which are then handed to the frontend for further processing.
pub(crate) mod directives;
pub mod error;
pub(crate) mod query;
5 changes: 5 additions & 0 deletions trustfall_core/src/graphql_query/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ impl ParsedDirective {
}
}

/// Attempts to extract the query root from an [ExecutableDocument]
///
/// May return [ParseError] if the query is empty, there is no query root, or
/// the query root is not formatted properly
fn try_get_query_root(document: &ExecutableDocument) -> Result<&Positioned<Field>, ParseError> {
if let Some(v) = document.fragments.values().next() {
return Err(ParseError::DocumentContainsNonInlineFragments(v.pos));
Expand Down Expand Up @@ -507,6 +511,7 @@ fn make_transform_group(
})
}

/// Parses a query document. May fail if a query root is missing (see [try_get_query_root](try_get_query_root))
pub(crate) fn parse_document(document: &ExecutableDocument) -> Result<Query, ParseError> {
let query_root = try_get_query_root(document)?;

Expand Down

0 comments on commit 83575e6

Please sign in to comment.