Add support for "dyn" trait object syntax (#385)

* Use "dyn" for TraitObjects

* Cleanup after adding "dyn" support

* Add entry to CHANGELOG.md
This commit is contained in:
Peter Majchrak 2019-06-27 17:44:30 +02:00 committed by Christian Legnitto
parent 97e1005178
commit 3373935046
6 changed files with 42 additions and 37 deletions

View file

@ -18,6 +18,7 @@ struct UserId(i32);
### Other Changes
- The `ID` scalar now implements Serde's `Serialize` and `Deserialize`
- Add support for `dyn` trait object syntax to procedural macros
# [[0.12.0] 2019-05-16](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.12.0)

View file

@ -17,7 +17,7 @@ pub trait Character {
fn friend_ids(&self) -> &[String];
fn appears_in(&self) -> &[Episode];
fn secret_backstory(&self) -> &Option<String>;
fn as_character(&self) -> &Character;
fn as_character(&self) -> &dyn Character;
}
pub trait Human: Character {
@ -62,7 +62,7 @@ impl Character for HumanData {
fn secret_backstory(&self) -> &Option<String> {
&self.secret_backstory
}
fn as_character(&self) -> &Character {
fn as_character(&self) -> &dyn Character {
self
}
}
@ -89,7 +89,7 @@ impl Character for DroidData {
fn secret_backstory(&self) -> &Option<String> {
&self.secret_backstory
}
fn as_character(&self) -> &Character {
fn as_character(&self) -> &dyn Character {
self
}
}
@ -248,7 +248,7 @@ impl Database {
}
}
pub fn get_hero(&self, episode: Option<Episode>) -> &Character {
pub fn get_hero(&self, episode: Option<Episode>) -> &dyn Character {
if episode == Some(Episode::Empire) {
self.get_human("1000").unwrap().as_character()
} else {
@ -256,15 +256,15 @@ impl Database {
}
}
pub fn get_human(&self, id: &str) -> Option<&Human> {
self.humans.get(id).map(|h| h as &Human)
pub fn get_human(&self, id: &str) -> Option<&dyn Human> {
self.humans.get(id).map(|h| h as &dyn Human)
}
pub fn get_droid(&self, id: &str) -> Option<&Droid> {
self.droids.get(id).map(|d| d as &Droid)
pub fn get_droid(&self, id: &str) -> Option<&dyn Droid> {
self.droids.get(id).map(|d| d as &dyn Droid)
}
pub fn get_character(&self, id: &str) -> Option<&Character> {
pub fn get_character(&self, id: &str) -> Option<&dyn Character> {
if let Some(h) = self.humans.get(id) {
Some(h)
} else if let Some(d) = self.droids.get(id) {
@ -274,7 +274,7 @@ impl Database {
}
}
pub fn get_friends(&self, c: &Character) -> Vec<&Character> {
pub fn get_friends(&self, c: &dyn Character) -> Vec<&dyn Character> {
c.friend_ids()
.iter()
.flat_map(|id| self.get_character(id))

View file

@ -5,7 +5,7 @@ use crate::tests::model::{Character, Database, Droid, Episode, Human};
impl Context for Database {}
graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
graphql_interface!(<'a> &'a dyn Character: Database as "Character" |&self| {
description: "A character in the Star Wars Trilogy"
field id() -> &str as "The id of the character" {
@ -16,7 +16,7 @@ graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
Some(self.name())
}
field friends(&executor) -> Vec<&Character>
field friends(&executor) -> Vec<&dyn Character>
as "The friends of the character" {
executor.context().get_friends(self.as_character())
}
@ -26,8 +26,8 @@ graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
}
instance_resolvers: |&context| {
&Human => context.get_human(&self.id()),
&Droid => context.get_droid(&self.id()),
&dyn Human => context.get_human(&self.id()),
&dyn Droid => context.get_droid(&self.id()),
}
});
@ -37,7 +37,7 @@ graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
interfaces = [&dyn Character],
)]
/// A humanoid creature in the Star Wars universe.
impl<'a> &'a Human {
impl<'a> &'a dyn Human {
/// The id of the human
fn id(&self) -> &str {
self.id()
@ -49,7 +49,7 @@ impl<'a> &'a Human {
}
/// The friends of the human
fn friends(&self, ctx: &Database) -> Vec<&Character> {
fn friends(&self, ctx: &Database) -> Vec<&dyn Character> {
ctx.get_friends(self.as_character())
}
@ -70,7 +70,7 @@ impl<'a> &'a Human {
interfaces = [&dyn Character],
)]
/// A mechanical creature in the Star Wars universe.
impl<'a> &'a Droid {
impl<'a> &'a dyn Droid {
/// The id of the droid
fn id(&self) -> &str {
self.id()
@ -82,7 +82,7 @@ impl<'a> &'a Droid {
}
/// The friends of the droid
fn friends(&self, ctx: &Database) -> Vec<&Character> {
fn friends(&self, ctx: &Database) -> Vec<&dyn Character> {
ctx.get_friends(self.as_character())
}
@ -106,19 +106,19 @@ pub struct Query;
/// The root query object of the schema
impl Query {
#[graphql(arguments(id(description = "id of the human")))]
fn human(database: &Database, id: String) -> Option<&Human> {
fn human(database: &Database, id: String) -> Option<&dyn Human> {
database.get_human(&id)
}
#[graphql(arguments(id(description = "id of the droid")))]
fn droid(database: &Database, id: String) -> Option<&Droid> {
fn droid(database: &Database, id: String) -> Option<&dyn Droid> {
database.get_droid(&id)
}
#[graphql(arguments(episode(
description = "If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode"
)))]
fn hero(database: &Database, episode: Option<Episode>) -> Option<&Character> {
fn hero(database: &Database, episode: Option<Episode>) -> Option<&dyn Character> {
Some(database.get_hero(episode).as_character())
}
}

View file

@ -52,19 +52,23 @@ pub fn build_object(args: TokenStream, body: TokenStream, is_internal: bool) ->
.unwrap()
.ident
.to_string(),
syn::Type::Reference(ref reference) => match &*reference.elem {
syn::Type::Path(ref type_path) => type_path
.path
.segments
.iter()
.last()
.unwrap()
.ident
.to_string(),
_ => {
panic!("Could not determine a name for the object type: specify one with #[juniper::object(name = \"SomeName\")");
}
},
syn::Type::Reference(ref reference) => {
let path = match &*reference.elem {
syn::Type::Path(ref type_path) => &type_path.path,
syn::Type::TraitObject(ref trait_obj) => {
match trait_obj.bounds.iter().nth(0).unwrap() {
syn::TypeParamBound::Trait(ref trait_bound) => &trait_bound.path,
_ => {
panic!("Could not determine a name for the object type: specify one with #[juniper::object(name = \"SomeName\")");
}
}
}
_ => {
panic!("Could not determine a name for the object type: specify one with #[juniper::object(name = \"SomeName\")");
}
};
path.segments.iter().last().unwrap().ident.to_string()
}
_ => {
panic!("Could not determine a name for the object type: specify one with #[juniper::object(name = \"SomeName\")");
}
@ -169,7 +173,7 @@ pub fn build_object(args: TokenStream, body: TokenStream, is_internal: bool) ->
.unwrap_or(false)
{
panic!(
"Invalid context argument: to access the context, you need to specify the type as a reference.\nDid you mean &{}?",
"Invalid context argument: to access the context, you need to specify the type as a reference.\nDid you mean &{}?",
quote!(captured.ty),
);
} else {

View file

@ -294,7 +294,7 @@ impl Error for GraphQLRequestError {
}
}
fn cause(&self) -> Option<&Error> {
fn cause(&self) -> Option<&dyn Error> {
match *self {
GraphQLRequestError::BodyHyper(ref err) => Some(err),
GraphQLRequestError::BodyUtf8(ref err) => Some(err),

View file

@ -416,7 +416,7 @@ impl Error for GraphQLIronError {
}
}
fn cause(&self) -> Option<&Error> {
fn cause(&self) -> Option<&dyn Error> {
match *self {
GraphQLIronError::Serde(ref err) => Some(err),
GraphQLIronError::Url(ref err) => Some(err),