diff --git a/derive/src/enum.rs b/derive/src/enum.rs index 2c2e15f1..e3e5859b 100644 --- a/derive/src/enum.rs +++ b/derive/src/enum.rs @@ -61,7 +61,7 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result Result &'static [#crate_name::EnumItem<#ident>] { + impl #crate_name::resolver_utils::EnumType for #ident { + fn items() -> &'static [#crate_name::resolver_utils::EnumItem<#ident>] { &[#(#items),*] } } @@ -107,18 +107,18 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result) -> #crate_name::InputValueResult { - #crate_name::EnumType::parse_enum(value.unwrap_or_default()) + #crate_name::resolver_utils::parse_enum(value.unwrap_or_default()) } fn to_value(&self) -> #crate_name::Value { - #crate_name::EnumType::to_value(self) + #crate_name::resolver_utils::enum_value(*self) } } #[#crate_name::async_trait::async_trait] impl #crate_name::OutputValueType for #ident { async fn resolve(&self, _: &#crate_name::ContextSelectionSet<'_>, _field: &#crate_name::Positioned<#crate_name::parser::types::Field>) -> #crate_name::Result<#crate_name::serde_json::Value> { - Ok(#crate_name::EnumType::to_value(self).into_json().unwrap()) + Ok(#crate_name::resolver_utils::enum_value(*self).into_json().unwrap()) } } }; diff --git a/src/lib.rs b/src/lib.rs index 58419891..03fdf8d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,16 +105,15 @@ mod look_ahead; mod model; mod request; mod response; -mod scalars; mod schema; mod serialize_resp; mod subscription; -mod types; mod validation; pub mod extensions; pub mod guard; pub mod validators; +pub mod types; #[doc(hidden)] pub mod resolver_utils; @@ -145,15 +144,11 @@ pub use parser::{types::ConstValue as Value, Pos, Positioned}; pub use registry::CacheControl; pub use request::Request; pub use response::Response; -pub use scalars::{Any, Json, OutputJson, ID}; pub use schema::{Schema, SchemaBuilder, SchemaEnv}; pub use serde_json::Number; pub use subscription::SimpleBroker; -pub use types::{ - connection, EmptyMutation, EmptySubscription, MaybeUndefined, MergedObject, - MergedObjectSubscriptionTail, MergedObjectTail, Upload, -}; pub use validation::ValidationMode; +pub use types::*; /// Result type pub type Result = std::result::Result; @@ -168,8 +163,6 @@ pub mod registry; pub use base::{InputObjectType, InputValueType, OutputValueType}; #[doc(hidden)] pub use subscription::SubscriptionType; -#[doc(hidden)] -pub use types::{EnumItem, EnumType}; /// Define a GraphQL object with methods /// diff --git a/src/resolver_utils/enum.rs b/src/resolver_utils/enum.rs new file mode 100644 index 00000000..f7ad3afc --- /dev/null +++ b/src/resolver_utils/enum.rs @@ -0,0 +1,45 @@ +use crate::parser::types::Name; +use crate::{InputValueError, InputValueResult, Type, Value}; + +/// A variant of an enum. +pub struct EnumItem { + /// The name of the variant. + pub name: &'static str, + /// The value of the variant. + pub value: T, +} + +/// An enum value. +pub trait EnumType: Type + Sized + Eq + Send + Copy + Sized + 'static { + /// Get a list of the variants of the enum value. + fn items() -> &'static [EnumItem]; +} + +/// Parse a value as an enum value. +/// +/// This can be used to implement `InputValueType::parse`. +pub fn parse_enum(value: Value) -> InputValueResult { + let value = match &value { + Value::Enum(s) => s, + Value::String(s) => s.as_str(), + _ => return Err(InputValueError::ExpectedType(value)), + }; + + T::items() + .iter() + .find(|item| item.name == value) + .map(|item| item.value) + .ok_or_else(|| InputValueError::Custom(format!( + r#"Enumeration type "{}" does not contain the value "{}""#, + T::type_name(), + value, + ))) +} + +/// Convert the enum value into a GraphQL value. +/// +/// This can be used to implement `InputValueType::to_value` or `OutputValueType::resolve`. +pub fn enum_value(value: T) -> Value { + let item = T::items().iter().find(|item| item.value == value).unwrap(); + Value::Enum(Name::new_unchecked(item.name.to_owned())) +} diff --git a/src/resolver_utils/mod.rs b/src/resolver_utils/mod.rs index a3984912..2210a754 100644 --- a/src/resolver_utils/mod.rs +++ b/src/resolver_utils/mod.rs @@ -1,5 +1,7 @@ //! Utilities for implementing `OutputValueType::resolve`. mod object; +mod r#enum; pub use object::*; +pub use r#enum::*; diff --git a/src/scalars/mod.rs b/src/scalars/mod.rs deleted file mode 100644 index d6081810..00000000 --- a/src/scalars/mod.rs +++ /dev/null @@ -1,84 +0,0 @@ -mod any; -mod bool; -mod datetime; -mod floats; -mod id; -mod integers; -mod json; -mod naive_time; -mod string; -mod uuid; - -#[cfg(feature = "bson")] -mod bson; -#[cfg(feature = "chrono_tz")] -mod chrono_tz; -#[cfg(feature = "url")] -mod url; - -pub use any::Any; -pub use id::ID; -pub use json::{Json, OutputJson}; - -#[cfg(test)] -mod tests { - use super::ID; - use crate::Type; - use bson::oid::ObjectId; - use chrono::{DateTime, FixedOffset, Local, NaiveDate, NaiveTime, Utc}; - use uuid::Uuid; - - #[test] - fn test_scalar_type() { - assert_eq!(::type_name(), "Boolean"); - assert_eq!(::qualified_type_name(), "Boolean!"); - - assert_eq!(::type_name(), "Int"); - assert_eq!(::qualified_type_name(), "Int!"); - - assert_eq!(::type_name(), "Float"); - assert_eq!(::qualified_type_name(), "Float!"); - - assert_eq!(<&str as Type>::type_name(), "String"); - assert_eq!(<&str as Type>::qualified_type_name(), "String!"); - - assert_eq!(::type_name(), "String"); - assert_eq!(::qualified_type_name(), "String!"); - - assert_eq!(::type_name(), "ID"); - assert_eq!(::qualified_type_name(), "ID!"); - - assert_eq!(::type_name(), "NaiveDate"); - assert_eq!(::qualified_type_name(), "NaiveDate!"); - - assert_eq!(::type_name(), "NaiveTime"); - assert_eq!(::qualified_type_name(), "NaiveTime!"); - - assert_eq!( as Type>::type_name(), "DateTime"); - assert_eq!( - as Type>::qualified_type_name(), - "DateTime!" - ); - - assert_eq!( as Type>::type_name(), "DateTime"); - assert_eq!( - as Type>::qualified_type_name(), - "DateTime!" - ); - - assert_eq!( as Type>::type_name(), "DateTime"); - assert_eq!( - as Type>::qualified_type_name(), - "DateTime!" - ); - - assert_eq!(::type_name(), "UUID"); - assert_eq!(::qualified_type_name(), "UUID!"); - - #[cfg(feature = "bson")] - { - assert_eq!(::type_name(), "ObjectId"); - assert_eq!(::qualified_type_name(), "ObjectId!"); - } - } -} diff --git a/src/scalars/any.rs b/src/types/any.rs similarity index 100% rename from src/scalars/any.rs rename to src/types/any.rs diff --git a/src/types/enum.rs b/src/types/enum.rs deleted file mode 100644 index 3fedb84d..00000000 --- a/src/types/enum.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::parser::types::Name; -use crate::{InputValueError, InputValueResult, Type, Value}; - -#[allow(missing_docs)] -pub struct EnumItem { - pub name: &'static str, - pub value: T, -} - -#[allow(missing_docs)] -#[async_trait::async_trait] -pub trait EnumType: Type + Sized + Eq + Send + Copy + Sized + 'static { - fn items() -> &'static [EnumItem]; - - fn parse_enum(value: Value) -> InputValueResult { - let value = match &value { - Value::Enum(s) => s, - Value::String(s) => s.as_str(), - _ => return Err(InputValueError::ExpectedType(value)), - }; - - let items = Self::items(); - for item in items { - if item.name == value { - return Ok(item.value); - } - } - Err(InputValueError::Custom(format!( - r#"Enumeration type "{}" does not contain the value "{}""#, - Self::type_name(), - value - ))) - } - - fn to_value(&self) -> Value { - let items = Self::items(); - for item in items { - if item.value == *self { - return Value::Enum(Name::new_unchecked(item.name.to_owned())); - } - } - unreachable!() - } -} diff --git a/src/scalars/bool.rs b/src/types/external/bool.rs similarity index 100% rename from src/scalars/bool.rs rename to src/types/external/bool.rs diff --git a/src/scalars/bson.rs b/src/types/external/bson.rs similarity index 100% rename from src/scalars/bson.rs rename to src/types/external/bson.rs diff --git a/src/scalars/chrono_tz.rs b/src/types/external/chrono_tz.rs similarity index 100% rename from src/scalars/chrono_tz.rs rename to src/types/external/chrono_tz.rs diff --git a/src/scalars/datetime.rs b/src/types/external/datetime.rs similarity index 100% rename from src/scalars/datetime.rs rename to src/types/external/datetime.rs diff --git a/src/scalars/floats.rs b/src/types/external/floats.rs similarity index 100% rename from src/scalars/floats.rs rename to src/types/external/floats.rs diff --git a/src/scalars/integers.rs b/src/types/external/integers.rs similarity index 100% rename from src/scalars/integers.rs rename to src/types/external/integers.rs diff --git a/src/types/list.rs b/src/types/external/list.rs similarity index 100% rename from src/types/list.rs rename to src/types/external/list.rs diff --git a/src/types/external/mod.rs b/src/types/external/mod.rs new file mode 100644 index 00000000..6253253b --- /dev/null +++ b/src/types/external/mod.rs @@ -0,0 +1,16 @@ +//! Implementations of `Type`, `ScalarType`, etc on external types. + +mod string; +mod list; +mod optional; +mod bool; +mod integers; +mod floats; +mod url; +mod uuid; +mod datetime; + +#[cfg(feature = "bson")] +mod bson; +#[cfg(feature = "chrono_tz")] +mod chrono_tz; diff --git a/src/scalars/naive_time.rs b/src/types/external/naive_time.rs similarity index 100% rename from src/scalars/naive_time.rs rename to src/types/external/naive_time.rs diff --git a/src/types/optional.rs b/src/types/external/optional.rs similarity index 100% rename from src/types/optional.rs rename to src/types/external/optional.rs diff --git a/src/scalars/string.rs b/src/types/external/string.rs similarity index 100% rename from src/scalars/string.rs rename to src/types/external/string.rs diff --git a/src/scalars/url.rs b/src/types/external/url.rs similarity index 100% rename from src/scalars/url.rs rename to src/types/external/url.rs diff --git a/src/scalars/uuid.rs b/src/types/external/uuid.rs similarity index 100% rename from src/scalars/uuid.rs rename to src/types/external/uuid.rs diff --git a/src/scalars/id.rs b/src/types/id.rs similarity index 90% rename from src/scalars/id.rs rename to src/types/id.rs index 13abc2a4..81bd3bc8 100644 --- a/src/scalars/id.rs +++ b/src/types/id.rs @@ -10,7 +10,7 @@ use std::ops::{Deref, DerefMut}; /// /// The input is a `&str`, `String`, `usize` or `uuid::UUID`, and the output is a string. #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] -pub struct ID(String); +pub struct ID(pub String); impl Deref for ID { type Target = String; @@ -26,18 +26,15 @@ impl DerefMut for ID { } } -impl From for ID -where - T: std::fmt::Display, -{ +impl From for ID { fn from(value: T) -> Self { ID(value.to_string()) } } -impl Into for ID { - fn into(self) -> String { - self.0 +impl From for String { + fn from(id: ID) -> Self { + id.0 } } @@ -55,7 +52,7 @@ macro_rules! try_from_integers { }; } -try_from_integers!(i8, i16, i32, i64, u8, u16, u32, u64, isize, usize); +try_from_integers!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize); impl TryFrom for uuid::Uuid { type Error = uuid::Error; diff --git a/src/scalars/json.rs b/src/types/json.rs similarity index 100% rename from src/scalars/json.rs rename to src/types/json.rs diff --git a/src/types/mod.rs b/src/types/mod.rs index f55a5309..30a62bfe 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,19 +1,26 @@ +//! Useful GraphQL types. + pub mod connection; +mod any; +mod json; mod empty_mutation; mod empty_subscription; -mod r#enum; -mod list; mod maybe_undefined; mod merged_object; -mod optional; mod query_root; mod upload; +mod id; +mod external; + +pub use any::Any; +pub use json::{Json, OutputJson}; pub use empty_mutation::EmptyMutation; pub use empty_subscription::EmptySubscription; pub use maybe_undefined::MaybeUndefined; pub use merged_object::{MergedObject, MergedObjectSubscriptionTail, MergedObjectTail}; -pub use query_root::QueryRoot; -pub use r#enum::{EnumItem, EnumType}; pub use upload::Upload; +pub use id::ID; + +pub(crate) use query_root::QueryRoot; diff --git a/src/types/query_root.rs b/src/types/query_root.rs index 011d27c2..ddea49b9 100644 --- a/src/types/query_root.rs +++ b/src/types/query_root.rs @@ -1,9 +1,8 @@ use crate::model::{__Schema, __Type}; use crate::parser::types::Field; use crate::resolver_utils::{resolve_object, ObjectType}; -use crate::scalars::Any; use crate::{ - registry, Context, ContextSelectionSet, Error, GQLSimpleObject, OutputValueType, Positioned, + registry, Any, Context, ContextSelectionSet, Error, GQLSimpleObject, OutputValueType, Positioned, QueryError, Result, Type, }; @@ -17,9 +16,9 @@ struct Service { sdl: Option, } -pub struct QueryRoot { - pub inner: T, - pub disable_introspection: bool, +pub(crate) struct QueryRoot { + pub(crate) inner: T, + pub(crate) disable_introspection: bool, } impl Type for QueryRoot {