diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 9424ea27..c9a889c7 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -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) diff --git a/juniper/src/tests/model.rs b/juniper/src/tests/model.rs index c0a990c0..3e8f94b5 100644 --- a/juniper/src/tests/model.rs +++ b/juniper/src/tests/model.rs @@ -17,7 +17,7 @@ pub trait Character { fn friend_ids(&self) -> &[String]; fn appears_in(&self) -> &[Episode]; fn secret_backstory(&self) -> &Option; - 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 { &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 { &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) -> &Character { + pub fn get_hero(&self, episode: Option) -> &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)) diff --git a/juniper/src/tests/schema.rs b/juniper/src/tests/schema.rs index 87dcefed..e4091f14 100644 --- a/juniper/src/tests/schema.rs +++ b/juniper/src/tests/schema.rs @@ -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) -> Option<&Character> { + fn hero(database: &Database, episode: Option) -> Option<&dyn Character> { Some(database.get_hero(episode).as_character()) } } diff --git a/juniper_codegen/src/impl_object.rs b/juniper_codegen/src/impl_object.rs index 27926f69..ab2c3bb6 100644 --- a/juniper_codegen/src/impl_object.rs +++ b/juniper_codegen/src/impl_object.rs @@ -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 { diff --git a/juniper_hyper/src/lib.rs b/juniper_hyper/src/lib.rs index 4ded2184..afcbb967 100644 --- a/juniper_hyper/src/lib.rs +++ b/juniper_hyper/src/lib.rs @@ -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), diff --git a/juniper_iron/src/lib.rs b/juniper_iron/src/lib.rs index 3e09cc88..abae21ba 100644 --- a/juniper_iron/src/lib.rs +++ b/juniper_iron/src/lib.rs @@ -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),