use crate::location::{Location, SourceId};
use crate::Shape;
use apollo_compiler::ast::{FieldDefinition, Type};
use apollo_compiler::collections::IndexMap;
use apollo_compiler::parser::SourceSpan;
use apollo_compiler::schema::{
Component, EnumType, ExtendedType, InputObjectType, InterfaceType, ObjectType, UnionType,
};
use apollo_compiler::{Name, Node, Schema};
#[must_use]
pub fn shapes_for_schema(schema: &Schema) -> IndexMap<&str, Shape> {
schema
.types
.iter()
.filter(|&(_name, ty)| !ty.is_built_in())
.map(|(name, ty)| (name.as_str(), Shape::from(ty)))
.collect()
}
#[must_use]
pub fn shape_for_arguments(field_definition: &Component<FieldDefinition>) -> Shape {
let mut source_span = None;
for next_source_span in field_definition.arguments.iter().map(Node::location) {
source_span = SourceSpan::recompose(source_span, next_source_span);
}
Shape::record(
field_definition
.arguments
.iter()
.map(|arg| {
(
arg.name.to_string(),
from_type(arg.ty.as_ref(), span(arg.location())),
)
})
.collect(),
span(source_span),
)
}
impl From<&ExtendedType> for Shape {
fn from(ext: &ExtendedType) -> Self {
match ext {
ExtendedType::Scalar(scalar) => Shape::unknown(span(scalar.location())),
ExtendedType::Object(obj) => Self::from(obj),
ExtendedType::Interface(intf) => Self::from(intf),
ExtendedType::Union(union) => Self::from(union),
ExtendedType::Enum(enm) => Self::from(enm),
ExtendedType::InputObject(input) => Self::from(input),
}
}
}
impl From<&Name> for Shape {
fn from(name: &Name) -> Self {
let locations = span(name.location());
match name.as_str() {
"String" | "ID" => Self::string(locations),
"Int" => Self::int(locations),
"Float" => Self::float(locations),
"Boolean" => Self::bool(locations),
other => Self::name(other, locations),
}
}
}
impl From<&Node<ObjectType>> for Shape {
fn from(object_type: &Node<ObjectType>) -> Self {
record(&object_type.fields, span(object_type.location()))
}
}
fn record(fields: &IndexMap<Name, Component<FieldDefinition>>, locations: Vec<Location>) -> Shape {
let fields = fields
.iter()
.map(|(key, value)| (key.to_string(), Shape::from(value)))
.collect();
Shape::record(fields, locations)
}
fn nullable(shape: Shape, locations: Vec<Location>) -> Shape {
Shape::one([shape, Shape::null(locations.clone())], locations)
}
impl From<&Component<FieldDefinition>> for Shape {
fn from(field: &Component<FieldDefinition>) -> Self {
let locations = span(field.location());
from_type(&field.ty, locations)
}
}
fn from_type(ty: &Type, locations: Vec<Location>) -> Shape {
match ty {
Type::Named(name) => nullable(Shape::from(name), locations),
Type::NonNullNamed(name) => Shape::from(name),
Type::List(ty) => nullable(
Shape::list(from_type(ty.as_ref(), locations.clone()), locations.clone()),
locations,
),
Type::NonNullList(ty) => Shape::list(from_type(ty.as_ref(), locations.clone()), locations),
}
}
impl From<&Node<InterfaceType>> for Shape {
fn from(interface_type: &Node<InterfaceType>) -> Self {
record(&interface_type.fields, span(interface_type.location()))
}
}
impl From<&Node<UnionType>> for Shape {
fn from(union_type: &Node<UnionType>) -> Self {
Self::one(
union_type
.members
.iter()
.map(|member| Self::from(&member.name)),
span(union_type.location()),
)
}
}
impl From<&Node<EnumType>> for Shape {
fn from(enum_type: &Node<EnumType>) -> Self {
Shape::one(
enum_type
.values
.iter()
.map(|(name, _)| Shape::string_value(name.as_str(), span(name.location()))),
span(enum_type.location()),
)
}
}
impl From<&Node<InputObjectType>> for Shape {
fn from(input_object_type: &Node<InputObjectType>) -> Self {
Self::record(
input_object_type
.fields
.iter()
.map(|(name, input)| {
(
name.to_string(),
from_type(input.ty.as_ref(), span(input.location())),
)
})
.collect(),
span(input_object_type.location()),
)
}
}
fn span(source_span: Option<SourceSpan>) -> Vec<Location> {
source_span.into_iter().map(Location::from).collect()
}
impl From<SourceSpan> for Location {
fn from(span: SourceSpan) -> Self {
Location {
source_id: SourceId::GraphQL(span.file_id()),
span: span.offset()..span.end_offset(),
}
}
}