Giter Site home page Giter Site logo

diesel-derive-newtype's Introduction

diesel-derive-newtype

Easy-peasy support of newtypes inside of Diesel.

Rust Crates.io Version

Installation

diesel-derive-newtype supports Diesel according to its major version -- 0.x through 1.x support the corresponding diesel versions, 2.0 supports diesel 2.0, and 2.1 supports 2.1.

New features are only developed for the currently supported version of Diesel.

[dependencies]
diesel-derive-newtype = "2.1.0"

for Diesel 2.0.x you have to tell cargo not to upgrade diesel-derive-newtype:

[dependencies]
diesel-derive-newtype = "~ 2.0.0"

And for Diesel 1.x:

[dependencies]
diesel-derive-newtype = "1.0"

#[derive(DieselNewType)]

This crate exposes a single custom-derive macro DieselNewType which implements ToSql, FromSql, FromSqlRow, Queryable, AsExpression and QueryId for the single-field tuple struct (NewType) it is applied to.

The goal of this project is that:

  • derive(DieselNewType) should be enough for you to use newtypes anywhere you would use their underlying types within Diesel. (plausibly successful)
  • Should get the same compile-time guarantees when using your newtypes as expression elements in Diesel as you do in other rust code (depends on your desires, see Limitations, below.)

Example

This implementation:

#[macro_use]
extern crate diesel_derive_newtype;

#[derive(DieselNewType)] // Doesn't need to be on its own line
#[derive(Debug, Hash, PartialEq, Eq)] // required by diesel
struct MyId(i64);

Allows you to use the MyId struct inside your entities as though they were the underlying type:

table! {
    my_items {
        id -> Integer,
        val -> Integer,
    }
}

#[derive(Debug, PartialEq, Identifiable, Queryable)]
struct MyItem {
    id: MyId,
    val: u8,
}

Oooohhh. Ahhhh.

See the tests for a more complete example.

Limitations

The DieselNewtype derive does not create new database types, or Diesel serialization types. That is, if you have a MyId(i64), this will use Diesel's underlying BigInt type, which means that even though your newtypes can be used anywhere the underlying type can be used, the underlying types, or any other newtypes of the same underlying type, can be used as well.

At a certain point everything does become bits on the wire, so if we didn't do it this way then Diesel would have to do it somewhere else, and this is reasonable default behavior (it's pretty debuggable), but I'm investigating auto-generating new proxy types as well to make it impossible to construct an insert statement using a tuple or a mis-typed struct.

Here's an example of that this type-hole looks like:

#[derive(Debug, Hash, PartialEq, Eq, DieselNewType)]
struct OneId(i64);

#[derive(Debug, Hash, PartialEq, Eq, DieselNewType)]
struct OtherId(i64);

#[derive(Debug, Clone, PartialEq, Identifiable, Insertable, Queryable)]
#[diesel(table_name = my_entities)]
pub struct MyEntity {
    id: OneId,
    val: i32,
}

fn darn(conn: &Connection) {
    // shouldn't allow constructing the wrong type, but does
    let OtherId: Vec<OtherId> = my_entities
        .select(id)
        .filter(id.eq(OtherId(1)))  // shouldn't allow filtering by wrong type
        .execute(conn).unwrap();
}

See tests/should-not-compile.rs for the things I think should fail to compile.

I believe that the root cause of this is that Diesel implements the various expression methods for types that implement AsExpression, based on the SQL type, not caring about self and other's Rust type matching. That seems like a pretty good decision in general, but it is a bit unfortunate here.

I hope to find a solution that doesn't involve implementing every *Expression trait manually with an extra bound, but for now you have to keep in mind that the Diesel methods basically auto-transmute your data into the underlying SQL type.

Releasing

This workflow uses:

Run, note that we always release patch releases unless diesel has got a new release:

cargo readme > README.md
git diff --exit-code --quiet README.* || (git add README.* && git commit -m "chore: Update README")
cargo release patch

License

diesel-derive-newtype is licensed under either of

at your option.

Patches and bug reports welcome!

diesel-derive-newtype's People

Contributors

adwhit avatar damszew avatar djc avatar ethan-lowman-fp avatar killercup avatar marhag87 avatar oeed avatar quodlibetor avatar sd2k avatar weiznich avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

diesel-derive-newtype's Issues

Required Diesel features are not documented

Create a project:

cargo new repro

cd repro

cargo add diesel diesel-derive-newtype --no-default-features --features=diesel/postgres

Use the derives:

use diesel_derive_newtype::DieselNewType;

#[derive(Debug, DieselNewType)]
struct Wrapper(i64);

fn main() {}

Build it:

cargo build

error[E0412]: cannot find type `RawValue` in module `diesel::backend`
 --> src/main.rs:3:17
  |
3 | #[derive(Debug, DieselNewType)]
  |                 ^^^^^^^^^^^^^ not found in `diesel::backend`
  |
  = note: this error originates in the derive macro `DieselNewType` (in Nightly builds, run with -Z macro-backtrace for more info)

outdated compatibiity information

The readme currently says:

diesel-derive-newtype supports Diesel 0.16 - 0.99.

While Cargo.toml declares diesel = "1" as dependency!

Keep a type's invariants

Is it possible to implement the derive in such a way, that it uses a type's new() should that exist?

That would help guarantee a type's invariants.

Fully namespace `Result`

Hi, thanks for the useful lib.

Hit a confusing error when deriving a newtype within a module which clobbers the Result type. This is a pretty common idiom AFAICT.

type Result<T> = std::result::Result<T, MyError>;

#[derive(DieselNewType)]
pub struct Id(i32);

Gives the unhelpful error:

error[E0244]: wrong number of type arguments: expected 1, found 2
  --> src/lib.rs:16:10
   |
16 | #[derive(DieselNewType)]
   |          ^^^^^^^^^^^^^ expected 1 type argument

A fix would be to fully-namespace the Result type whereever it is used.

Support Diesel 2.0/Master

I know this crate is little old now, but it'd be great to have this working again with Diesel 2.0 (master branch) - although I'd understand reservations as it's not technically stable. A number of changes in Diesel appear to have broken this crate sadly.

Doesn't work with the new resolver

Compiling an empty project with the following Cargo.toml throws numerous errors.

[package]
name = "test"
version = "0.1.0"
edition = "2018"
resolver = "2"

[dependencies]
diesel = { version = "1.4.5", features = ["postgres"] }
diesel-derive-newtype = { version = "0.1.2" }

Errors look like

error[E0432]: unresolved import `diesel`
  --> /home/me/.cargo/registry/src/github.com-1ecc6299db9ec823/diesel-1.4.6/src/sql_types/mod.rs:37:48
   |
37 | #[derive(Debug, Clone, Copy, Default, QueryId, SqlType)]
   |                                                ^^^^^^^ could not find `pg` in `diesel`
   |
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

It works fine without resolver = "2"

I guess the previous resolver was implicitly activating the postgres feature of the diesel dependency of this crate.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.