Skip to content

Support for generic endpoints #132

@rcgoodfellow

Description

@rcgoodfellow

Generic functions are not currently supported as Dropshot endpoints. This capability would be useful for writing generic Dropshot interfaces over traits. A minimum reproducible example is provided below.

In the mean time, the error message that comes up when this is attempted should be something to the effect of "generic endpoints are not currently supported". The error that comes up now (below) does not make it clear what's going on.

Example:

use std::sync::Arc;
use dropshot::{        
    endpoint,
    ConfigDropshot,
    ConfigLogging,
    ConfigLoggingLevel,
    ApiDescription,
    HttpServerStarter,
    RequestContext,
    HttpResponseOk,
    HttpError,
};

trait Greeter {
    fn say_hello(&self) -> String;
}

struct Context<G: Greeter + std::marker::Send + std::marker::Sync + 'static> {
    greeter: G,
}

#[endpoint { method = GET, path = "/greet" }]                                                      
async fn greet<G: Greeter + std::marker::Send + std::marker::Sync + 'static>(
    ctx: Arc<RequestContext<Context<G>>>
) -> Result<HttpResponseOk<String>, HttpError> {

    let api_context = ctx.context();
    Ok(HttpResponseOk(api_context.greeter.say_hello()))

}                                                                                                  

#[tokio::main]
async fn main() -> Result<(), String> {

    let log =
        ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }
        .to_logger("minimal-example")
        .map_err(|e| e.to_string())?;

    let mut api = ApiDescription::new();
    // I realize this bit is going to be tricky, as the concrete type to make this an
    // instantiated function does not come until the HttpServerStarter::new call
    // when we pass in the Context with the specific Greeter implementation
    api.register(greet).unwrap();

    let api_context = Context{ greeter: Mandarin{} };

    let server = HttpServerStarter::new(
        &ConfigDropshot { ..Default::default() },
        api,
        api_context,
        &log,
    ).expect("server new").start();

    server.await

}

struct Mandarin {}
impl Greeter for Mandarin {
    fn say_hello(&self) -> String { "你好".to_string() }
}

struct English{}
impl Greeter for English {
    fn say_hello(&self) -> String { "hello".to_string() }
}

Which produces the compiler error

error[E0412]: cannot find type `G` in this scope
  --> src/main.rs:24:37
   |
24 |     ctx: Arc<RequestContext<Context<G>>>
   |                                     ^   - help: you might be missing a type parameter: `<G>`
   |                                     |
   |                                     not found in this scope

error[E0412]: cannot find type `G` in this scope
  --> src/main.rs:24:37
   |
24 |     ctx: Arc<RequestContext<Context<G>>>
   |                                     ^ not found in this scope

error[E0277]: the trait bound `ApiEndpoint<_>: From<greet>` is not satisfied
  --> src/main.rs:41:9
   |
41 |     api.register(greet).unwrap();
   |         ^^^^^^^^ the trait `From<greet>` is not implemented for `ApiEndpoint<_>`
   |
   = note: required because of the requirements on the impl of `Into<ApiEndpoint<_>>` for `greet`

Some errors have detailed explanations: E0277, E0412.
For more information about an error, try `rustc --explain E0277`.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions