grafbase_sdk/extension/
resolver.rs

1use crate::{
2    host_io::pubsub::Subscription,
3    types::{Configuration, Directive, FieldDefinition, FieldInputs, FieldOutput},
4    wit::{Error, SharedContext},
5};
6
7use super::Extension;
8
9type InitFn = Box<dyn Fn(Vec<Directive>, Configuration) -> Result<Box<dyn Resolver>, Box<dyn std::error::Error>>>;
10
11pub(super) static mut EXTENSION: Option<Box<dyn Resolver>> = None;
12pub static mut INIT_FN: Option<InitFn> = None;
13
14pub(super) static mut SUBSCRIBER: Option<Box<dyn Subscription>> = None;
15
16pub(super) fn get_extension() -> Result<&'static mut dyn Resolver, Error> {
17    // Safety: This is hidden, only called by us. Every extension call to an instance happens
18    // in a single-threaded environment. Do not call this multiple times from different threads.
19    unsafe {
20        EXTENSION.as_deref_mut().ok_or_else(|| Error {
21            message: "Resolver extension not initialized correctly.".to_string(),
22            extensions: Vec::new(),
23        })
24    }
25}
26
27pub(super) fn set_subscriber(subscriber: Box<dyn Subscription>) {
28    unsafe {
29        SUBSCRIBER = Some(subscriber);
30    }
31}
32
33pub(super) fn get_subscriber() -> Result<&'static mut dyn Subscription, Error> {
34    unsafe {
35        SUBSCRIBER.as_deref_mut().ok_or_else(|| Error {
36            message: "No active subscription.".to_string(),
37            extensions: Vec::new(),
38        })
39    }
40}
41
42/// Initializes the resolver extension with the provided directives using the closure
43/// function created with the `register_extension!` macro.
44pub(super) fn init(directives: Vec<Directive>, configuration: Configuration) -> Result<(), Box<dyn std::error::Error>> {
45    // Safety: This function is only called from the SDK macro, so we can assume that there is only one caller at a time.
46    unsafe {
47        let init = INIT_FN.as_ref().expect("Resolver extension not initialized correctly.");
48        EXTENSION = Some(init(directives, configuration)?);
49    }
50
51    Ok(())
52}
53
54/// This function gets called when the extension is registered in the user code with the `register_extension!` macro.
55///
56/// This should never be called manually by the user.
57#[doc(hidden)]
58pub fn register(f: InitFn) {
59    // Safety: This function is only called from the SDK macro, so we can assume that there is only one caller at a time.
60    unsafe {
61        INIT_FN = Some(f);
62    }
63}
64
65/// A trait that extends `Extension` and provides functionality for resolving fields.
66///
67/// Implementors of this trait are expected to provide a method to resolve field values based on
68/// the given context, directive, and inputs. This is typically used in scenarios where field
69/// resolution logic needs to be encapsulated within a resolver object, allowing for modular
70/// and reusable code design.
71pub trait Resolver: Extension {
72    /// Resolves a field value based on the given context, directive, definition, and inputs.
73    ///
74    /// # Arguments
75    ///
76    /// * `context` - The shared context containing runtime information
77    /// * `directive` - The directive associated with this field resolution
78    /// * `definition` - The field definition containing metadata
79    /// * `inputs` - The input values provided for this field
80    ///
81    /// # Returns
82    ///
83    /// Returns a `Result` containing either the resolved `FieldOutput` value or an `Error`
84    fn resolve_field(
85        &mut self,
86        context: SharedContext,
87        directive: Directive,
88        definition: FieldDefinition,
89        inputs: FieldInputs,
90    ) -> Result<FieldOutput, Error>;
91
92    /// Resolves a subscription field by setting up a subscription handler.
93    ///
94    /// # Arguments
95    ///
96    /// * `context` - The shared context containing runtime information
97    /// * `directive` - The directive associated with this subscription field
98    /// * `definition` - The field definition containing metadata about the subscription
99    ///
100    /// # Returns
101    ///
102    /// Returns a `Result` containing either a boxed `Subscriber` implementation or an `Error`
103    fn resolve_subscription(
104        &mut self,
105        context: SharedContext,
106        directive: Directive,
107        definition: FieldDefinition,
108    ) -> Result<Box<dyn Subscription>, Error>;
109}