graphql-composition 0.12.2

An implementation of GraphQL federated schema composition
Documentation
mod used_directives;

pub(super) use self::used_directives::UsedDirectives;

use super::field_types_map::FieldTypesMap;
use crate::{composition_ir as ir, federated_graph as federated, subgraphs};
use std::collections::HashMap;

pub(super) struct Context<'a> {
    pub(super) out: &'a mut federated::FederatedGraph,
    pub(super) subgraphs: &'a subgraphs::Subgraphs,
    pub(super) field_types_map: FieldTypesMap,
    pub(super) selection_map: HashMap<(federated::Definition, subgraphs::StringId), federated::FieldId>,
    pub(super) definitions: HashMap<federated::StringId, federated::Definition>,
    pub(super) composed_directive_linked_schemas: Vec<(subgraphs::LinkedSchemaId, federated::StringId)>,

    pub(super) used_directives: UsedDirectives,

    used_extensions: fixedbitset::FixedBitSet,
    strings_ir: ir::StringsIr,
}

impl<'a> Context<'a> {
    pub(crate) fn new(
        ir: &mut ir::CompositionIr,
        subgraphs: &'a subgraphs::Subgraphs,
        out: &'a mut federated::FederatedGraph,
    ) -> Self {
        Context {
            out,
            subgraphs,
            definitions: std::mem::take(&mut ir.definitions_by_name),
            strings_ir: std::mem::take(&mut ir.strings),
            selection_map: HashMap::with_capacity(ir.fields.len()),
            field_types_map: FieldTypesMap::default(),
            used_extensions: ir.used_extensions.clone(),
            used_directives: UsedDirectives::empty(),
            composed_directive_linked_schemas: Vec::new(),
        }
    }

    pub(crate) fn convert_extension_id(&self, extension_id: subgraphs::ExtensionId) -> federated::ExtensionId {
        assert!(
            self.used_extensions[usize::from(extension_id)],
            "Unused extensions used in FederatedGraph?"
        );
        let offset = self.used_extensions.count_zeroes(..usize::from(extension_id));
        federated::ExtensionId::from(usize::from(extension_id) - offset)
    }

    /// Subgraphs string -> federated graph string.
    pub(crate) fn insert_string(&mut self, string_id: subgraphs::StringId) -> federated::StringId {
        self.strings_ir.insert(&self.subgraphs[string_id])
    }

    pub(crate) fn insert_str(&mut self, string: &str) -> federated::StringId {
        self.strings_ir.insert(string)
    }

    pub(crate) fn lookup_str(&self, string: &str) -> Option<federated::StringId> {
        self.strings_ir.lookup(string)
    }

    pub(crate) fn lookup_string_id(&self, string: federated::StringId) -> &str {
        &self.strings_ir[string]
    }

    pub(crate) fn insert_value(&mut self, value: &subgraphs::Value) -> federated::Value {
        self.insert_value_with_type(value, None)
    }

    pub(crate) fn insert_value_with_type(
        &mut self,
        value: &subgraphs::Value,
        enum_type: Option<federated::EnumDefinitionId>,
    ) -> federated::Value {
        match value {
            subgraphs::Value::Null => federated::Value::Null,
            subgraphs::Value::String(value) => federated::Value::String(self.insert_string(*value)),
            subgraphs::Value::Int(value) => federated::Value::Int(*value),
            subgraphs::Value::Float(value) => federated::Value::Float(*value),
            subgraphs::Value::Boolean(value) => federated::Value::Boolean(*value),
            subgraphs::Value::Enum(value) => {
                let value_name = self.insert_string(*value);
                let enum_value = enum_type.and_then(|enum_id| self.out.find_enum_value_by_name_id(enum_id, value_name));

                if let Some(enum_value) = enum_value {
                    federated::Value::EnumValue(enum_value.id())
                } else {
                    federated::Value::UnboundEnumValue(value_name)
                }
            }
            subgraphs::Value::Object(value) => federated::Value::Object(
                value
                    .iter()
                    .map(|(k, v)| (self.insert_string(*k), self.insert_value_with_type(v, enum_type)))
                    .collect(),
            ),
            subgraphs::Value::List(value) => federated::Value::List(
                value
                    .iter()
                    .map(|v| self.insert_value_with_type(v, enum_type))
                    .collect(),
            ),
        }
    }
}

impl std::ops::Index<federated::StringId> for Context<'_> {
    type Output = str;

    fn index(&self, index: federated::StringId) -> &Self::Output {
        &self.strings_ir[index]
    }
}

impl std::ops::Index<subgraphs::StringId> for Context<'_> {
    type Output = str;

    fn index(&self, index: subgraphs::StringId) -> &Self::Output {
        &self.subgraphs[index]
    }
}

impl Drop for Context<'_> {
    fn drop(&mut self) {
        self.out.strings = std::mem::take(&mut self.strings_ir).into_federated_strings();
    }
}