use std::borrow::Cow;
use std::fmt;
use ast::{FromInputValue, InputValue, Type};
use parser::{ParseError, ScalarToken};
use schema::model::SchemaType;
use types::base::TypeKind;
use value::{DefaultScalarValue, ParseScalarValue, ScalarRefValue, ScalarValue};
#[derive(Debug, PartialEq, Hash, Clone)]
pub enum DeprecationStatus {
Current,
Deprecated(Option<String>),
}
impl DeprecationStatus {
pub fn is_deprecated(&self) -> bool {
match self {
&DeprecationStatus::Current => false,
&DeprecationStatus::Deprecated(_) => true,
}
}
pub fn reason(&self) -> Option<&String> {
match self {
&DeprecationStatus::Current => None,
&DeprecationStatus::Deprecated(ref reason) => reason.as_ref(),
}
}
}
pub struct ScalarMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
#[doc(hidden)]
pub description: Option<String>,
pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> bool,
pub(crate) parse_fn: for<'b> fn(ScalarToken<'b>) -> Result<S, ParseError<'b>>,
}
#[derive(Debug)]
pub struct ListMeta<'a> {
#[doc(hidden)]
pub of_type: Type<'a>,
}
#[derive(Debug)]
pub struct NullableMeta<'a> {
#[doc(hidden)]
pub of_type: Type<'a>,
}
#[derive(Debug)]
pub struct ObjectMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
#[doc(hidden)]
pub description: Option<String>,
#[doc(hidden)]
pub fields: Vec<Field<'a, S>>,
#[doc(hidden)]
pub interface_names: Vec<String>,
}
pub struct EnumMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
#[doc(hidden)]
pub description: Option<String>,
#[doc(hidden)]
pub values: Vec<EnumValue>,
pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> bool,
}
#[derive(Debug)]
pub struct InterfaceMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
#[doc(hidden)]
pub description: Option<String>,
#[doc(hidden)]
pub fields: Vec<Field<'a, S>>,
}
#[derive(Debug)]
pub struct UnionMeta<'a> {
#[doc(hidden)]
pub name: Cow<'a, str>,
#[doc(hidden)]
pub description: Option<String>,
#[doc(hidden)]
pub of_type_names: Vec<String>,
}
pub struct InputObjectMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
#[doc(hidden)]
pub description: Option<String>,
#[doc(hidden)]
pub input_fields: Vec<Argument<'a, S>>,
pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> bool,
}
#[derive(Debug)]
pub struct PlaceholderMeta<'a> {
#[doc(hidden)]
pub of_type: Type<'a>,
}
#[derive(Debug)]
pub enum MetaType<'a, S = DefaultScalarValue> {
#[doc(hidden)]
Scalar(ScalarMeta<'a, S>),
#[doc(hidden)]
List(ListMeta<'a>),
#[doc(hidden)]
Nullable(NullableMeta<'a>),
#[doc(hidden)]
Object(ObjectMeta<'a, S>),
#[doc(hidden)]
Enum(EnumMeta<'a, S>),
#[doc(hidden)]
Interface(InterfaceMeta<'a, S>),
#[doc(hidden)]
Union(UnionMeta<'a>),
#[doc(hidden)]
InputObject(InputObjectMeta<'a, S>),
#[doc(hidden)]
Placeholder(PlaceholderMeta<'a>),
}
#[derive(Debug, Clone)]
pub struct Field<'a, S> {
#[doc(hidden)]
pub name: String,
#[doc(hidden)]
pub description: Option<String>,
#[doc(hidden)]
pub arguments: Option<Vec<Argument<'a, S>>>,
#[doc(hidden)]
pub field_type: Type<'a>,
#[doc(hidden)]
pub deprecation_status: DeprecationStatus,
}
#[derive(Debug, Clone)]
pub struct Argument<'a, S> {
#[doc(hidden)]
pub name: String,
#[doc(hidden)]
pub description: Option<String>,
#[doc(hidden)]
pub arg_type: Type<'a>,
#[doc(hidden)]
pub default_value: Option<InputValue<S>>,
}
#[derive(Debug, Clone)]
pub struct EnumValue {
pub name: String,
pub description: Option<String>,
pub deprecation_status: DeprecationStatus,
}
impl<'a, S> MetaType<'a, S> {
pub fn name(&self) -> Option<&str> {
match *self {
MetaType::Scalar(ScalarMeta { ref name, .. })
| MetaType::Object(ObjectMeta { ref name, .. })
| MetaType::Enum(EnumMeta { ref name, .. })
| MetaType::Interface(InterfaceMeta { ref name, .. })
| MetaType::Union(UnionMeta { ref name, .. })
| MetaType::InputObject(InputObjectMeta { ref name, .. }) => Some(name),
_ => None,
}
}
pub fn description(&self) -> Option<&String> {
match *self {
MetaType::Scalar(ScalarMeta {
ref description, ..
})
| MetaType::Object(ObjectMeta {
ref description, ..
})
| MetaType::Enum(EnumMeta {
ref description, ..
})
| MetaType::Interface(InterfaceMeta {
ref description, ..
})
| MetaType::Union(UnionMeta {
ref description, ..
})
| MetaType::InputObject(InputObjectMeta {
ref description, ..
}) => description.as_ref(),
_ => None,
}
}
pub fn type_kind(&self) -> TypeKind {
match *self {
MetaType::Scalar(_) => TypeKind::Scalar,
MetaType::List(_) => TypeKind::List,
MetaType::Nullable(_) => panic!("Can't take type_kind of nullable meta type"),
MetaType::Object(_) => TypeKind::Object,
MetaType::Enum(_) => TypeKind::Enum,
MetaType::Interface(_) => TypeKind::Interface,
MetaType::Union(_) => TypeKind::Union,
MetaType::InputObject(_) => TypeKind::InputObject,
MetaType::Placeholder(_) => panic!("Can't take type_kind of placeholder meta type"),
}
}
pub fn field_by_name(&self, name: &str) -> Option<&Field<S>> {
match *self {
MetaType::Object(ObjectMeta { ref fields, .. })
| MetaType::Interface(InterfaceMeta { ref fields, .. }) => {
fields.iter().find(|f| f.name == name)
}
_ => None,
}
}
pub fn input_field_by_name(&self, name: &str) -> Option<&Argument<S>> {
match *self {
MetaType::InputObject(InputObjectMeta {
ref input_fields, ..
}) => input_fields.iter().find(|f| f.name == name),
_ => None,
}
}
pub fn as_type(&self) -> Type<'a> {
match *self {
MetaType::Scalar(ScalarMeta { ref name, .. })
| MetaType::Object(ObjectMeta { ref name, .. })
| MetaType::Enum(EnumMeta { ref name, .. })
| MetaType::Interface(InterfaceMeta { ref name, .. })
| MetaType::Union(UnionMeta { ref name, .. })
| MetaType::InputObject(InputObjectMeta { ref name, .. }) => {
Type::NonNullNamed(name.clone())
}
MetaType::List(ListMeta { ref of_type }) => {
Type::NonNullList(Box::new(of_type.clone()))
}
MetaType::Nullable(NullableMeta { ref of_type }) => match *of_type {
Type::NonNullNamed(ref inner) => Type::Named(inner.clone()),
Type::NonNullList(ref inner) => Type::List(inner.clone()),
ref t => t.clone(),
},
MetaType::Placeholder(PlaceholderMeta { ref of_type }) => of_type.clone(),
}
}
pub fn input_value_parse_fn(&self) -> Option<for<'b> fn(&'b InputValue<S>) -> bool> {
match *self {
MetaType::Scalar(ScalarMeta {
ref try_parse_fn, ..
})
| MetaType::Enum(EnumMeta {
ref try_parse_fn, ..
})
| MetaType::InputObject(InputObjectMeta {
ref try_parse_fn, ..
}) => Some(*try_parse_fn),
_ => None,
}
}
pub fn is_composite(&self) -> bool {
match *self {
MetaType::Object(_) | MetaType::Interface(_) | MetaType::Union(_) => true,
_ => false,
}
}
pub fn is_leaf(&self) -> bool {
match *self {
MetaType::Enum(_) | MetaType::Scalar(_) => true,
_ => false,
}
}
pub fn is_abstract(&self) -> bool {
match *self {
MetaType::Interface(_) | MetaType::Union(_) => true,
_ => false,
}
}
pub fn is_input(&self) -> bool {
match *self {
MetaType::Scalar(_) | MetaType::Enum(_) | MetaType::InputObject(_) => true,
_ => false,
}
}
pub(crate) fn fields<'b>(&self, schema: &'b SchemaType<S>) -> Option<Vec<&'b Field<'b, S>>> {
schema.lookup_type(&self.as_type()).and_then(|tpe| {
match *tpe {
MetaType::Interface(ref i) => Some(i.fields.iter().collect()),
MetaType::Object(ref o) => Some(o.fields.iter().collect()),
MetaType::Union(ref u) => Some(
u.of_type_names
.iter()
.filter_map(|n| schema.concrete_type_by_name(n))
.filter_map(|t| t.fields(schema))
.flat_map(|f| f)
.collect(),
),
_ => None,
}
})
}
}
impl<'a, S> ScalarMeta<'a, S>
where
S: ScalarValue + 'a,
{
pub fn new<T>(name: Cow<'a, str>) -> Self
where
T: FromInputValue<S> + ParseScalarValue<S> + 'a,
for<'b> &'b S: ScalarRefValue<'b>,
{
ScalarMeta {
name: name,
description: None,
try_parse_fn: try_parse_fn::<S, T>,
parse_fn: <T as ParseScalarValue<S>>::from_str,
}
}
pub fn description(mut self, description: &str) -> ScalarMeta<'a, S> {
self.description = Some(description.to_owned());
self
}
pub fn into_meta(self) -> MetaType<'a, S> {
MetaType::Scalar(self)
}
}
impl<'a> ListMeta<'a> {
pub fn new(of_type: Type<'a>) -> ListMeta<'a> {
ListMeta { of_type: of_type }
}
pub fn into_meta<S>(self) -> MetaType<'a, S> {
MetaType::List(self)
}
}
impl<'a> NullableMeta<'a> {
pub fn new(of_type: Type<'a>) -> NullableMeta<'a> {
NullableMeta { of_type: of_type }
}
pub fn into_meta<S>(self) -> MetaType<'a, S> {
MetaType::Nullable(self)
}
}
impl<'a, S> ObjectMeta<'a, S>
where
S: ScalarValue,
{
pub fn new(name: Cow<'a, str>, fields: &[Field<'a, S>]) -> Self {
ObjectMeta {
name: name,
description: None,
fields: fields.to_vec(),
interface_names: vec![],
}
}
pub fn description(mut self, description: &str) -> ObjectMeta<'a, S> {
self.description = Some(description.to_owned());
self
}
pub fn interfaces(mut self, interfaces: &[Type<'a>]) -> ObjectMeta<'a, S> {
self.interface_names = interfaces
.iter()
.map(|t| t.innermost_name().to_owned())
.collect();
self
}
pub fn into_meta(self) -> MetaType<'a, S> {
MetaType::Object(self)
}
}
impl<'a, S> EnumMeta<'a, S>
where
S: ScalarValue + 'a,
{
pub fn new<T>(name: Cow<'a, str>, values: &[EnumValue]) -> Self
where
T: FromInputValue<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
EnumMeta {
name: name,
description: None,
values: values.to_vec(),
try_parse_fn: try_parse_fn::<S, T>,
}
}
pub fn description(mut self, description: &str) -> EnumMeta<'a, S> {
self.description = Some(description.to_owned());
self
}
pub fn into_meta(self) -> MetaType<'a, S> {
MetaType::Enum(self)
}
}
impl<'a, S> InterfaceMeta<'a, S>
where
S: ScalarValue,
{
pub fn new(name: Cow<'a, str>, fields: &[Field<'a, S>]) -> InterfaceMeta<'a, S> {
InterfaceMeta {
name: name,
description: None,
fields: fields.to_vec(),
}
}
pub fn description(mut self, description: &str) -> InterfaceMeta<'a, S> {
self.description = Some(description.to_owned());
self
}
pub fn into_meta(self) -> MetaType<'a, S> {
MetaType::Interface(self)
}
}
impl<'a> UnionMeta<'a> {
pub fn new(name: Cow<'a, str>, of_types: &[Type]) -> UnionMeta<'a> {
UnionMeta {
name: name,
description: None,
of_type_names: of_types
.iter()
.map(|t| t.innermost_name().to_owned())
.collect(),
}
}
pub fn description(mut self, description: &str) -> UnionMeta<'a> {
self.description = Some(description.to_owned());
self
}
pub fn into_meta<S>(self) -> MetaType<'a, S> {
MetaType::Union(self)
}
}
impl<'a, S> InputObjectMeta<'a, S>
where
S: ScalarValue,
{
pub fn new<T: FromInputValue<S>>(name: Cow<'a, str>, input_fields: &[Argument<'a, S>]) -> Self
where
for<'b> &'b S: ScalarRefValue<'b>,
{
InputObjectMeta {
name: name,
description: None,
input_fields: input_fields.to_vec(),
try_parse_fn: try_parse_fn::<S, T>,
}
}
pub fn description(mut self, description: &str) -> InputObjectMeta<'a, S> {
self.description = Some(description.to_owned());
self
}
pub fn into_meta(self) -> MetaType<'a, S> {
MetaType::InputObject(self)
}
}
impl<'a, S> Field<'a, S> {
pub fn description(mut self, description: &str) -> Self {
self.description = Some(description.to_owned());
self
}
pub fn push_docstring(mut self, multiline: &[&str]) -> Field<'a, S> {
if let Some(docstring) = clean_docstring(multiline) {
match &mut self.description {
&mut Some(ref mut desc) => {
desc.push('\n');
desc.push_str(&docstring);
}
desc @ &mut None => {
*desc = Some(docstring);
}
}
}
self
}
pub fn argument(mut self, argument: Argument<'a, S>) -> Self {
match self.arguments {
None => {
self.arguments = Some(vec![argument]);
}
Some(ref mut args) => {
args.push(argument);
}
};
self
}
pub fn deprecated(mut self, reason: Option<&str>) -> Self {
self.deprecation_status = DeprecationStatus::Deprecated(reason.map(|s| s.to_owned()));
self
}
}
impl<'a, S> Argument<'a, S> {
#[doc(hidden)]
pub fn new(name: &str, arg_type: Type<'a>) -> Self {
Argument {
name: name.to_owned(),
description: None,
arg_type: arg_type,
default_value: None,
}
}
pub fn description(mut self, description: &str) -> Self {
self.description = Some(description.to_owned());
self
}
pub fn push_docstring(mut self, multiline: &[&str]) -> Argument<'a, S> {
if let Some(docstring) = clean_docstring(multiline) {
match &mut self.description {
&mut Some(ref mut desc) => {
desc.push('\n');
desc.push_str(&docstring);
}
desc @ &mut None => {
*desc = Some(docstring)
}
}
}
self
}
pub fn default_value(mut self, default_value: InputValue<S>) -> Self {
self.default_value = Some(default_value);
self
}
}
impl EnumValue {
pub fn new(name: &str) -> EnumValue {
EnumValue {
name: name.to_owned(),
description: None,
deprecation_status: DeprecationStatus::Current,
}
}
pub fn description(mut self, description: &str) -> EnumValue {
self.description = Some(description.to_owned());
self
}
pub fn deprecated(mut self, reason: Option<&str>) -> Self {
self.deprecation_status = DeprecationStatus::Deprecated(reason.map(|s| s.to_owned()));
self
}
}
impl<'a, S: fmt::Debug> fmt::Debug for ScalarMeta<'a, S> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("ScalarMeta")
.field("name", &self.name)
.field("description", &self.description)
.finish()
}
}
impl<'a, S: fmt::Debug> fmt::Debug for EnumMeta<'a, S> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("EnumMeta")
.field("name", &self.name)
.field("description", &self.description)
.field("values", &self.values)
.finish()
}
}
impl<'a, S: fmt::Debug> fmt::Debug for InputObjectMeta<'a, S> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("InputObjectMeta")
.field("name", &self.name)
.field("description", &self.description)
.field("input_fields", &self.input_fields)
.finish()
}
}
fn try_parse_fn<S, T>(v: &InputValue<S>) -> bool
where
T: FromInputValue<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
<T as FromInputValue<S>>::from_input_value(v).is_some()
}
fn clean_docstring(multiline: &[&str]) -> Option<String> {
if multiline.is_empty() {
return None;
}
let trim_start = multiline
.iter()
.filter_map(|ln| ln.chars().position(|ch| !ch.is_whitespace()))
.min()
.unwrap_or(0);
Some(
multiline
.iter()
.enumerate()
.flat_map(|(line, ln)| {
let new_ln = if !ln
.chars()
.next()
.map(|ch| ch.is_whitespace())
.unwrap_or(false)
{
ln.trim_right()
} else if ln.len() >= trim_start {
ln[trim_start..].trim_right()
} else {
""
};
new_ln.chars().chain(
['\n']
.iter()
.take_while(move |_| line < multiline.len() - 1)
.cloned(),
)
}).collect::<String>(),
)
}