Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/adwhit/diesel-derive-enum
Use enums with Diesel ORM
https://github.com/adwhit/diesel-derive-enum
Last synced: 13 days ago
JSON representation
Use enums with Diesel ORM
- Host: GitHub
- URL: https://github.com/adwhit/diesel-derive-enum
- Owner: adwhit
- License: apache-2.0
- Created: 2018-01-03T11:15:25.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2024-10-15T18:37:24.000Z (29 days ago)
- Last Synced: 2024-10-30T12:51:23.483Z (14 days ago)
- Language: Rust
- Homepage:
- Size: 166 KB
- Stars: 267
- Watchers: 3
- Forks: 30
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
- License: LICENSE-APACHE
Awesome Lists containing this project
README
# diesel-derive-enum
[![crates.io](https://img.shields.io/crates/v/diesel-derive-enum.svg)](https://crates.io/crates/diesel-derive-enum)
![Build Status](https://github.com/adwhit/diesel-derive-enum/workflows/CI/badge.svg)Use Rust enums directly with [`diesel`](https://github.com/diesel-rs/diesel) ORM.
Diesel is great, but wouldn't it be nice if this would work?
``` rust
use crate::schema::my_table;
pub enum MyEnum {
Foo,
Bar,
BazQuxx,
}fn do_some_work(data: MyEnum, connection: &mut Connection) {
insert_into(my_table)
.values(&data)
.execute(connection)
.unwrap();
}
```Unfortunately, it won't work out of the box, because any type which
we wish to use with Diesel must implement various traits.
Tedious to do by hand, easy to do with a `derive` macro - enter `diesel-derive-enum`.The latest release, `2.1.0`, is tested against `diesel 2.1.0` and `rustc 1.65` (we try to keep in lock-step with `diesel`).
For earlier versions of `diesel`, check out the `2.0.1` and `1.*` releases of this crate.## Upgrading from `2.0.x` -> `2.1.0`
Using `diesel-cli`? Due to an upstream change, you may need to modify your existing `diesel.toml` file.
As of version `2.1.0`, it **must** contain the following lines:
```
[print-schema]
# ... other config ...
custom_type_derives = ["diesel::query_builder::QueryId"]
```
So if it doesn't - add it!## Setup with Diesel CLI
This crate integrates nicely with
[diesel-cli](http://diesel.rs/guides/configuring-diesel-cli.html) -
this is the recommended workflow.
Note that for now, this **only** works with Postgres - for other databases,
or if not using Diesel CLI, see the next section.Cargo.toml:
```toml
[dependencies]
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
```Suppose our project has the following `diesel.toml` (as generated by `diesel setup`):
``` toml
[print_schema]
file = "src/schema.rs"
custom_type_derives = ["diesel::query_builder::QueryId"]
```And the following SQL:
```sql
CREATE TYPE my_enum AS ENUM ('foo', 'bar', 'baz_quxx');CREATE TABLE my_table (
id SERIAL PRIMARY KEY,
some_enum my_enum NOT NULL
);
```Then running `$ diesel migration run` will generate code something like:
```rust
// src/schema.rs -- autogeneratedpub mod sql_types {
#[derive(diesel::sql_types::SqlType, diesel::query_builder::QueryId)]
#[diesel(postgres_type(name = "my_enum"))]
pub struct MyEnum;
}table! {
use diesel::types::Integer;
use super::sql_types::MyEnum;my_table {
id -> Integer,
some_enum -> MyEnum
}
}
```Now we can use `diesel-derive-enum` to hook in our own enum:
```rust
// src/my_code.rs#[derive(diesel_derive_enum::DbEnum)]
#[ExistingTypePath = "crate::schema::sql_types::MyEnum"]
pub enum MyEnum {
Foo,
Bar,
BazQuxx,
}
```Note the `ExistingTypePath` attribute. This instructs this crate to import the
(remote, autogenerated) type and implement various traits upon it. That's it!
Now we can use `MyEnum` with `diesel` (see 'Usage' below).## Setup without Diesel CLI
If you are using `mysql` or `sqlite`, or you aren't using `diesel-cli` to generate
your schema, the setup is a little different.### Postgres
Cargo.toml:
```toml
[dependencies]
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
```SQL:
```sql
CREATE TYPE my_enum AS ENUM ('foo', 'bar', 'baz_quxx');CREATE TABLE my_table (
id SERIAL PRIMARY KEY,
some_enum my_enum NOT NULL
);
```Rust:
```rust
#[derive(diesel_derive_enum::DbEnum)]
pub enum MyEnum {
Foo,
Bar,
BazQuxx,
}// define your table
table! {
use diesel::types::Integer;
use super::MyEnumMapping;
my_table {
id -> Integer,
some_enum -> MyEnumMapping, // Generated Diesel type - see below for explanation
}
}
```### MySQL
Cargo.toml:
```toml
[dependencies]
diesel-derive-enum = { version = "2.1.0", features = ["mysql"] }
```SQL:
```sql
CREATE TABLE my_table (
id SERIAL PRIMARY KEY,
my_enum enum('foo', 'bar', 'baz_quxx') NOT NULL -- note: snake_case
);
```Rust:
```rust
#[derive(diesel_derive_enum::DbEnum)]
pub enum MyEnum {
Foo,
Bar,
BazQuxx,
}// define your table
table! {
use diesel::types::Integer;
use super::MyEnumMapping;
my_table {
id -> Integer,
some_enum -> MyEnumMapping, // Generated Diesel type - see below for explanation
}
}
```### sqlite
Cargo.toml:
```toml
[dependencies]
diesel-derive-enum = { version = "2.1.0", features = ["sqlite"] }
```SQL:
```sql
CREATE TABLE my_table (
id SERIAL PRIMARY KEY,
my_enum TEXT CHECK(my_enum IN ('foo', 'bar', 'baz_quxx')) NOT NULL -- note: snake_case
);
```Rust:
``` rust
#[derive(diesel_derive_enum::DbEnum)]
pub enum MyEnum {
Foo,
Bar,
BazQuxx,
}// define your table
table! {
use diesel::types::Integer;
use super::MyEnumMapping;
my_table {
id -> Integer,
some_enum -> MyEnumMapping, // Generated Diesel type - see below for explanation
}
}
```## Usage
Once set up, usage is similar regardless of your chosen database.
We can define a struct with which to populate/query the table:``` rust
#[derive(Insertable, Queryable, Identifiable, Debug, PartialEq)]
#[diesel(table_name = my_table)]
struct MyRow {
id: i32,
some_enum: MyEnum,
}
```And use it in the natural way:
```rust
let data = vec![
MyRow {
id: 1,
some_enum: MyEnum::Foo,
},
MyRow {
id: 2,
some_enum: MyEnum::BazQuxx,
},
];
let connection = PgConnection::establish(/*...*/).unwrap();
let inserted = insert_into(my_table::table)
.values(&data)
.get_results(&connection)
.unwrap();
assert_eq!(data, inserted);
```
Postgres arrays work too! See [this example.](tests/src/pg_array.rs)### Enums Representations
Enums are not part of the SQL standard and have database-specific implementations.
* In Postgres, we declare an enum as a separate type within a schema (`CREATE TYPE ...`),
which may then be used in multiple tables. Internally, an enum value is encoded as an int (four bytes)
and stored inline within a row (a much more efficient representation than a string).* MySQL is similar except the enum is not declared as a separate type and is 'local' to
it's parent table. It is encoded as either one or two bytes.* sqlite does not have enums - in fact, it does
[not really have types](https://dba.stackexchange.com/questions/106364/text-string-stored-in-sqlite-integer-column);
you can store any kind of data in any column. Instead we emulate static checking by
adding the `CHECK` command, as per above. This does not give a more compact encoding
but does ensure better data integrity. Note that if you somehow retreive some other invalid
text as an enum, `diesel` will error at the point of deserialization.### How It Works
Diesel maintains a set of internal types which correspond one-to-one to the types available in various
relational databases. Each internal type in turn maps to some kind of Rust native type.
e.g. Postgres `INTEGER` maps to `diesel::types::Integer` maps to `i32`.*For `postgres` only*, as of `diesel-2.0.0`, diesel-cli will create the 'dummy' internal
enum mapping type as part of the schema generation process.
We then specify the location of this type with the `ExistingTypePath` attribute.In the case where `ExistingTypePath` is **not** specified, we assume the internal type
has *not* already been generated, so this macro will instead create it
with the default name `{enum_name}Mapping`. This name can be overridden with the `DieselType` attribute.In either case, this macro will then implement various traits on the internal type.
This macro will also implement various traits on the user-defined `enum` type.
The net result of this is that the user-defined enum can be directly inserted into (and retrieved
from) the diesel database.Note that by default we assume that the possible SQL ENUM variants are simply the Rust enum variants
translated to `snake_case`. These can be renamed with the inline annotation `#[db_rename = "..."]`.See [this test](tests/src/rename.rs) for an example of renaming.
You can override the `snake_case` assumption for the entire enum using the `#[DbValueStyle = "..."]`
attribute. Individual variants can still be renamed using `#[db_rename = "..."]`.| DbValueStyle | Variant | Value |
|:-------------------:|:---------:|:---|
| camelCase | BazQuxx | "bazQuxx" |
| kebab-case | BazQuxx | "baz-quxx" |
| PascalCase | BazQuxx | "BazQuxx" |
| SCREAMING_SNAKE_CASE | BazQuxx | "BAZ_QUXX" |
| UPPERCASE | BazQuxx | "BAZQUXX" |
| snake_case | BazQuxx | "baz_quxx" |
| verbatim | Baz__quxx | "Baz__quxx" |See [this test](tests/src/value_style.rs) for an example of changing the output style.
### License
Licensed under either of these:
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
https://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
https://opensource.org/licenses/MIT)