diff --git a/trustfall_core/src/graphql_query/directives.rs b/trustfall_core/src/graphql_query/directives.rs index a29d74b7..1967ca5f 100644 --- a/trustfall_core/src/graphql_query/directives.rs +++ b/trustfall_core/src/graphql_query/directives.rs @@ -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}; @@ -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), + + /// 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), } +/// 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>, } @@ -152,8 +179,21 @@ impl TryFrom<&Positioned> 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>, } @@ -214,8 +254,21 @@ impl TryFrom<&Positioned> 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, } @@ -277,6 +330,18 @@ impl TryFrom<&Positioned> 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")] @@ -337,6 +402,7 @@ impl TryFrom<&Positioned> for TagDirective { } } +/// A Trustfall `@optional` directive. #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub(crate) struct OptionalDirective {} @@ -357,6 +423,7 @@ impl TryFrom<&Positioned> for OptionalDirective { } } +/// A Trustfall `@fold` directive. #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub(crate) struct FoldDirective {} @@ -377,6 +444,18 @@ impl TryFrom<&Positioned> 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, diff --git a/trustfall_core/src/graphql_query/error.rs b/trustfall_core/src/graphql_query/error.rs index f803ffbc..abc70fbb 100644 --- a/trustfall_core/src/graphql_query/error.rs +++ b/trustfall_core/src/graphql_query/error.rs @@ -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}; diff --git a/trustfall_core/src/graphql_query/mod.rs b/trustfall_core/src/graphql_query/mod.rs index 7d65045d..39e71527 100644 --- a/trustfall_core/src/graphql_query/mod.rs +++ b/trustfall_core/src/graphql_query/mod.rs @@ -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; diff --git a/trustfall_core/src/graphql_query/query.rs b/trustfall_core/src/graphql_query/query.rs index cef4b755..dd3f4c24 100644 --- a/trustfall_core/src/graphql_query/query.rs +++ b/trustfall_core/src/graphql_query/query.rs @@ -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, ParseError> { if let Some(v) = document.fragments.values().next() { return Err(ParseError::DocumentContainsNonInlineFragments(v.pos)); @@ -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 { let query_root = try_get_query_root(document)?;