Skip to main content

apollo_federation/connectors/
variable.rs

1//! Variables used in connector directives `@connect` and `@source`.
2
3use std::fmt::Display;
4use std::fmt::Formatter;
5use std::ops::Range;
6use std::str::FromStr;
7
8use itertools::Itertools;
9
10use super::id::ConnectedElement;
11use super::json_selection::SelectionTrie;
12use crate::connectors::validation::Code;
13
14/// A variable context for Apollo Connectors. Variables are used within a `@connect` or `@source`
15/// [`Directive`], are used in a particular [`Phase`], and have a specific [`Target`].
16#[derive(Clone, PartialEq)]
17pub(crate) struct VariableContext<'schema> {
18    /// The field definition or type the directive is on
19    pub(crate) element: &'schema ConnectedElement<'schema>,
20
21    pub(super) phase: Phase,
22    pub(super) target: Target,
23}
24
25impl<'schema> VariableContext<'schema> {
26    pub(crate) const fn new(
27        element: &'schema ConnectedElement<'schema>,
28        phase: Phase,
29        target: Target,
30    ) -> Self {
31        Self {
32            element,
33            phase,
34            target,
35        }
36    }
37
38    /// Get the variable namespaces that are available in this context
39    pub(crate) fn available_namespaces(&self) -> impl Iterator<Item = Namespace> {
40        match &self.phase {
41            Phase::Response => {
42                vec![
43                    Namespace::Args,
44                    Namespace::Config,
45                    Namespace::Context,
46                    Namespace::Status,
47                    Namespace::This,
48                    Namespace::Request,
49                    Namespace::Response,
50                    Namespace::Env,
51                ]
52            }
53        }
54        .into_iter()
55    }
56
57    /// Get the list of namespaces joined as a comma separated list
58    pub(crate) fn namespaces_joined(&self) -> String {
59        self.available_namespaces()
60            .map(|s| s.to_string())
61            .sorted()
62            .join(", ")
63    }
64
65    /// Get the error code for this context
66    pub(crate) const fn error_code(&self) -> Code {
67        match self.target {
68            Target::Body => Code::InvalidSelection,
69        }
70    }
71}
72
73/// The phase an expression is associated with
74#[derive(Clone, Copy, PartialEq)]
75pub(crate) enum Phase {
76    /// The response phase
77    Response,
78}
79
80/// The target of an expression containing a variable reference
81#[allow(unused)]
82#[derive(Clone, Copy, PartialEq)]
83pub(crate) enum Target {
84    /// The expression is used in the body of a request or response
85    Body,
86}
87
88/// The variable namespaces defined for Apollo Connectors
89#[derive(PartialEq, Eq, Clone, Copy, Hash)]
90pub enum Namespace {
91    Args,
92    Config,
93    Context,
94    Status,
95    This,
96    Batch,
97    Request,
98    Response,
99    Env,
100}
101
102impl Namespace {
103    pub const fn as_str(&self) -> &'static str {
104        match self {
105            Self::Args => "$args",
106            Self::Config => "$config",
107            Self::Context => "$context",
108            Self::Status => "$status",
109            Self::This => "$this",
110            Self::Batch => "$batch",
111            Self::Request => "$request",
112            Self::Response => "$response",
113            Self::Env => "$env",
114        }
115    }
116}
117
118impl FromStr for Namespace {
119    type Err = ();
120
121    fn from_str(s: &str) -> Result<Self, Self::Err> {
122        match s {
123            "$args" => Ok(Self::Args),
124            "$config" => Ok(Self::Config),
125            "$context" => Ok(Self::Context),
126            "$status" => Ok(Self::Status),
127            "$this" => Ok(Self::This),
128            "$batch" => Ok(Self::Batch),
129            "$request" => Ok(Self::Request),
130            "$response" => Ok(Self::Response),
131            "$env" => Ok(Self::Env),
132            _ => Err(()),
133        }
134    }
135}
136
137impl std::fmt::Debug for Namespace {
138    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
139        write!(f, "{}", self.as_str())
140    }
141}
142
143impl Display for Namespace {
144    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
145        write!(f, "{}", self.as_str())
146    }
147}
148
149/// A variable reference. Consists of a namespace starting with a `$` and an optional path
150/// separated by '.' characters.
151#[derive(Debug, Eq, PartialEq, Clone, Hash)]
152pub struct VariableReference<N: FromStr + ToString> {
153    /// The namespace of the variable - `$this`, `$args`, `$status`, etc.
154    pub namespace: VariableNamespace<N>,
155
156    /// The path elements of this reference. For example, the reference `$this.a.b.c`
157    /// has path elements `a`, `b`, `c`. May be empty in some cases, as in the reference `$status`.
158    pub(crate) selection: SelectionTrie,
159
160    /// The location of the reference within the original text.
161    pub(crate) location: Option<Range<usize>>,
162}
163
164impl<N: FromStr + ToString> Display for VariableReference<N> {
165    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
166        f.write_str(self.namespace.namespace.to_string().as_str())?;
167        if !self.selection.is_empty() {
168            write!(f, " {{ {} }}", self.selection)?;
169        }
170        Ok(())
171    }
172}
173
174/// A namespace in a variable reference, like `$this` in `$this.a.b.c`
175#[derive(Debug, Eq, PartialEq, Clone, Hash)]
176pub struct VariableNamespace<N: FromStr + ToString> {
177    pub namespace: N,
178    pub(crate) location: Option<Range<usize>>,
179}