как получить значение из запроса без использования вложенных совпадений и развертывания - PullRequest
0 голосов
/ 28 июня 2019

Я хочу выполнить некоторые запросы MySQL, но обработать ошибки другого типа, от Error::IoError для проверки некоторых тайм-аутов, до пользовательского типа ошибки NotMatching(String), когда возвращаемый элемент из запроса не соответствует, следуя другому вопросу , я придумал это:

use std::{error, fmt};

#[derive(Debug)]
pub enum Error {
    MySQL(mysql::Error),
    NotMatching(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::MySQL(ref err) => err.fmt(f),
            Error::NotMatching(ref err) => err.fmt(f),
        }
    }
}

impl error::Error for Error {}

impl From<mysql::Error> for Error {
    fn from(err: mysql::Error) -> Self {
        Error::MySQL(err)
    }
}

pub struct Queries {
    pool: mysql::Pool,
}

pub fn new(pool: mysql::Pool) -> Queries {
    return Queries { pool: pool };
}

impl Queries {
    pub fn test_rw(&self, now: u64) -> Result<(), Error> {
        let pool = &self.pool.clone();

        // create table
        pool.prep_exec("CREATE TABLE IF NOT EXISTS dbpulse_rw (id INT NOT NULL, t INT(11) NOT NULL, PRIMARY KEY(id))", ())?;

        // write into table
        let mut stmt = pool
            .prepare("INSERT INTO dbpulse_rw (id, t) VALUES (1, ?) ON DUPLICATE KEY UPDATE t=?")?;
        stmt.execute((now, now))?;

        let rows = pool.prep_exec("SELECT t FROM dbpulse_rw WHERE id=1", ())?;
        for row in rows {
            match row {
                Ok(row) => match mysql::from_row_opt::<u64>(row) {
                    Ok(row) => {
                        if now != row {
                            return Result::Err(Error::NotMatching("no matching...".into()));
                        }
                    }
                    Err(e) => {
                        return Result::Err(Error::MySQL(e.into()));
                    }
                },
                Err(e) => {
                    return Result::Err(Error::MySQL(e));
                }
            }
        }
        Ok(())
    }
}

Это работает, но интересно, есть ли лучший (чистый) способ, возможно, использующий and_then или map, чтобы уменьшить код для получения одного значения из запроса, я в основном имею в виду эта часть:

let rows = pool.prep_exec("SELECT t FROM dbpulse_rw WHERE id=1", ())?;
for row in rows {
    match row {
        Ok(row) => match mysql::from_row_opt::<u64>(row) {
            Ok(row) => {
                if now != row {
                    return Result::Err(Error::NotMatching("no matching...".into()));
                }
            }
            Err(e) => {
                return Result::Err(Error::MySQL(e.into()));
            }
        },
        Err(e) => {
            return Result::Err(Error::MySQL(e));
        }
    }
}

Я бы хотел предотвратить панику и лучше обработать ошибку, поэтому я опускаю использование unwrap, но был бы признателен за некоторые примеры того, как это можно улучшить.

1 Ответ

1 голос
/ 28 июня 2019

? и map_err должны помочь избавиться от большей части вложенности.Эскиз:

let rows = pool.prep_exec("SELECT t FROM dbpulse_rw WHERE id=1", ())?;
for row in rows {
    let row = row.map_err(Error::MySQL)?;
    let row = mysql::from_row_opt::<u64>(row).map_err(|e| Error::MySQL(e.into()))?;
    if now != row {
        return Err(Error::NotMatching("no matching...".into()));
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...