blackbeam / rust-mysql-simple

Mysql client library implemented in rust.
Apache License 2.0
658 stars 144 forks source link

How to implement FromValue for a custom enum? #279

Closed svanharmelen closed 3 years ago

svanharmelen commented 3 years ago

I struggle to find an example or some details about what I need to implement so that I "read" data from a specific column into a custom enum.

blackbeam commented 3 years ago

Hi. You mean reading MySql ENUM into rust enum?

First of all you should note, that mysql enums have both string and integer representation. Also you should note, that integer representation may change if new variants are added to an ENUM column.

Here is a rough implementation for col ENUM('foo', 'bar') (not tested):

#[repr(u8)]
pub enum Col {
    Foo = 1,
    Bar = 2,
}

impl From<Col> for Value {
    fn from(ty: Col) -> Self {
        Value::Int(ty as u8 as i64)
    }
}

impl ConvIr<Col> for Col {
    fn new(v: Value) -> Result<Self, FromValueError> {
        match v {
            Value::Bytes(ref bytes) if *bytes == "foo".as_bytes() => Ok(Self::Foo),
            Value::Bytes(ref bytes) if *bytes == "bar".as_bytes() => Ok(Self::Bar),
            Value::Int(1) | Value::UInt(1) => Ok(Self::Foo),
            Value::Int(2) | Value::UInt(2) => Ok(Self::Bar),
            v => Err(FromValueError(v)),
        }
    }

    fn commit(self) -> Self {
        self
    }

    fn rollback(self) -> Value {
        self.into()
    }
}

impl FromValue for ReadingRoomType {
    type Intermediate = Self;
}
svanharmelen commented 3 years ago

Ah, super nice! Thanks for the info/pointer! It was enough to make it work 👍🏻

I probably should have been more clear, but I was only talking about using a Rust enum. So my solution now looks like this:

#[derive(Debug)]
enum Kind {
    Foo,
    Bar,
}

impl ConvIr<Kind> for Kind {
    fn new(v: Value) -> std::result::Result<Kind, FromValueError> {
        match v {
            Value::Bytes(bytes) => match bytes.as_slice() {
                b"Foo" => Ok(Kind::Foo),
                b"Bar" => Ok(Kind::Bar),
                _ => Err(FromValueError(Value::Bytes(bytes))),
            },
            v => Err(FromValueError(v)),
        }
    }
    fn commit(self) -> Kind {
        self
    }

    fn rollback(self) -> Value {
        match self {
            Kind::Foo => Value::Bytes(b"Foo".to_vec()),
            Kind::Bar => Value::Bytes(b"Bar".to_vec()),
        }
    }
}

impl FromValue for Kind {
    type Intermediate = Kind;
}