Есть ли более простой способ создать пользовательский метод Filter для вызова обработчика деформации? - PullRequest
1 голос
/ 13 апреля 2020

Вместо того, чтобы писать что-то вроде:

let hello = get()
    .and(path!(String))
    .and_then(|string| handlers::hello(string).map_err(warp::reject::custom))
    .boxed()

Я бы хотел написать:

let hello = get()
    .and(path!(String))
    .handle(handlers::hello)

, где handle делает and_then -> map_err -> boxed вещь.

Сейчас я использую набор черт для каждого обработчика арности. Это 2-арный обработчик:

pub trait Handle2<A, B, Fut, R, F>
where
    R: Reject,
    F: Fn(A, B) -> Fut,
    Fut: Future<Output = Result<String, R>>,
{
    fn handle(self, f: F) -> BoxedFilter<(String,)>;
}

impl<A, B, Fut, R, F, T, Futr> Handle2<A, B, Fut, R, F> for T
where
    R: Reject,
    F: Fn(A, B) -> Fut + Clone + Sync + Send + 'static,
    Fut: Future<Output = Result<String, R>> + Send,
    Futr: Future<Output = Result<(A, B), Rejection>> + Send,
    T: Filter<Extract = (A, B), Error = Rejection, Future = Futr> + Sync + Send + 'static,
{
    fn handle(self, f: F) -> BoxedFilter<(String,)> {
        self.and_then(move |a, b| f(a, b).map_err(warp::reject::custom))
            .boxed()
    }
}

Удивительно, что вы можете сделать это в Rust, но есть ли более простой способ добиться этого?

1 Ответ

0 голосов
/ 18 апреля 2020

Есть.

С таким маршрутом, как этот, объявленный в вашем main ():

let routes = get().and(path("hello")).and_then(routes::getRoot);

Вы можете использовать синтаксический сахар async для автоматического преобразования простого обработчика в будущее (0,3 / стандарт один). Это заставляет его реализовывать все необходимые черты, чтобы использовать его как warp::Filter:

// routes/mod.rs

pub async fn getRoot() -> Result<impl warp::Reply, warp::Rejection> {
    Ok("Hello world !")
}

Довольно просто пока! Теперь единственное, чего вам не хватает, - правильной обработки ошибок с помощью warp::reject::custom.

. Сначала давайте заставим наш обработчик возвращать ошибку:

// routes/mod.rs

pub async fn getRoot() -> Result<impl warp::Reply, warp::Rejection> {
    let _parsed_url = Url::parse(&"https://whydoesn.it/work?").map_err(ServiceError::from)?;
    Ok("Hello world !")
}

Обратите внимание на map_err(ServiceError::from) и тип возврата warp::Rejection? Нам нужно превратить нашу указанную ошибку c в специальное перечисление ошибок (это ServiceError, мы определим его в ближайшее время), а затем предоставить способ автоматического преобразования ServiceError в warp :: Rejection.

Давайте сделаем это, создав способ обработки ошибок из всех функций обработчика маршрута:

// ./errors.rs

/// An internal error enum for representing all the possible failure states
#[derive(thiserror::Error, Debug)]
pub enum ServiceError {
    #[error("unacceptable !")]
    SomeSpecificWayOfFailing,
    #[error(transparent)]
    Other(#[from] Box<dyn std::error::Error + Sync + Send>), // catchAll error type
}
impl warp::reject::Reject for ServiceError {}
impl From<ServiceError> for warp::reject::Rejection {
    fn from(e: ServiceError) -> Self {
        warp::reject::custom(e)
    }
}

В нашем случае приведенный мной пример ошибки возвращает url::ParseError. Давайте добавим способ привязать его к ServiceError:

// ./errors.rs

impl From<url::ParseError> for ServiceError {
    fn from(e: url::ParseError) -> Self {
        ServiceError::Other(e.into()) // you can refine that and turn it into a more specific enum variant of ServiceError here
    }
}

У вас все должно быть готово. Теперь добавить новый маршрут / обработчик так же просто, как добавить функцию asyn c (плюс добавление impl ServiceError::from для любого нового типа ошибки, который вы хотите обработать в функции обработчика)

...