nbari
nbari

Reputation: 26925

how to get a value from a query without using nested matches and unwrapping

I want to run some MySQL queries but handle different type of errors, from Error::IoError to check some timeouts, to a custom error type NotMatching(String) when the returned item from a query doesn't match, following up in other question, I came up with this:

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(())
    }
}

It works but wondering if there is a better (clean) way, probably using and_then or map to reduce the code for getting a single value from the query, I am mainly referring to this part:

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));
        }
    }
}

I would like to prevent panicking and handle better the error, that's why I am omitting the usage of unwrap but would appreciate some examples about how could this be improved.

Upvotes: 0

Views: 327

Answers (1)

stephaneyfx
stephaneyfx

Reputation: 326

? and map_err should help getting rid of most of the nesting. Sketch:

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()));
    }
}

Upvotes: 1

Related Questions