How can i implement `std::iter::FromIterator<_>' for Struct?

1.7k views Asked by At

When I try to make the one to many relational query for graph with diesel vie mysql, querying enum fails with fromIterator. helps show i need to implement `std::iter::FromIterator<_> but I don't know how exactly implement the struct. Any helps ? thanks in advance!

error log

error[E0277]: a value of type `&std::vec::Vec<graphql::ContactContent>` cannot be built from an iterator over elements of type `_`
   --> src/graphql.rs:129:89
    |
129 |                 .and_then(|contact_contents| Ok(contact_contents.into_iter().map_into().collect()))
    |                                                                                         ^^^^^^^ value of type `&std::vec::Vec<graphql::ContactContent>` cannot be built from `std::iter::Iterator<Item=_>`
    |
    = help: the trait `std::iter::FromIterator<_>` is not implemented for `&std::vec::Vec<graphql::ContactContent>`

cargo.toml

[package]
name = "lead_generation_api"
version = "0.1.0"
authors = ["ARBOR.JP"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
diesel = { version = "1.4.5", features = ["mysql", "r2d2", "chrono"] }
dotenv = "~0.15"
serde = "~1.0"
serde_derive = "~1.0"
serde_json = "~1.0"
chrono = "~0.4"
rand = "0.7.3"
actix-web = "1.0.9"
actix-cors = "0.1.0"
juniper = "0.14.1"
juniper-from-schema = "0.5.1"
juniper-eager-loading = "0.5.0"
r2d2_mysql = "*"
r2d2-diesel = "0.16.0"
mysql = "*"
r2d2 = "*"
futures01 = "0.1.29"
itertools = "0.8.2"

[[bin]]
name = "main"
path = "src/main.rs"

src/graphql.rs

use std::convert::From;
use std::sync::Arc;
use chrono::NaiveDateTime; 
use actix_web::{web, Error, HttpResponse};
use futures01::future::Future;

use juniper::http::playground::playground_source;
use juniper::{http::GraphQLRequest,GraphQLObject, Executor, FieldResult, FieldError,ID};
use juniper_from_schema::graphql_schema_from_file;

use diesel::prelude::*;

use itertools::Itertools;
use crate::schema::{customers, contact_contents};

use crate::{DbCon, DbPool};

graphql_schema_from_file!("src/schema.graphql");

pub struct Context {
    db_con: DbCon,
}
impl juniper::Context for Context {}

pub struct Query;
pub struct Mutation;

impl QueryFields for Query {
    fn field_customers(
        &self,
        executor: &Executor<'_, Context>,
        _trail: &QueryTrail<'_, Customer, Walked>,
    ) -> FieldResult<Vec<Customer>> {
        //type FieldResult<T> = Result<T, String>;

        customers::table
            .load::<crate::models::Customer>(&executor.context().db_con)
            .and_then(|customers| Ok(customers.into_iter().map_into().collect()))
            .map_err(Into::into)
    }
}

impl MutationFields for Mutation {
    fn field_create_customer(
        &self,
        executor: &Executor<'_, Context>,
        _trail: &QueryTrail<'_, Customer, Walked>,
        name: String,
        email: String,
        contact_contents: Vec<String>,
    ) -> FieldResult<Customer> {
        //type FieldResult<T> = Result<T, String>;

        let new_customer = crate::models::NewCustomer { name: name, email: email};

        diesel::insert_into(customers::table)
            .values(&new_customer)
            .execute(&executor.context().db_con);
            
        customers::table
            .first::<crate::graphql::Customer>(&executor.context().db_con)
            .map_err(Into::into)
    }
}

//#[derive(GraphQLObject)]
//#[graphql_object(name="customer name", email ="mail address")]
#[derive(Queryable)]
pub struct Customer {
    id: u64,
    name: String,
    email: String,
    created_at: NaiveDateTime,
    updated_at: NaiveDateTime,
}

#[derive(Queryable)]
pub struct ContactContent {
        id: u64,
        customer_id: u64,
        email: String,
}
    
impl ContactContentFields for ContactContent {
    fn field_id(&self, _: &Executor<'_, Context>) -> FieldResult<juniper::ID> {
        Ok(juniper::ID::new(self.id.to_string()))
    }

    fn field_customer_id(&self, _: &Executor<'_, Context>) -> FieldResult<juniper::ID> {
        Ok(juniper::ID::new(self.customer_id.to_string()))
    }

    fn field_email(&self, _: &Executor<'_, Context>) -> FieldResult<&String> {
        Ok(&self.email)
    }
}

impl From<crate::models::ContactContent> for ContactContent {
    fn from(contact_contents: crate::models::ContactContent) -> Self {
        Self {
            id: contact_contents.id,
            customer_id: contact_contents.customer_id,
            email: contact_contents.email,
        }
    }
}
    
//#[juniper::object(Context = Context)]
//#[graphql(description = "A Photo returns struct")]
impl CustomerFields for Customer {
    fn field_id(&self, _: &Executor<'_, Context>) -> FieldResult<juniper::ID> {
        Ok(juniper::ID::new(self.id.to_string()))
    }

    fn field_name(&self, _: &Executor<'_, Context>) -> FieldResult<&String> {
        Ok(&self.name)
    }
    fn field_email(&self, _: &Executor<'_, Context>) -> FieldResult<&String> {
        Ok(&self.email)
    }
    fn field_contact_contents(
            &self,
            executor: &Executor<'_, Context>,
            _trail: &QueryTrail<'_, ContactContent, Walked>,
        ) -> FieldResult<&Vec<ContactContent>> {
            contact_contents::table
                .filter(contact_contents::customer_id.eq(&self.id))
                .load::<crate::models::ContactContent>(&executor.context().db_con)
                .and_then(|contact_contents| Ok(contact_contents.into_iter().map_into().collect()))
                .map_err(Into::into)
    }
}

impl From<crate::models::Customer> for Customer {
    fn from(customer: crate::models::Customer) -> Self {
        Self {
            id: customer.id,
            name: customer.name,
            email: customer.email,
            created_at: customer.created_at,
            updated_at: customer.updated_at,
        }
    }
}

fn playground() -> HttpResponse {
    let html = playground_source("");
    HttpResponse::Ok()
        .content_type("text/html; charset=utf-8")
        .body(html)
}

fn graphql(
    schema: web::Data<Arc<Schema>>,
    data: web::Json<GraphQLRequest>,
    db_pool: web::Data<DbPool>,
) -> impl Future<Item = HttpResponse, Error = Error> {
    let ctx = Context {
        db_con: db_pool.get().unwrap(),
    };

    web::block(move || {
        let res = data.execute(&schema, &ctx);
        Ok::<_, serde_json::error::Error>(serde_json::to_string(&res)?)
    })
    .map_err(Error::from)
    .and_then(|customer| {
        Ok(HttpResponse::Ok()
            .content_type("application/json")
            .body(customer))
    })
}

pub fn register(config: &mut web::ServiceConfig) {
    let schema = std::sync::Arc::new(Schema::new(Query, Mutation));

    config
        .data(schema)
        .route("/", web::post().to_async(graphql))
        .route("/", web::get().to(playground));
}

src/schema.rs

table! {
    contact_contents (id) {
        id -> Unsigned<Bigint>,
        customer_id -> Unsigned<Bigint>,
        email -> Varchar,
    }
}

table! {
    customers (id) {
        id -> Unsigned<Bigint>,
        name -> Varchar,
        email -> Varchar,
        created_at -> Timestamp,
        updated_at -> Timestamp,
    }
}

src/schema.graphql

schema {
  query: Query
  mutation: Mutation
}

type Query {
  customers: [Customer!]! @juniper(ownership: "owned")
}

type Mutation {
  createCustomer(
    name: String!
    email: String!
    contactContents: [String!]! 
  ): Customer! @juniper(ownership: "owned")
}

type Customer {
  id: ID! @juniper(ownership: "owned")
  name: String!
  email: String!
  contactContents: [ContactContent!]!
}

type ContactContent {
  id: ID! @juniper(ownership: "owned")
  customerId: ID! @juniper(ownership: "owned")
  email: String!
 }

src/models.rs

use chrono::NaiveDateTime; 
pub struct Query;
pub struct Mutation;


#[derive(Queryable, Identifiable, AsChangeset, Clone, PartialEq, Debug)]
pub struct Customer {
    pub id: u64,
    pub name: String,
    pub email: String,
    pub created_at: NaiveDateTime,
    pub updated_at: NaiveDateTime,
}

use super::schema::customers;
#[derive(Queryable,Insertable, AsChangeset)]
#[table_name="customers"]
pub struct NewCustomer {
    pub name: String,
    pub email: String,
}

use super::schema::contact_contents;
#[derive(Queryable,Identifiable)]
#[table_name="contact_contents"]
pub struct ContactContent {
    pub id: u64,
    pub customer_id: u64,
    pub email: String,
}

src/main.rs

#[macro_use]
extern crate diesel;
extern crate r2d2;
use actix_cors::Cors;
use actix_web::{web, App, HttpServer};

use diesel::{
    prelude::*,
};
use diesel::r2d2::ConnectionManager;

pub mod graphql;
pub mod models;
pub mod schema;

pub type DbPool = r2d2::Pool<ConnectionManager<MysqlConnection>>;
pub type DbCon = r2d2::PooledConnection<ConnectionManager<MysqlConnection>>;

fn main() {
    let db_pool = create_db_pool();
    let port: u16 = std::env::var("PORT")
        .ok()
        .and_then(|p| p.parse().ok())
        .unwrap_or(8080);

    let addr = std::net::SocketAddr::from(([0, 0, 0, 0], port));

    HttpServer::new(move || {
        App::new()
            .data(db_pool.clone())
            .wrap(Cors::new())
            .configure(graphql::register)
            .default_service(web::to(|| "404"))
    })
    .bind(addr)
    .unwrap()
    .run()
    .unwrap();
}

pub fn create_db_pool() -> DbPool {
    let manager = ConnectionManager::<MysqlConnection>::new("mysql://root:example@mysql/test");
    r2d2::Pool::new(manager).expect("Failed to create DB Pool")
}
1

There are 1 answers

1
Masklinn On

There's nothing to implement, the issue here is that the typing of field_contact_contents is not correct: &Vec<ContactContent> would mean that it returns a member of one of its parameters somehow.

Since you're not borrowing the vec from somewhere but creating it from whole cloth inside the method, that makes no sense, the method should return a proper Vec.

Here's a trivial reproduction case: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8f0b6341f7ffd72fe8de334a098295a1