MikeTheSapien
MikeTheSapien

Reputation: 297

How to implement a Rust macro using module paths as a macro function parameter?

As asked by @isaactfa,

PS C:\...> rustc --version
rustc 1.62.1 (e092d0b6b 2022-07-16)
// Cargo.toml
[dependencies]
diesel = { version = "1.4.5", features = ["postgres", "sqlite", "chrono"] }
chrono = "0.4"
libsqlite3-sys = { version = "^0", features = ["bundled"] }

Original Post

I've looked at the code more than a dozen times here: What fragment specifiers (metavariable types) should be used in macro-by-example for a module path + type name?, and I fear I'm too dense to grasp it. I tried tinkering with it myself but don't understand what's happening. I looked for examples and tutorials for Rust macros but are either too basic or too advance that half of the words are alien to me.

Like OP on that SO post, I'm using diesel. Now, how can I use as a macro!() parameter a module path? A module that is created by diesel::table!()?

The following code works:

use diesel::table;

table! {
    user (id) {
        id -> Integer,
        uuid -> Text,
        username -> Text,
    }
}

#[derive(Queryable)]
struct User {
    id: i32,
    uuid: String,
    username: String
}

macro_rules! my_macro {
    ($table:path, $username:path) => {
        impl User {
            pub fn search_w_username(
                username: String,
                conn: &DB,
            ) -> Result<Option<Self>, diesel::result::Error> {
                use crate::diesel::ExpressionMethods;
                use crate::diesel::{QueryDsl, RunQueryDsl};

                match $table
                    .filter($username.eq(username))
                    .load::<User>(conn)
                {
                    ...
                }
            }
        }
    };
}

my_macro!(user::table, user::columns::username);

But of course, the code above is a code accepted from a defeated man, and it's bugging me for more than a week now.

using the same code above, the following would fail to compile

use diesel::table;
...

macro_rules! my_macro {
    ($module_path:path) => {
        use $module_path::table;
        use $module_path::columns::username;
        ...
    }
}

Compiler error thrown:

expected one of `.`, `?`, `{`, or an operator, found `::`

As mentioned by Chayim Friedman who answered the SO post above, the error above is due to an invalid AST (another term I don't understand).

What metavariables and/or how to implement the mentioned "tt_munching", that I'm also having a hard time to understand, should be done so that the macro function signature should just be like the following:

use diesel::table

table! {
    user (id) {
        id -> Integer,
        uuid -> Text,
        username -> Text,
    }
}

macro_rules! my_macro {
    ...
}

my_macro!(user);

Upvotes: 0

Views: 1587

Answers (1)

MikeTheSapien
MikeTheSapien

Reputation: 297

The solution I came up with made me conclude that I just don't understand what diesel::table macro produces and also don't understand Rust's macro_rules!().

I simply changed the metavariable from $module_path:path into $module_path:ident and the following code snippet now works

use diesel::table;

table! {
    user (id) {
        id -> Integer,
        uuid -> Text,
        username -> Text,
    }
}

#[derive(Queryable)]
struct User {
    id: i32,
    uuid: String,
    username: String
}

macro_rules! my_macro {
($module_path:ident) => {
        impl User {
            pub fn search_w_username(
                username: String,
                conn: &DB,
            ) -> Result<Option<Self>, diesel::result::Error> {
                use crate::diesel::ExpressionMethods;
                use crate::diesel::{QueryDsl, RunQueryDsl};

                match $module_path::table
                    .filter($module_path::dsl::username.eq(username))
                    .load::<GenericUserType>(conn)
                {
                    ...
                }
            }
        }
    };
}

my_macro!(user);

Now, $module_path::table and $module_path::dsl::username works for reasons I don't understand.

Thanks to the comment, it made me rethink the metavariable type of the macro signature.

Upvotes: 2

Related Questions