tokio-rs / tokio-service

The core `Service` trait in Tokio and support
Apache License 2.0
82 stars 19 forks source link

Unable to create new trait based on `tokio_service::Service` #26

Open ghost opened 7 years ago

ghost commented 7 years ago

So while creating a new trait that is base on the tokio_service::Service on, I run into some problems. My current code (in short form) looks like:

// Some crate stuff here
// ZapError = std::io::Error
// ZapResult = future::Ok<Response, io::Error>
// Full source: https://github.com/oldaniel/zap
trait Controller {
    type Request = Request;
    type Response = Response;
    type Error = ZapError;
    type Future = ZapResult;

    fn call(&self, req: Request) -> ZapResult;
}

struct HelloWorld;

impl Controller for HelloWorld {
        let mut resp = Response::new();

        resp.body("Hello World!");

        resp.ok()
    }
}

fn main() {
    let addr = "0.0.0.0:8080".parse().unwrap();
    let mut server = Server::new(Http, addr);
    server.threads(8);
    server.serve(move || Ok(HelloWorld));
}

The error I ran into was:

error[E0277]: the trait bound `HelloWorld: tokio_service::Service` is not satisfied
  --> examples/hello-world.rs:21:12
   |
21 |     server.serve(move || Ok(HelloWorld));
   |            ^^^^^ the trait `tokio_service::Service` is not implemented for `HelloWorld`
   |
   = note: required because of the requirements on the impl of `tokio_service::NewService` for `[closure@examples/hello-world.rs:21:18: 21:40]`

error: aborting due to previous error

So how can I create a custom trait based on tokio_service::Service?

anton-dutov commented 7 years ago

Please provide complete example, https://github.com/oldaniel/zap contains just lib, seems to train Service not implemented

Instead of

trait Controller {
    type Request = Request;
    type Response = Response;
    type Error = ZapError;
    type Future = ZapResult;

    fn call(&self, req: Request) -> ZapResult;
}

try implement service

impl Service for Server {
    type Request = Request;
    type Response = Response;
    type Error = ZapError;
    type Future = ZapResult;

    fn call(&self, req: Request) -> ZapResult {

    }
} 
ghost commented 7 years ago

@anton-dutov

My current lib.rs:

 #![feature(associated_type_defaults)]
// Load all crates and modules
extern crate bytes;
extern crate futures;
extern crate httparse;
extern crate tokio_io;
extern crate tokio_proto;

mod request;
mod response;

// Use the stuff we need to
use std::io;

use bytes::BytesMut;
use tokio_io::codec::{Encoder, Decoder, Framed};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_proto::pipeline::ServerProto;
use futures::future;

// Make Server, Handler, Request and Response public accessable
pub use tokio_proto::TcpServer as Server;
pub use request::Request;
pub use response::Response;

/// The expected response result
pub type ZapResult = future::Ok<Response, io::Error>;
pub use std::io::Error as ZapError;

pub trait Controller {
    /// Requests handled by the service.
    type Request = Request;

    /// Responses given by the service.
    type Response = Response;

    /// Errors produced by the service.
    type Error = ZapError;

    /// The future response value.
    type Future = ZapResult;

    /// Process the request and return the response asynchronously.
    fn call(&self, req: Request) -> ZapResult;
}

/// A module to import the required things.
pub mod prelude {
    pub use ZapResult;
    pub use Server;
    pub use request::Request;
    pub use response::Response;
    pub use Http;
    pub use Controller;
    pub use ZapError;
}

/// Handling incoming requests with tokio-proto
pub struct Http;

impl<T: AsyncRead + AsyncWrite + 'static> ServerProto<T> for Http {
    // Setup ServerProto types
    type Request = Request;
    type Response = Response;
    type Transport = Framed<T, HttpCodec>;
    type BindTransport = io::Result<Framed<T, HttpCodec>>;

    fn bind_transport(&self, io: T) -> io::Result<Framed<T, HttpCodec>> {
        // Frame the request with tokio-io and handle it with HttpCodec
        Ok(io.framed(HttpCodec))
    }
}

/// Encode and decode our request
pub struct HttpCodec;

impl Decoder for HttpCodec {
    type Item = Request;
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<Request>> {
        // Decode our buffer
        request::decode(buf)
    }
}

impl Encoder for HttpCodec {
    type Item = Response;
    type Error = io::Error;

    fn encode(&mut self, msg: Response, buf: &mut BytesMut) -> io::Result<()> {
        // Encode and write response
        response::encode(&msg, buf);
        Ok(())
    }
}

And my current hello-world.rs:

extern crate zap;

use zap::prelude::*;

struct HelloWorld;

impl Controller for HelloWorld {
    fn call(&self, _: zap::Request) -> ZapResult {
        let mut resp = Response::new();

        resp.body("Hello World!");

        resp.ok()
    }
}

fn main() {
    let addr = "0.0.0.0:8080".parse().unwrap();
    let mut server = Server::new(Http, addr);
    server.threads(8);
    server.serve(move || Ok(HelloWorld));
}
anton-dutov commented 7 years ago

Try that

extern crate zap;
extern crate futures;
extern crate tokio_service;

use futures::{Future, BoxFuture};
use tokio_service::Service;

use std::io::Error as ZapError;

use zap::prelude::{Server, Http};
use zap::prelude::Request  as ZapRequest;
use zap::prelude::Response as ZapResponse;

struct HelloWorld;

impl Service for HelloWorld {
    type Request  = ZapRequest;
    type Response = ZapResponse;
    type Error    = ZapError;
    type Future   = BoxFuture<Self::Response, Self::Error>;

    fn call(&self, _: Self::Request) -> Self::Future {
        let mut resp = Self::Response::new();

        resp.body("Hello World!");

        futures::finished(resp).boxed()
    }
}

fn main() {
    let addr = "0.0.0.0:8080".parse().unwrap();
    let mut server = Server::new(Http, addr);
    server.threads(8);
    server.serve(move || Ok(HelloWorld));
}

Cargo.toml

[dependencies]
zap =  { git = "https://github.com/oldaniel/zap" }
futures = "0.1"
tokio-service = "0.1"
ghost commented 7 years ago

@anton-dutov That's what I had in the early Version. I want to have my own custom trait of tokio_service::Service so the user saves lines of the type stuff, becuaae they are the same anyway.

anton-dutov commented 7 years ago

Look at https://github.com/tokio-rs/tokio-proto/blob/master/src/tcp_server.rs#L84 You should implement Service trait anyway or create custom wrapper around service creation with advanced abstraction layer