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 ### Other Changes
- The `ID` scalar now implements Serde's `Serialize` and `Deserialize` - 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) # [[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 friend_ids(&self) -> &[String];
fn appears_in(&self) -> &[Episode]; fn appears_in(&self) -> &[Episode];
fn secret_backstory(&self) -> &Option<String>; fn secret_backstory(&self) -> &Option<String>;
fn as_character(&self) -> &Character; fn as_character(&self) -> &dyn Character;
} }
pub trait Human: Character { pub trait Human: Character {
@ -62,7 +62,7 @@ impl Character for HumanData {
fn secret_backstory(&self) -> &Option<String> { fn secret_backstory(&self) -> &Option<String> {
&self.secret_backstory &self.secret_backstory
} }
fn as_character(&self) -> &Character { fn as_character(&self) -> &dyn Character {
self self
} }
} }
@ -89,7 +89,7 @@ impl Character for DroidData {
fn secret_backstory(&self) -> &Option<String> { fn secret_backstory(&self) -> &Option<String> {
&self.secret_backstory &self.secret_backstory
} }
fn as_character(&self) -> &Character { fn as_character(&self) -> &dyn Character {
self 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) { if episode == Some(Episode::Empire) {
self.get_human("1000").unwrap().as_character() self.get_human("1000").unwrap().as_character()
} else { } else {
@ -256,15 +256,15 @@ impl Database {
} }
} }
pub fn get_human(&self, id: &str) -> Option<&Human> { pub fn get_human(&self, id: &str) -> Option<&dyn Human> {
self.humans.get(id).map(|h| h as &Human) self.humans.get(id).map(|h| h as &dyn Human)
} }
pub fn get_droid(&self, id: &str) -> Option<&Droid> { pub fn get_droid(&self, id: &str) -> Option<&dyn Droid> {
self.droids.get(id).map(|d| d as &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) { if let Some(h) = self.humans.get(id) {
Some(h) Some(h)
} else if let Some(d) = self.droids.get(id) { } 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() c.friend_ids()
.iter() .iter()
.flat_map(|id| self.get_character(id)) .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 {} 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" description: "A character in the Star Wars Trilogy"
field id() -> &str as "The id of the character" { 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()) Some(self.name())
} }
field friends(&executor) -> Vec<&Character> field friends(&executor) -> Vec<&dyn Character>
as "The friends of the character" { as "The friends of the character" {
executor.context().get_friends(self.as_character()) executor.context().get_friends(self.as_character())
} }
@ -26,8 +26,8 @@ graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
} }
instance_resolvers: |&context| { instance_resolvers: |&context| {
&Human => context.get_human(&self.id()), &dyn Human => context.get_human(&self.id()),
&Droid => context.get_droid(&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], interfaces = [&dyn Character],
)] )]
/// A humanoid creature in the Star Wars universe. /// A humanoid creature in the Star Wars universe.
impl<'a> &'a Human { impl<'a> &'a dyn Human {
/// The id of the human /// The id of the human
fn id(&self) -> &str { fn id(&self) -> &str {
self.id() self.id()
@ -49,7 +49,7 @@ impl<'a> &'a Human {
} }
/// The friends of the 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()) ctx.get_friends(self.as_character())
} }
@ -70,7 +70,7 @@ impl<'a> &'a Human {
interfaces = [&dyn Character], interfaces = [&dyn Character],
)] )]
/// A mechanical creature in the Star Wars universe. /// A mechanical creature in the Star Wars universe.
impl<'a> &'a Droid { impl<'a> &'a dyn Droid {
/// The id of the droid /// The id of the droid
fn id(&self) -> &str { fn id(&self) -> &str {
self.id() self.id()
@ -82,7 +82,7 @@ impl<'a> &'a Droid {
} }
/// The friends of the 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()) ctx.get_friends(self.as_character())
} }
@ -106,19 +106,19 @@ pub struct Query;
/// The root query object of the schema /// The root query object of the schema
impl Query { impl Query {
#[graphql(arguments(id(description = "id of the human")))] #[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) database.get_human(&id)
} }
#[graphql(arguments(id(description = "id of the droid")))] #[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) database.get_droid(&id)
} }
#[graphql(arguments(episode( #[graphql(arguments(episode(
description = "If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular 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()) 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() .unwrap()
.ident .ident
.to_string(), .to_string(),
syn::Type::Reference(ref reference) => match &*reference.elem { syn::Type::Reference(ref reference) => {
syn::Type::Path(ref type_path) => type_path let path = match &*reference.elem {
.path syn::Type::Path(ref type_path) => &type_path.path,
.segments syn::Type::TraitObject(ref trait_obj) => {
.iter() match trait_obj.bounds.iter().nth(0).unwrap() {
.last() syn::TypeParamBound::Trait(ref trait_bound) => &trait_bound.path,
.unwrap()
.ident
.to_string(),
_ => { _ => {
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\")");
} }
}, }
}
_ => {
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\")"); panic!("Could not determine a name for the object type: specify one with #[juniper::object(name = \"SomeName\")");
} }

View file

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