Repository: Brendonovich/prisma-client-rust (language: rust)

Issue:
Use proc macros for include/select
Because the generated `include!` and `select!` macros are output on a single line, errors that occur when using the macros cause sub-optimal error messages.

It looks "okay" in github because github doesn't wrap, but in a terminal it's a giant wall of the macro code :)

<details><summary>error snippet (scroll horizontally to see full error)</summary>

```
> cargo c
    Blocking waiting for file lock on build directory
   Compiling schema v0.1.0 (/Users/choc/code/prisma-rust-fun/schema)
    Checking prisma-rust-fun v0.1.0 (/Users/choc/code/prisma-rust-fun)
error[E0433]: failed to resolve: `crate` in paths can only be used in start position
  --> src/tick.rs:28:22
   |
28 |             .include(cultivator::include!({ mission }))
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `crate` in paths can only be used in start position
   |
   = note: this error originates in the macro `cultivator::include` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0412]: cannot find type `Data` in this scope
  --> src/tick.rs:28:22
   |
28 |             .include(cultivator::include!({ mission }))
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: this error originates in the macro `cultivator::include` (in Nightly builds, run with -Z macro-backtrace for more info)
help: you might have meant to use the associated type
  --> /Users/choc/code/prisma-rust-fun/schema/src/schema.rs:2752:1937
   |
275|     macro_rules ! _include_cultivator { ($ (($ ($ func_arg : ident : $ func_arg_ty : ty) , +) =>) ? $ module_name : ident { $ ($ field : ident $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? $ (: $ selection_mode : ident { $ ($ selections : tt) + }) ?) + }) => { # [allow (warnings)] pub mod $ module_name { $ crate :: crate :: schema :: cultivator :: include ! (@ definitions ; $ module_name ; $ ($ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) +) ; use super :: * ; pub struct Selection (Vec < :: prisma_client_rust :: Selection >) ; impl :: prisma_client_rust :: IncludeType for Selection { type Data = Data ; type ModelData = $ crate :: crate :: schema :: cultivator :: Data ; fn to_selections (self) -> Vec < :: prisma_client_rust :: Selection > { self . 0 } } pub fn include ($ ($ ($ func_arg : $ func_arg_ty) , +) ?) -> Selection { Selection ([$ crate :: crate :: schema :: cultivator :: include ! (@ selections_to_params ; : include { $ ($ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) + }) . into_iter () . map (| p | p . to_selection ()) . collect :: < Vec < _ >> () , < $ crate :: crate :: schema :: cultivator :: Types as :: prisma_client_rust :: ModelTypes > :: scalar_selections ()] . into_iter () . flatten () . collect :: < Vec < _ >> ()) } } } ; ({ $ ($ field : ident $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? $ (: $ selection_mode : ident { $ ($ selections : tt) + }) ?) + }) => { { $ crate :: crate :: schema :: cultivator :: include ! (@ definitions ; ; $ ($ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) +) ; pub struct Selection (Vec < :: prisma_client_rust :: Selection >) ; impl :: prisma_client_rust :: IncludeType for Selection { type Data = Self::Data ; type ModelData = $ crate :: crate :: schema :: cultivator :: Data ; fn to_selections (self) -> Vec < :: prisma_client_rust :: Selection > { self . 0 } } Selection ([$ crate :: crate :: schema :: cultivator :: include ! (@ selections_to_params ; : include { $ ($ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) + }) . into_iter () . map (| p | p . to_selection ()) . collect :: < Vec < _ >> () , < $ crate :: crate :: schema :: cultivator :: Types as :: prisma_client_rust :: ModelTypes > :: scalar_selections ()] . into_iter () . flatten () . collect :: < Vec < _ >> ()) } } ; (@ definitions ; $ ($ module_name : ident) ? ; $ ($ field : ident $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? $ (: $ selection_mode : ident { $ ($ selections : tt) + }) ?) +) => { # [allow (warnings)] enum Fields { sect , mission } # [allow (warnings)] impl Fields { fn selections () { $ (let _ = Fields :: $ field ;) + } } # [allow (warnings)] # [derive (std :: fmt :: Debug , Clone)] pub struct Data { pub id : String , pub created_at : :: prisma_client_rust :: chrono :: DateTime < :: prisma_client_rust :: chrono :: FixedOffset , > , pub updated_at : :: prisma_client_rust :: chrono :: DateTime < :: prisma_client_rust :: chrono :: FixedOffset , > , pub sect_id : String , pub mission_id : Option < String > , $ (pub $ field : $ crate :: crate :: schema :: cultivator :: include ! (@ field_type ; $ field $ (: $ selection_mode { $ ($ selections) + }) ?) ,) + } impl :: serde :: Serialize for Data { fn serialize < S > (& self , serializer : S) -> Result < S :: Ok , S :: Error > where S : :: serde :: Serializer , { use :: serde :: ser :: SerializeStruct ; let mut state = serializer . serialize_struct ("Data" , [$ (stringify ! ($ field) ,) + stringify ! (id) , stringify ! (created_at) , stringify ! (updated_at) , stringify ! (sect_id) , stringify ! (mission_id)] . len ()) ? ; $ (state . serialize_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; $ field) , & self . $ field) ? ;) * state . serialize_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; id) , & self . id) ? ; state . serialize_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; created_at) , & self . created_at) ? ; state . serialize_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; updated_at) , & self . updated_at) ? ; state . serialize_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; sect_id) , & self . sect_id) ? ; state . serialize_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; mission_id) , & self . mission_id) ? ; state . end () } } impl < 'de > :: serde :: Deserialize < 'de > for Data { fn deserialize < D > (deserializer : D) -> Result < Self , D :: Error > where D : :: serde :: Deserializer < 'de > , { # [allow (warnings)] enum Field { $ ($ field) , + , id , created_at , updated_at , sect_id , mission_id } impl < 'de > :: serde :: Deserialize < 'de > for Field { fn deserialize < D > (deserializer : D) -> Result < Field , D :: Error > where D : :: serde :: Deserializer < 'de > , { struct FieldVisitor ; impl < 'de > :: serde :: de :: Visitor < 'de > for FieldVisitor { type Value = Field ; fn expecting (& self , formatter : & mut :: std :: fmt :: Formatter) -> :: std :: fmt :: Result { formatter . write_str (concat ! ($ ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; $ field) , ", ") , + , $ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; id) , ", " , $ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; created_at) , ", " , $ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; updated_at) , ", " , $ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; sect_id) , ", " , $ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; mission_id) , ", ")) } fn visit_str < E > (self , value : & str) -> Result < Field , E > where E : :: serde :: de :: Error , { match value { $ ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; $ field) => Ok (Field :: $ field)) , * , $ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; id) => Ok (Field :: id) , $ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; created_at) => Ok (Field :: created_at) , $ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; updated_at) => Ok (Field :: updated_at) , $ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; sect_id) => Ok (Field :: sect_id) , $ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; mission_id) => Ok (Field :: mission_id) , _ => Err (:: serde :: de :: Error :: unknown_field (value , FIELDS)) , } } } deserializer . deserialize_identifier (FieldVisitor) } } struct DataVisitor ; impl < 'de > :: serde :: de :: Visitor < 'de > for DataVisitor { type Value = Data ; fn expecting (& self , formatter : & mut std :: fmt :: Formatter) -> std :: fmt :: Result { formatter . write_str ("struct Data") } fn visit_map < V > (self , mut map : V) -> Result < Data , V :: Error > where V : :: serde :: de :: MapAccess < 'de > , { $ (let mut $ field = None ;) * let mut id = None ; let mut created_at = None ; let mut updated_at = None ; let mut sect_id = None ; let mut mission_id = None ; while let Some (key) = map . next_key () ? { match key { Field :: id => { if id . is_some () { return Err (:: serde :: de :: Error :: duplicate_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; id))) ; } id = Some (map . next_value () ?) ; } Field :: created_at => { if created_at . is_some () { return Err (:: serde :: de :: Error :: duplicate_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; created_at))) ; } created_at = Some (map . next_value () ?) ; } Field :: updated_at => { if updated_at . is_some () { return Err (:: serde :: de :: Error :: duplicate_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; updated_at))) ; } updated_at = Some (map . next_value () ?) ; } Field :: sect_id => { if sect_id . is_some () { return Err (:: serde :: de :: Error :: duplicate_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; sect_id))) ; } sect_id = Some (map . next_value () ?) ; } Field :: mission_id => { if mission_id . is_some () { return Err (:: serde :: de :: Error :: duplicate_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; mission_id))) ; } mission_id = Some (map . next_value () ?) ; } $ (Field :: $ field => { if $ field . is_some () { return Err (:: serde :: de :: Error :: duplicate_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; $ field))) ; } $ field = Some (map . next_value () ?) ; }) * } } $ (let $ field = $ field . ok_or_else (|| serde :: de :: Error :: missing_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; $ field))) ? ;) * let id = id . ok_or_else (|| serde :: de :: Error :: missing_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; id))) ? ; let created_at = created_at . ok_or_else (|| serde :: de :: Error :: missing_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; created_at))) ? ; let updated_at = updated_at . ok_or_else (|| serde :: de :: Error :: missing_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; updated_at))) ? ; let sect_id = sect_id . ok_or_else (|| serde :: de :: Error :: missing_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; sect_id))) ? ; let mission_id = mission_id . ok_or_else (|| serde :: de :: Error :: missing_field ($ crate :: crate :: schema :: cultivator :: include ! (@ field_serde_name ; mission_id))) ? ; Ok (Data { id , created_at , updated_at , sect_id , mission_id , $ ($ field) , * }) } } const FIELDS : & 'static [& 'static str] = & ["id" , "createdAt" , "updatedAt" , "sect" , "sectId" , "mission" , "missionId"] ; deserializer . deserialize_struct ("Data" , FIELDS , DataVisitor) } } $ ($ (pub mod $ field { $ crate :: crate :: schema :: cultivator :: $ selection_mode ! (@ field_module ; $ field : $ selection_mode { $ ($ selections) + }) ; }) ?) + } ; (@ field_type ; sect : $ selection_mode : ident { $ ($ selections : tt) + }) => { sect :: Data } ; (@ field_type ; sect) => { crate :: crate :: schema :: sect :: Data } ; (@ field_type ; mission : $ selection_mode : ident { $ ($ selections : tt) + }) => { Option < mission :: Data > } ; (@ field_type ; mission) => { Option < crate :: crate :: schema :: mission :: Data > } ; (@ field_type ; $ field : ident $ ($ tokens : tt) *) => { compile_error ! (stringify ! (Cannot include nonexistent relation $ field on model "Cultivator" , available relations are "sect, mission")) } ; (@ field_module ; sect : $ selection_mode : ident { $ ($ selections : tt) + }) => { $ crate :: crate :: schema :: sect :: include ! (@ definitions ; ; $ ($ selections) +) ; } ; (@ field_module ; mission : $ selection_mode : ident { $ ($ selections : tt) + }) => { $ crate :: crate :: schema :: mission :: include ! (@ definitions ; ; $ ($ selections) +) ; } ; (@ field_module ; $ ($ tokens : tt) *) => { } ; (@ selection_field_to_selection_param ; sect $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? : $ selection_mode : ident { $ ($ selections : tt) + }) => { { Into :: < $ crate :: crate :: schema :: cultivator :: IncludeParam > :: into ($ crate :: crate :: schema :: cultivator :: sect :: Include :: $ selection_mode ($ crate :: crate :: schema :: sect :: select ! (@ selections_to_params ; : $ selection_mode { $ ($ selections) + }) . into_iter () . collect ())) } } ; (@ selection_field_to_selection_param ; sect $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ?) => { { Into :: < $ crate :: crate :: schema :: cultivator :: IncludeParam > :: into ($ crate :: crate :: schema :: cultivator :: sect :: Include :: Fetch) } } ; (@ selection_field_to_selection_param ; mission $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? : $ selection_mode : ident { $ ($ selections : tt) + }) => { { Into :: < $ crate :: crate :: schema :: cultivator :: IncludeParam > :: into ($ crate :: crate :: schema :: cultivator :: mission :: Include :: $ selection_mode ($ crate :: crate :: schema :: mission :: select ! (@ selections_to_params ; : $ selection_mode { $ ($ selections) + }) . into_iter () . collect ())) } } ; (@ selection_field_to_selection_param ; mission $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ?) => { { Into :: < $ crate :: crate :: schema :: cultivator :: IncludeParam > :: into ($ crate :: crate :: schema :: cultivator :: mission :: Include :: Fetch) } } ; (@ selection_field_to_selection_param ; $ ($ tokens : tt) *) => { compile_error ! (stringify ! ($ ($ tokens) *)) } ; (@ selections_to_params ; : $ macro_name : ident { $ ($ field : ident $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? $ (: $ selection_mode : ident { $ ($ selections : tt) + }) ?) + }) => { [$ ($ crate :: crate :: schema :: cultivator :: $ macro_name ! (@ selection_field_to_selection_param ; $ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) ,) +] } ; (@ filters_to_args ;) => { vec ! [] } ; (@ filters_to_args ; $ ($ t : tt) *) => { $ ($ t) * } ; (@ field_serde_name ; id) => { "id" } ; (@ field_serde_name ; created_at) => { "createdAt" } ; (@ field_serde_name ; updated_at) => { "updatedAt" } ; (@ field_serde_name ; sect) => { "sect" } ; (@ field_serde_name ; sect_id) => { "sectId" } ; (@ field_serde_name ; mission) => { "mission" } ; (@ field_serde_name ; mission_id) => { "missionId" } ; }
   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 ~~~~~~~~~~
help: consider importing one of these items
   |
1  | use async_graphql::Data;
   |
1  | use crate::tick::cultivator::Data;
   |
1  | use crate::tick::mission::Data;
   |
1  | use crate::tick::user::Data;
   |
     and 6 other candidates

warning: unused import: `mission`
 --> src/tick.rs:4:26
  |
4 | use schema::{cultivator, mission, user, PrismaClient};
  |                          ^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

Some errors have detailed explanations: E0412, E0433.
For more information about an error, try `rustc --explain E0412`.
warning: `prisma-rust-fun` (bin "prisma-rust-fun") generated 1 warning
error: could not compile `prisma-rust-fun` due to 5 previous errors; 1 warning emitted
```
</details>

Unfortunately I can't use rustfmt to format it because it [gives up on lines over a certain length](https://github.com/rust-lang/rustfmt/issues/3863).



Discussion / hints:
With the current implementation of `include` and `select`, this is a tricky issue to address. Splitting over multiple lines isn't possible since `quote!` doesn't include whitespace, and I'm not sure rustfmt would even work since it's not always great at formatting macro code.

In your example, I don't think there's a good way to alleviate the terminal spam since it's a result of an invalid configuration ( which I addressed in Discord). But for errors that are syntactic, there's definitely some extra error handling I could add.
Take this for example, where `non_existent` isn't actually a field on `FilePath`.

```
file_path::select!({ non_existent })
```

The correct error is produced.

```
error: Cannot include nonexistent relation non_existent on model "FilePath",
       available relations are
       "id, is_dir, cas_id, ..."
```

But the same terminal spam is produced from some other branch of the macro.

```
error: no rules expected the token `non_existent`
     --> core/src/prisma.rs:14116:3432
      |
14116 |     macro_rules ! _select_file_path ...
```

This could definitely be worked on.
ok maybe proc macros are a really good idea

<img width="503" alt="image" src="https://user-images.githubusercontent.com/14191578/216253748-9ebb908d-e835-441e-82ad-825824d156d8.png">

damn even rustfmt can work

<img width="644" alt="image" src="https://user-images.githubusercontent.com/14191578/216285911-a512d7c5-dd8a-4f19-98b1-185712ce89a8.png">


😍 nice!

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index e3593650..9597e3fe 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4759,7 +4759,7 @@ dependencies = [
  "include_dir",
  "indexmap",
  "paste",
- "prisma-client-rust-macros",
+ "prisma-client-rust-generator-macros",
  "prisma-models",
  "psl",
  "query-core",
@@ -4804,6 +4804,7 @@ dependencies = [
  "directories",
  "flate2",
  "http",
+ "prisma-client-rust-generator-shared",
  "prisma-client-rust-sdk",
  "proc-macro2",
  "quote",
@@ -4817,16 +4818,28 @@ dependencies = [
 ]
 
 [[package]]
-name = "prisma-client-rust-macros"
+name = "prisma-client-rust-generator-macros"
 version = "0.6.8"
 dependencies = [
  "convert_case 0.6.0",
+ "prisma-client-rust-generator-shared",
  "proc-macro2",
  "quote",
  "specta",
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "prisma-client-rust-generator-shared"
+version = "0.1.0"
+dependencies = [
+ "convert_case 0.6.0",
+ "prisma-client-rust-sdk",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "prisma-client-rust-sdk"
 version = "0.6.8"
diff --git a/crates/macros/Cargo.toml b/crates/generator-macros/Cargo.toml
similarity index 62%
rename from crates/macros/Cargo.toml
rename to crates/generator-macros/Cargo.toml
index aaaff9dd..fcd87f4d 100644
--- a/crates/macros/Cargo.toml
+++ b/crates/generator-macros/Cargo.toml
@@ -1,14 +1,13 @@
 [package]
-name = "prisma-client-rust-macros"
+name = "prisma-client-rust-generator-macros"
 version = "0.6.8"
 edition = "2021"
 
 [lib]
 proc-macro = true
 
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
 [dependencies]
+prisma-client-rust-generator-shared = { path = "../generator-shared" }
 convert_case.workspace = true
 proc-macro2 = "1.0.39"
 quote = "1.0.18"
diff --git a/crates/macros/src/filter.rs b/crates/generator-macros/src/filter.rs
similarity index 62%
rename from crates/macros/src/filter.rs
rename to crates/generator-macros/src/filter.rs
index d6a8cb7a..a4d922c7 100644
--- a/crates/macros/src/filter.rs
+++ b/crates/generator-macros/src/filter.rs
@@ -1,59 +1,14 @@
+use prisma_client_rust_generator_shared::{select_include::SelectableFields, Arity};
 use proc_macro2::TokenStream;
 use quote::{quote, quote_spanned};
 use syn::{
-    braced, bracketed, parenthesized,
+    braced,
     parse::{Parse, ParseStream},
     parse_macro_input,
     punctuated::Punctuated,
     Ident, Path, Token,
 };
 
-enum Arity {
-    Scalar,
-    Relation(Path),
-}
-
-impl Parse for Arity {
-    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
-        let ident = input.parse::<Ident>()?;
-
-        Ok(match ident.to_string().as_str() {
-            "Scalar" => Self::Scalar,
-            "Relation" => Self::Relation({
-                let content;
-                parenthesized!(content in input);
-                content.parse()?
-            }),
-            _ => {
-                return Err(syn::Error::new_spanned(
-                    ident,
-                    "expected `Scalar` or `Relation`",
-                ))
-            }
-        })
-    }
-}
-
-struct FieldTuple {
-    name: Ident,
-    arity: Arity,
-}
-
-impl Parse for FieldTuple {
-    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
-        let content;
-        parenthesized!(content in input);
-
-        Ok(Self {
-            name: content.parse()?,
-            arity: {
-                content.parse::<Token![,]>()?;
-                content.parse()?
-            },
-        })
-    }
-}
-
 struct Method {
     name: Ident,
     value: TokenStream,
@@ -77,7 +32,7 @@ struct Filter {
 }
 
 impl Parse for Filter {
-    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
         Ok(Self {
             field: input.parse()?,
             methods: {
@@ -95,12 +50,12 @@ impl Parse for Filter {
 struct Input {
     dollar_crate: Ident,
     module_path: Path,
-    fields: Punctuated<FieldTuple, Token![,]>,
+    fields: SelectableFields,
     filter: Punctuated<Filter, Token![,]>,
 }
 
 impl Parse for Input {
-    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
         Ok(Self {
             dollar_crate: input.parse()?,
             module_path: {
@@ -109,10 +64,7 @@ impl Parse for Input {
             },
             fields: {
                 input.parse::<Token![,]>()?;
-
-                let content;
-                bracketed!(content in input);
-                Punctuated::parse_terminated(&content)?
+                input.parse()?
             },
             filter: {
                 input.parse::<Token![,]>()?;
@@ -150,14 +102,20 @@ pub fn proc_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
             match &field.arity {
                 Arity::Scalar => {
                     let methods = methods.into_iter().map(
-                        |Method { name, value }| quote!(#dollar_crate::#model_path::#field_name::#name(#value)),
+                        |Method { name, value }| quote! {
+                        	#dollar_crate::#model_path::#field_name::#name(#value)
+                        },
                     );
 
                     quote!(#(#methods),*)
                 }
-                Arity::Relation(related_model_path) => {
+                Arity::Relation(related_model_path, _) => {
                     let methods = methods.into_iter().map(
-						|Method { name, value }| quote!(#dollar_crate::#model_path::#field_name::#name(#dollar_crate::#related_model_path::filter! #value)),
+						|Method { name, value }| quote! {
+							#dollar_crate::#model_path::#field_name::#name(
+								#dollar_crate::#related_model_path::filter! #value
+							)
+						},
 					);
 
                     quote!(#(#methods),*)
@@ -166,19 +124,13 @@ pub fn proc_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
         })
         .collect::<Vec<_>>();
 
-    quote! {
-        vec![
-            #(#items),*
-        ]
-    }
-    .into()
+    quote!(vec![#(#items),*]).into()
 }
 
 // factory means rustfmt can work!
 pub fn proc_macro_factory(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
     struct FactoryInput {
         name: Ident,
-        model_path: Path,
         rest: TokenStream,
     }
 
@@ -186,10 +138,6 @@ pub fn proc_macro_factory(input: proc_macro::TokenStream) -> proc_macro::TokenSt
         fn parse(input: ParseStream) -> syn::Result<Self> {
             Ok(Self {
                 name: input.parse()?,
-                model_path: {
-                    input.parse::<Token![,]>()?;
-                    input.parse()?
-                },
                 rest: {
                     input.parse::<Token![,]>()?;
                     input.parse()?
@@ -198,11 +146,7 @@ pub fn proc_macro_factory(input: proc_macro::TokenStream) -> proc_macro::TokenSt
         }
     }
 
-    let FactoryInput {
-        name,
-        model_path,
-        rest,
-    } = parse_macro_input!(input as FactoryInput);
+    let FactoryInput { name, rest } = parse_macro_input!(input as FactoryInput);
 
     quote! {
         #[macro_export]
@@ -210,7 +154,6 @@ pub fn proc_macro_factory(input: proc_macro::TokenStream) -> proc_macro::TokenSt
             ($($inner:tt)+) => {
                 ::prisma_client_rust::macros::filter!(
                     $crate,
-                    #model_path,
                     #rest,
                     { $($inner)+ }
                 )
diff --git a/crates/generator-macros/src/lib.rs b/crates/generator-macros/src/lib.rs
new file mode 100644
index 00000000..26c9ea62
--- /dev/null
+++ b/crates/generator-macros/src/lib.rs
@@ -0,0 +1,50 @@
+mod filter;
+mod partial_unchecked;
+mod select_include;
+
+#[proc_macro]
+pub fn to_pascal_case(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    let converted = convert_case::Casing::to_case(&input.to_string(), convert_case::Case::Pascal);
+
+    proc_macro::TokenTree::Literal(proc_macro::Literal::string(&converted)).into()
+}
+
+#[proc_macro]
+pub fn partial_unchecked(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    partial_unchecked::proc_macro(input)
+}
+
+#[proc_macro]
+pub fn partial_unchecked_factory(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    partial_unchecked::proc_macro_factory(input)
+}
+
+#[proc_macro]
+pub fn filter(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    filter::proc_macro(input)
+}
+
+#[proc_macro]
+pub fn filter_factory(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    filter::proc_macro_factory(input)
+}
+
+#[proc_macro]
+pub fn select(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    select_include::proc_macro(input, select_include::Variant::Select)
+}
+
+#[proc_macro]
+pub fn select_factory(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    select_include::proc_macro_factory(input, select_include::Variant::Select)
+}
+
+#[proc_macro]
+pub fn include(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    select_include::proc_macro(input, select_include::Variant::Include)
+}
+
+#[proc_macro]
+pub fn include_factory(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    select_include::proc_macro_factory(input, select_include::Variant::Include)
+}
diff --git a/crates/generator-macros/src/partial_unchecked.rs b/crates/generator-macros/src/partial_unchecked.rs
new file mode 100644
index 00000000..3f7e20c4
--- /dev/null
+++ b/crates/generator-macros/src/partial_unchecked.rs
@@ -0,0 +1,138 @@
+use proc_macro2::{Ident, TokenStream};
+use quote::quote;
+use syn::{
+    bracketed,
+    parse::{Parse, ParseStream},
+    parse_macro_input,
+    punctuated::Punctuated,
+    ItemStruct, Path, Token,
+};
+
+struct Input {
+    dollar_crate: Ident,
+    model_module: Path,
+    data_struct: ItemStruct,
+    struct_name: Ident,
+    selection: Punctuated<Ident, Token![,]>,
+}
+
+impl Parse for Input {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        Ok(Self {
+            dollar_crate: input.parse()?,
+            model_module: {
+                input.parse::<Token![,]>()?;
+                input.parse()?
+            },
+            data_struct: {
+                input.parse::<Token![,]>()?;
+                input.parse()?
+            },
+            struct_name: {
+                input.parse::<Token![,]>()?;
+                input.parse()?
+            },
+            selection: {
+                input.parse::<Token![,]>()?;
+
+                let content;
+                bracketed!(content in input);
+                Punctuated::<Ident, Token![,]>::parse_terminated(&content)?
+            },
+        })
+    }
+}
+
+pub fn proc_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    let Input {
+        dollar_crate,
+        model_module,
+        data_struct,
+        struct_name,
+        selection,
+    } = parse_macro_input!(input as Input);
+
+    let fields = data_struct
+        .fields
+        .iter()
+        .filter(|f| selection.iter().any(|s| s == f.ident.as_ref().unwrap()))
+        .map(|field| {
+            let attrs = &field.attrs;
+            let ident = &field.ident;
+            let ty = &field.ty;
+
+            let specta_attrs = cfg!(feature = "specta").then(|| quote!(#[specta(optional)]));
+
+            quote! {
+                #(#attrs)*
+                #specta_attrs
+                pub #ident: Option<#dollar_crate::#ty>
+            }
+        });
+
+    let specta_attrs = cfg!(feature = "specta").then(|| {
+        quote! {
+            #[derive(::prisma_client_rust::specta::Type)]
+            #[specta(crate = "prisma_client_rust::specta")]
+        }
+    });
+
+    let selection = selection.iter().collect::<Vec<_>>();
+
+    quote! {
+        #[derive(serde::Deserialize)]
+        #specta_attrs
+        #[allow(unused)]
+        pub struct #struct_name {
+           #(#fields),*
+        }
+
+        impl #struct_name {
+            pub fn to_params(self) -> Vec<#dollar_crate::#model_module::UncheckedSetParam> {
+                [
+                    #(self.#selection.map(#dollar_crate::#model_module::#selection::set)),*
+                ].into_iter().flatten().collect()
+            }
+        }
+    }
+    .into()
+}
+
+pub fn proc_macro_factory(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    struct FactoryInput {
+        name: Ident,
+        rest: TokenStream,
+    }
+
+    impl Parse for FactoryInput {
+        fn parse(input: ParseStream) -> syn::Result<Self> {
+            Ok(Self {
+                name: input.parse()?,
+                rest: {
+                    input.parse::<Token![,]>()?;
+                    input.parse()?
+                },
+            })
+        }
+    }
+
+    let FactoryInput { name, rest } = parse_macro_input!(input as FactoryInput);
+
+    quote! {
+        #[macro_export]
+        macro_rules! #name {
+            ($struct_name:ident {
+                $($scalar_field:ident)+
+            }) => {
+                ::prisma_client_rust::macros::partial_unchecked!(
+                    $crate,
+                    #rest,
+                    $struct_name,
+                    [$($scalar_field),+]
+                );
+            }
+        }
+        pub use #name as partial_unchecked;
+    }
+    .into()
+}
diff --git a/crates/generator-macros/src/select_include/definitions.rs b/crates/generator-macros/src/select_include/definitions.rs
new file mode 100644
index 00000000..65421d83
--- /dev/null
+++ b/crates/generator-macros/src/select_include/definitions.rs
@@ -0,0 +1,97 @@
+use convert_case::Casing;
+use prisma_client_rust_generator_shared::Arity;
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::Field;
+
+use super::Input;
+
+pub fn definitions(input: &Input) -> TokenStream {
+    let Input {
+        dollar,
+        model_path,
+        schema_struct,
+        selectable_fields,
+        macro_rules,
+        ..
+    } = input;
+
+    let mut attrs = quote! {
+        #[allow(warnings)]
+        #[derive(Debug, Clone, ::serde::Serialize, ::serde::Deserialize)]
+    };
+
+    if cfg!(feature = "specta") {
+        attrs.extend(quote! {
+            #[derive(::prisma_client_rust::specta::Type)]
+            #[specta(crate = "prisma_client_rust::specta")]
+        });
+
+        attrs.extend(match &macro_rules.name {
+            None => quote!(#[specta(inline)]),
+            Some(name) => {
+                let name_pascal = name.to_string().to_case(convert_case::Case::Pascal);
+
+                quote! {
+                    #[specta(rename = #name_pascal)]
+                }
+            }
+        });
+    }
+
+    let (fields, field_modules): (Vec<_>, Vec<_>) = schema_struct
+        .fields
+        .iter()
+        .filter_map(|field| {
+            let Field {
+                attrs, ty, ident, ..
+            } = &field;
+
+            let field_in_selectables = selectable_fields
+                .iter()
+                .find(|item| Some(&item.name) == ident.as_ref());
+            let field_in_selection = macro_rules
+                .selection
+                .iter()
+                .find(|item| Some(&item.name) == ident.as_ref());
+
+            if field_in_selectables.is_some() && field_in_selection.is_none() {
+                return None;
+            }
+
+            let field = quote! {
+                #(#attrs)*
+                pub #ident: #dollar::#model_path::#ty
+            };
+
+            let field_module = field_in_selectables
+                .zip(field_in_selection.and_then(|f| f.sub_selection.as_ref()))
+                .and_then(|(field_in_selectables, (variant, sub_selection))| {
+                    let Arity::Relation(relation_model_path, _) = &field_in_selectables.arity else {
+						return None;
+					};
+
+                    let value = quote! {
+                        pub mod #ident {
+                            #dollar::#relation_model_path::#variant! {
+                                definitions @ #sub_selection
+                            }
+                        }
+                    };
+
+                    Some(value)
+                });
+
+            Some((field, field_module))
+        })
+        .unzip();
+
+    quote! {
+        #attrs
+        pub struct Data {
+            #(#fields),*
+        }
+
+        #(#field_modules)*
+    }
+}
diff --git a/crates/generator-macros/src/select_include/mod.rs b/crates/generator-macros/src/select_include/mod.rs
new file mode 100644
index 00000000..49eea157
--- /dev/null
+++ b/crates/generator-macros/src/select_include/mod.rs
@@ -0,0 +1,244 @@
+mod definitions;
+mod selection;
+
+use prisma_client_rust_generator_shared::select_include::SelectableFields;
+use proc_macro2::TokenStream;
+use quote::*;
+use syn::{
+    parse::{Parse, ParseStream},
+    punctuated::Punctuated,
+    token::Paren,
+    *,
+};
+
+use definitions::*;
+use selection::*;
+
+pub use prisma_client_rust_generator_shared::{
+    select_include::Variant, Arity, FieldTuple, RelationArity,
+};
+
+#[derive(Debug)]
+enum SectionOrName {
+    Section(Section),
+    Name(Ident),
+}
+
+impl Parse for SectionOrName {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        if input.peek2(Token![@]) {
+            let value = input.parse()?;
+            input.parse::<Token![@]>()?;
+
+            Ok(Self::Section(value))
+        } else if input.peek(Ident) {
+            Ok(Self::Name(input.parse()?))
+        } else {
+            Err(input.error("expected section or name"))
+        }
+    }
+}
+
+#[derive(Debug)]
+struct MacroRulesInput {
+    section: Option<Section>,
+    // TODO: merge with name to have more accurate types
+    fn_args: Option<Vec<FnArg>>,
+    name: Option<Ident>,
+    pub selection: Selection,
+}
+
+impl Parse for MacroRulesInput {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        Ok(Self {
+            section: {
+                if input.peek(Ident) && input.peek2(Token![@]) {
+                    let value = input.parse()?;
+                    input.parse::<Token![@]>()?;
+                    Some(value)
+                } else {
+                    None
+                }
+            },
+            fn_args: {
+                if input.peek(Paren) {
+                    let content;
+                    parenthesized!(content in input);
+
+                    let args = Punctuated::<FnArg, Token![,]>::parse_terminated(&content)?
+                        .into_iter()
+                        .collect();
+
+                    input.parse::<Token![=>]>()?;
+
+                    Some(args)
+                } else {
+                    None
+                }
+            },
+            name: input.parse()?,
+            selection: input.parse()?,
+        })
+    }
+}
+
+#[derive(Debug)]
+enum Section {
+    Definitions,
+    SelectionToParams,
+}
+
+impl Parse for Section {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let value = input.parse::<Ident>()?;
+
+        match value.to_string().as_str() {
+            "definitions" => Ok(Self::Definitions),
+            "selection_to_params" => Ok(Self::SelectionToParams),
+            _ => Err(input.error("expected 'definitions' or 'selection_to_params'")),
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct Input {
+    dollar: Ident,
+    model_path: Path,
+    schema_struct: ItemStruct,
+    selectable_fields: SelectableFields,
+    macro_rules: MacroRulesInput,
+}
+
+impl Parse for Input {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        Ok(Self {
+            dollar: input.parse()?,
+            model_path: {
+                input.parse::<Token![,]>()?;
+                input.parse()?
+            },
+            schema_struct: {
+                input.parse::<Token![,]>()?;
+                input.parse()?
+            },
+            selectable_fields: {
+                input.parse::<Token![,]>()?;
+
+                input.parse()?
+            },
+            macro_rules: {
+                input.parse::<Token![,]>()?;
+
+                let contents;
+                braced!(contents in input);
+                contents.parse()?
+            },
+        })
+    }
+}
+
+pub fn proc_macro(input: proc_macro::TokenStream, variant: Variant) -> proc_macro::TokenStream {
+    let input = parse_macro_input!(input as Input);
+
+    let Input { macro_rules, .. } = &input;
+
+    match &macro_rules.section {
+        Some(Section::Definitions) => definitions(&input),
+        Some(Section::SelectionToParams) => {
+            let params = selection_to_params(&input, variant);
+            quote!([#(#params),*])
+        }
+        None => {
+            let definitions = definitions(&input);
+            let selection_struct = selection_struct(&input, variant);
+            let selection = selection(&input, variant);
+
+            let variant_ident = format_ident!("{variant}");
+
+            match &macro_rules.name {
+                Some(module_name) => {
+                    let fn_args = macro_rules
+                        .fn_args
+                        .as_ref()
+                        .map(|fn_args| {
+                            let iter = fn_args.iter();
+                            quote!(#(#iter),*)
+                        })
+                        .unwrap_or_default();
+
+                    quote! {
+                        #[allow(warnings)]
+                        pub mod #module_name {
+                            use super::*;
+
+                            #definitions
+                            #selection_struct
+
+                            pub fn #variant_ident(#fn_args) -> Selection {
+                                #selection
+                            }
+                        }
+                    }
+                }
+                None => {
+                    quote! {{
+                        #definitions
+                        #selection_struct
+                        #selection
+                    }}
+                }
+            }
+        }
+    }
+    .into()
+}
+
+struct FactoryInput {
+    internal_name: Ident,
+    public_name: Ident,
+    // model_path & fields
+    rest: TokenStream,
+}
+
+impl Parse for FactoryInput {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        Ok(Self {
+            internal_name: input.parse()?,
+            public_name: {
+                input.parse::<Token![,]>()?;
+                input.parse()?
+            },
+            rest: {
+                input.parse::<Token![,]>()?;
+                input.parse()?
+            },
+        })
+    }
+}
+
+// factory means rustfmt can work!
+pub fn proc_macro_factory(
+    input: proc_macro::TokenStream,
+    variant: Variant,
+) -> proc_macro::TokenStream {
+    let FactoryInput {
+        internal_name,
+        public_name,
+        rest,
+    } = parse_macro_input!(input as FactoryInput);
+
+    quote! {
+        #[macro_export]
+        macro_rules! #internal_name {
+            ($($input:tt)+) => {
+                ::prisma_client_rust::macros::#variant! {
+                    $crate,
+                    #rest,
+                    { $($input)+ }
+                }
+            };
+        }
+        pub use #internal_name as #public_name;
+    }
+    .into()
+}
diff --git a/crates/generator-macros/src/select_include/selection/arg.rs b/crates/generator-macros/src/select_include/selection/arg.rs
new file mode 100644
index 00000000..5098e7de
--- /dev/null
+++ b/crates/generator-macros/src/select_include/selection/arg.rs
@@ -0,0 +1,35 @@
+use proc_macro2::TokenStream;
+use quote::*;
+use syn::{
+    parse::{Parse, ParseStream},
+    *,
+};
+
+#[derive(Debug)]
+pub struct SelectionArg {
+    name: Ident,
+    values: TokenStream,
+}
+
+impl Parse for SelectionArg {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        input.parse::<Token![.]>()?;
+
+        Ok(Self {
+            name: input.parse()?,
+            values: {
+                let content;
+                parenthesized!(content in input);
+
+                content.parse()?
+            },
+        })
+    }
+}
+
+impl ToTokens for SelectionArg {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        let Self { name, values } = self;
+        tokens.extend(quote!(.#name(#values)))
+    }
+}
diff --git a/crates/generator-macros/src/select_include/selection/filters.rs b/crates/generator-macros/src/select_include/selection/filters.rs
new file mode 100644
index 00000000..c1fb7ebc
--- /dev/null
+++ b/crates/generator-macros/src/select_include/selection/filters.rs
@@ -0,0 +1,25 @@
+use proc_macro2::TokenStream;
+use quote::*;
+use syn::{
+    parenthesized,
+    parse::{Parse, ParseStream},
+};
+
+#[derive(Debug)]
+pub struct SelectionFilters(TokenStream);
+
+impl Parse for SelectionFilters {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let content;
+        parenthesized!(content in input);
+
+        Ok(Self(content.parse()?))
+    }
+}
+
+impl ToTokens for SelectionFilters {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        let Self(inner) = self;
+        tokens.extend(quote!((#inner)))
+    }
+}
diff --git a/crates/generator-macros/src/select_include/selection/item.rs b/crates/generator-macros/src/select_include/selection/item.rs
new file mode 100644
index 00000000..e79c8696
--- /dev/null
+++ b/crates/generator-macros/src/select_include/selection/item.rs
@@ -0,0 +1,60 @@
+use prisma_client_rust_generator_shared::select_include::Variant;
+use proc_macro2::TokenStream;
+use quote::*;
+use syn::{
+    parse::{Parse, ParseStream},
+    token::Paren,
+    *,
+};
+
+use super::*;
+
+#[derive(Debug)]
+pub struct SelectionItem {
+    pub name: Ident,
+    pub filters: Option<SelectionFilters>,
+    pub args: Vec<SelectionArg>,
+    // We don't parse here as we don't care about subselection.
+    // That gets passed on to another macro invoction.
+    pub sub_selection: Option<(Variant, TokenStream)>,
+}
+
+impl Parse for SelectionItem {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        Ok(Self {
+            name: input.parse()?,
+            filters: {
+                if input.peek(Paren) {
+                    Some(input.parse()?)
+                } else {
+                    None
+                }
+            },
+            args: {
+                let mut ret = vec![];
+
+                while input.peek(Token![.]) {
+                    ret.push(input.parse()?);
+                }
+
+                ret
+            },
+            sub_selection: {
+                if input.peek(Token![:]) {
+                    input.parse::<Token![:]>()?;
+
+                    let variant = input.parse()?;
+
+                    let content;
+                    braced!(content in input);
+                    // parse separately to re-wrap in braces for Selection::parse
+                    let content = content.parse::<TokenStream>()?;
+
+                    Some((variant, quote!({ #content })))
+                } else {
+                    None
+                }
+            },
+        })
+    }
+}
diff --git a/crates/generator-macros/src/select_include/selection/mod.rs b/crates/generator-macros/src/select_include/selection/mod.rs
new file mode 100644
index 00000000..1e671c40
--- /dev/null
+++ b/crates/generator-macros/src/select_include/selection/mod.rs
@@ -0,0 +1,178 @@
+mod arg;
+mod filters;
+mod item;
+
+use convert_case::*;
+use prisma_client_rust_generator_shared::{select_include::Variant, Arity, RelationArity};
+use proc_macro2::TokenStream;
+use quote::*;
+use syn::{
+    braced,
+    parse::{Parse, ParseStream},
+};
+
+pub use arg::*;
+pub use filters::*;
+pub use item::*;
+
+use super::Input;
+
+pub fn selection(input: &Input, variant: Variant) -> TokenStream {
+    let Input {
+        dollar, model_path, ..
+    } = input;
+
+    let scalar_selections = matches!(variant, Variant::Include).then(|| {
+		quote! {
+			<#dollar::#model_path::Types as ::prisma_client_rust::ModelTypes>::scalar_selections()
+		}
+	}).unwrap_or_else(|| quote!(Vec::<::prisma_client_rust::Selection>::new()));
+
+    let selected_selections = selection_to_params(input, variant);
+
+    quote! {
+        Selection(
+            #scalar_selections
+                .into_iter()
+                .chain([#(#selected_selections),*].into_iter().map(Into::into))
+                .collect()
+        )
+    }
+}
+
+pub fn selection_to_params(input: &Input, variant: Variant) -> Vec<TokenStream> {
+    let Input {
+        dollar,
+        model_path,
+        macro_rules,
+        selectable_fields,
+        ..
+    } = input;
+
+    let variant_param = variant.param();
+    let variant_pascal = format_ident!("{}", variant.to_string().to_case(Case::Pascal));
+
+    macro_rules
+        .selection
+        .iter()
+        .map(
+            |SelectionItem {
+                 name,
+                 filters,
+                 args,
+                 sub_selection,
+             }| {
+                let Some(selectable_item) = selectable_fields
+					.iter()
+					.find(|field| &field.name == name) else {
+						return quote_spanned!(name.span() => compile_error!("Field not found in selectable fields"))
+					};
+
+                let into = quote!(Into::<#dollar::#model_path::#variant_param>::into);
+                let variant_type_path = quote!(#dollar::#model_path::#name::#variant_pascal);
+
+                let filters = filters
+                    .as_ref()
+                    .map(|s| quote!(#s))
+                    .unwrap_or_else(|| quote!(vec![]));
+
+                match &selectable_item.arity {
+                    Arity::Scalar => quote! {
+                        #into(#variant_type_path)
+                    },
+                    Arity::Relation(relation_model_path, relation_arity) => {
+                        match (relation_arity, sub_selection) {
+                            (RelationArity::One, None) => quote! {
+                                #into(#variant_type_path::Fetch)
+                            },
+                            (RelationArity::One, Some((selection_variant, selection))) => quote! {
+                                #into(
+                                    #variant_type_path::#selection_variant(
+                                        #dollar::#relation_model_path::#selection_variant! {
+                                            selection_to_params @ #selection
+                                        }.into_iter().collect()
+                                    )
+                                )
+                            },
+                            (RelationArity::Many, None) => quote! {
+                                #into(
+                                    #variant_type_path::Fetch(
+                                        #dollar::#relation_model_path::ManyArgs::new(
+                                            #filters
+                                        ) #(#args)*
+                                    )
+                                )
+                            },
+                            (RelationArity::Many, Some((selection_variant, selection))) => quote! {
+                                #into(
+                                    #variant_type_path::#selection_variant(
+                                        #dollar::#relation_model_path::ManyArgs::new(
+                                            #filters
+                                        ) #(#args)*,
+                                        #dollar::#relation_model_path::#selection_variant! {
+                                            selection_to_params @ #selection
+                                        }.into_iter().collect()
+                                    )
+                                )
+                            },
+                        }
+                    }
+                }
+            },
+        )
+        .collect()
+}
+
+pub fn selection_struct(input: &Input, variant: Variant) -> TokenStream {
+    let Input {
+        dollar, model_path, ..
+    } = input;
+
+    let selection_type = variant.type_trait();
+
+    quote! {
+        pub struct Selection(Vec<::prisma_client_rust::Selection>);
+
+        impl ::prisma_client_rust::#selection_type for Selection {
+            type Data = Data;
+            type ModelData = #dollar::#model_path::Data;
+
+            fn to_selections(self) -> Vec<::prisma_client_rust::Selection> {
+                self.0
+            }
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct Selection(Vec<SelectionItem>);
+
+impl Selection {
+    pub fn iter(&self) -> impl Iterator<Item = &SelectionItem> {
+        self.0.iter()
+    }
+}
+
+impl IntoIterator for Selection {
+    type Item = SelectionItem;
+    type IntoIter = std::vec::IntoIter<Self::Item>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.0.into_iter()
+    }
+}
+
+impl Parse for Selection {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let contents;
+        braced!(contents in input);
+
+        let mut ret = vec![];
+
+        while !contents.is_empty() {
+            ret.push(contents.parse()?);
+        }
+
+        Ok(Self(ret))
+    }
+}
diff --git a/crates/generator-shared/Cargo.toml b/crates/generator-shared/Cargo.toml
new file mode 100644
index 00000000..aee6168a
--- /dev/null
+++ b/crates/generator-shared/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "prisma-client-rust-generator-shared"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+convert_case.workspace = true
+proc-macro2 = "1.0.39"
+quote = "1.0.18"
+syn = { version = "1.0.91", features = ["extra-traits"] }
+
+prisma-client-rust-sdk = { path = "../sdk" }
diff --git a/crates/generator-shared/src/lib.rs b/crates/generator-shared/src/lib.rs
new file mode 100644
index 00000000..d8a527da
--- /dev/null
+++ b/crates/generator-shared/src/lib.rs
@@ -0,0 +1,140 @@
+use prisma_client_rust_sdk::{
+    prelude::*,
+    prisma::prisma_models::{
+        walkers::{FieldWalker, RefinedFieldWalker},
+        FieldArity,
+    },
+};
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use syn::{
+    parenthesized,
+    parse::{Parse, ParseStream},
+    parse_quote, Ident, Path, Token,
+};
+
+pub mod select_include;
+
+#[derive(Debug)]
+pub enum RelationArity {
+    One,
+    Many,
+}
+
+impl Parse for RelationArity {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let ident = input.parse::<Ident>()?;
+
+        Ok(match ident.to_string().as_str() {
+            "One" => Self::One,
+            "Many" => Self::Many,
+            _ => return Err(syn::Error::new_spanned(ident, "expected `One` or `Many`")),
+        })
+    }
+}
+
+impl ToTokens for RelationArity {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        tokens.extend(match self {
+            Self::One => quote!(One),
+            Self::Many => quote!(Many),
+        })
+    }
+}
+
+#[derive(Debug)]
+pub enum Arity {
+    Scalar,
+    Relation(Path, RelationArity),
+}
+
+impl Parse for Arity {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let ident = input.parse::<Ident>()?;
+
+        Ok(match ident.to_string().as_str() {
+            "Scalar" => Self::Scalar,
+            "Relation" => {
+                let content;
+                parenthesized!(content in input);
+
+                Self::Relation(content.parse()?, {
+                    content.parse::<Token![,]>()?;
+                    content.parse()?
+                })
+            }
+            _ => {
+                return Err(syn::Error::new_spanned(
+                    ident,
+                    "expected `Scalar` or `Relation`",
+                ))
+            }
+        })
+    }
+}
+
+impl ToTokens for Arity {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        match self {
+            Self::Scalar => tokens.extend(quote!(Scalar)),
+            Self::Relation(path, arity) => tokens.extend(quote!(Relation(#path, #arity))),
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct FieldTuple {
+    pub name: Ident,
+    pub arity: Arity,
+}
+
+impl FieldTuple {
+    pub fn new(field: FieldWalker, module_path: &TokenStream) -> Self {
+        let field_name_snake = snake_ident(field.name());
+
+        let arity = match field.refine() {
+            RefinedFieldWalker::Scalar(_) => Arity::Scalar,
+            RefinedFieldWalker::Relation(relation_field) => {
+                let related_model_name_snake = snake_ident(relation_field.related_model().name());
+
+                let relation_arity = match &field.ast_field().arity {
+                    FieldArity::List => RelationArity::Many,
+                    _ => RelationArity::One,
+                };
+
+                Arity::Relation(
+                    parse_quote!(#module_path #related_model_name_snake),
+                    relation_arity,
+                )
+            }
+        };
+
+        FieldTuple {
+            name: field_name_snake,
+            arity,
+        }
+    }
+}
+
+impl Parse for FieldTuple {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let content;
+        parenthesized!(content in input);
+
+        Ok(Self {
+            name: content.parse()?,
+            arity: {
+                content.parse::<Token![,]>()?;
+                content.parse()?
+            },
+        })
+    }
+}
+
+impl ToTokens for FieldTuple {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        let Self { name, arity } = self;
+
+        tokens.extend(quote!((#name, #arity)));
+    }
+}
diff --git a/crates/generator-shared/src/select_include/mod.rs b/crates/generator-shared/src/select_include/mod.rs
new file mode 100644
index 00000000..5f485408
--- /dev/null
+++ b/crates/generator-shared/src/select_include/mod.rs
@@ -0,0 +1,5 @@
+mod selectable_fields;
+mod variant;
+
+pub use selectable_fields::*;
+pub use variant::*;
diff --git a/crates/generator-shared/src/select_include/selectable_fields.rs b/crates/generator-shared/src/select_include/selectable_fields.rs
new file mode 100644
index 00000000..0209ac27
--- /dev/null
+++ b/crates/generator-shared/src/select_include/selectable_fields.rs
@@ -0,0 +1,54 @@
+use prisma_client_rust_sdk::prisma::prisma_models::walkers::FieldWalker;
+use proc_macro2::TokenStream;
+use quote::*;
+use syn::{
+    bracketed,
+    parse::{Parse, ParseStream},
+    punctuated::Punctuated,
+    Token,
+};
+
+use crate::FieldTuple;
+
+#[derive(Debug)]
+pub struct SelectableFields(Vec<FieldTuple>);
+
+impl SelectableFields {
+    pub fn new<'a>(
+        fields: impl Iterator<Item = FieldWalker<'a>>,
+        module_path: &TokenStream,
+    ) -> Self {
+        Self(
+            fields
+                .map(|field| FieldTuple::new(field, module_path))
+                .collect(),
+        )
+    }
+}
+
+impl Parse for SelectableFields {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let contents;
+        bracketed!(contents in input);
+
+        Ok(Self(
+            Punctuated::<FieldTuple, Token![,]>::parse_terminated(&contents)?
+                .into_iter()
+                .collect(),
+        ))
+    }
+}
+
+impl ToTokens for SelectableFields {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        let Self(fields) = self;
+
+        tokens.extend(quote!([#(#fields),*]))
+    }
+}
+
+impl SelectableFields {
+    pub fn iter(&self) -> impl Iterator<Item = &FieldTuple> {
+        self.0.iter()
+    }
+}
diff --git a/crates/generator-shared/src/select_include/variant.rs b/crates/generator-shared/src/select_include/variant.rs
new file mode 100644
index 00000000..9713e543
--- /dev/null
+++ b/crates/generator-shared/src/select_include/variant.rs
@@ -0,0 +1,58 @@
+use convert_case::{Case, Casing};
+use proc_macro2::TokenStream;
+use quote::*;
+use syn::{
+    parse::{Parse, ParseStream},
+    Ident,
+};
+
+mod kw {
+    syn::custom_keyword!(select);
+    syn::custom_keyword!(include);
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum Variant {
+    Select,
+    Include,
+}
+
+impl Variant {
+    pub fn type_trait(&self) -> Ident {
+        format_ident!("{}Type", self.to_string().to_case(Case::Pascal))
+    }
+
+    pub fn param(&self) -> Ident {
+        format_ident!("{}Param", self.to_string().to_case(Case::Pascal))
+    }
+}
+
+impl Parse for Variant {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        if input.peek(kw::select) {
+            input.parse::<kw::select>().map(|_| Self::Select)
+        } else if input.peek(kw::include) {
+            input.parse::<kw::include>().map(|_| Self::Include)
+        } else {
+            Err(input.error("expected 'select' or 'include'"))
+        }
+    }
+}
+
+impl core::fmt::Display for Variant {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let s = match self {
+            Self::Select => "select",
+            Self::Include => "include",
+        };
+
+        write!(f, "{}", s)
+    }
+}
+
+impl ToTokens for Variant {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        let ident = format_ident!("{self}");
+        tokens.extend(quote!(#ident));
+    }
+}
diff --git a/crates/generator/Cargo.toml b/crates/generator/Cargo.toml
index 468b34e2..548e3d86 100644
--- a/crates/generator/Cargo.toml
+++ b/crates/generator/Cargo.toml
@@ -24,7 +24,7 @@ serde_json.workspace = true
 serde.workspace = true
 serde_path_to_error = "0.1.7"
 quote = "1.0.18"
-syn = "1.0.91"
+syn = { version = "1.0.91", features = ["full"] }
 proc-macro2 = "1.0.39"
 http = "0.2.6"
 directories = "4.0.1"
@@ -34,3 +34,4 @@ regex = "1.5.5"
 thiserror = "1.0.37"
 
 prisma-client-rust-sdk = { path = "../sdk", default_features = false }
+prisma-client-rust-generator-shared = { path = "../generator-shared" }
diff --git a/crates/generator/src/models/filter.rs b/crates/generator/src/models/filter.rs
index c9badb94..794c9035 100644
--- a/crates/generator/src/models/filter.rs
+++ b/crates/generator/src/models/filter.rs
@@ -1,4 +1,5 @@
-use prisma_client_rust_sdk::prisma::prisma_models::walkers::{ModelWalker, RefinedFieldWalker};
+use prisma_client_rust_generator_shared::select_include::SelectableFields;
+use prisma_client_rust_sdk::prisma::prisma_models::walkers::ModelWalker;
 
 use crate::prelude::*;
 
@@ -7,26 +8,13 @@ pub fn r#macro(model: ModelWalker, module_path: &TokenStream) -> TokenStream {
 
     let name = format_ident!("_{}_filter", model.name().to_case(Case::Snake, true));
 
-    let fields = model.fields().map(|field| {
-        let field_name_snake = snake_ident(field.name());
-
-        let variant = match field.refine() {
-            RefinedFieldWalker::Scalar(_) => quote!(Scalar),
-            RefinedFieldWalker::Relation(relation_field) => {
-                let related_model_name_snake = snake_ident(relation_field.related_model().name());
-
-                quote!(Relation(#module_path #related_model_name_snake))
-            }
-        };
-
-        quote!((#field_name_snake, #variant))
-    });
+    let selectable_fields = SelectableFields::new(model.fields(), module_path);
 
     quote! {
         ::prisma_client_rust::macros::filter_factory!(
             #name,
             #module_path #model_name_snake,
-            [#(#fields),*]
+            #selectable_fields
         );
     }
 }
diff --git a/crates/generator/src/models/include_select.rs b/crates/generator/src/models/include_select.rs
deleted file mode 100644
index f263c65e..00000000
--- a/crates/generator/src/models/include_select.rs
+++ /dev/null
@@ -1,749 +0,0 @@
-use prisma_client_rust_sdk::prisma::{
-    prisma_models::{
-        walkers::{FieldWalker, ModelWalker, RefinedFieldWalker, ScalarFieldWalker},
-        FieldArity,
-    },
-    psl::parser_database::ScalarFieldType,
-};
-
-use crate::prelude::*;
-
-enum Variant {
-    Select,
-    Include,
-}
-
-impl core::fmt::Display for Variant {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        let s = match self {
-            Self::Select => "select",
-            Self::Include => "include",
-        };
-
-        write!(f, "{}", s)
-    }
-}
-
-impl Variant {
-    fn type_trait(&self) -> Ident {
-        format_ident!("{}Type", pascal_ident(&self.to_string()))
-    }
-
-    fn param(&self) -> Ident {
-        format_ident!("{}Param", pascal_ident(&self.to_string()))
-    }
-}
-
-fn model_macro<'a>(
-    model: ModelWalker<'a>,
-    module_path: &TokenStream,
-    variant: Variant,
-    // Fields that should always be included
-    base_fields: impl Iterator<Item = ScalarFieldWalker<'a>> + Clone,
-    // Fields that can be picked from
-    selection_fields: impl Iterator<Item = FieldWalker<'a>> + Clone,
-) -> TokenStream {
-    let model_name_pascal_str = pascal_ident(model.name()).to_string();
-    let model_name_snake = snake_ident(model.name());
-    let model_name_snake_raw = snake_ident_raw(model.name());
-    let macro_name = format_ident!("_{variant}_{model_name_snake_raw}");
-
-    let model_module = quote!($crate::#module_path #model_name_snake);
-
-    let selection_type = variant.type_trait();
-    let selection_param = variant.param();
-
-    let variant_ident = format_ident!("{variant}");
-    let variant_pascal = pascal_ident(&variant.to_string());
-
-    let filters_pattern_produce = quote!(($($filters:tt)+)$(.$arg:ident($($arg_params:tt)*))*);
-    let filters_pattern_consume = quote!(($($filters)+)$(.$arg($($arg_params)*))*);
-
-    let selections_pattern_produce = quote!(: $selection_mode:ident {$($selections:tt)+});
-    let selections_pattern_consume = quote!(: $selection_mode {$($selections)+});
-
-    let selection_pattern_produce =
-        quote!($field:ident $(#filters_pattern_produce)? $(#selections_pattern_produce)?);
-    let selection_pattern_consume =
-        quote!($field $(#filters_pattern_consume)? $(#selections_pattern_consume)?);
-
-    let field_type_impls = selection_fields.clone().map(|field| {
-        let field_name_snake = snake_ident(field.name());
-        let field_type = field.type_tokens(module_path);
-
-        let selection_type_impl = matches!(field.refine(), RefinedFieldWalker::Relation(_)).then(|| {
-            let field_type = field
-                .ast_field()
-                .arity
-                .wrap_type(&quote!(#field_name_snake::Data));
-
-            quote!((@field_type; #field_name_snake #selections_pattern_produce) => { #field_type };)
-        });
-
-        quote!(#selection_type_impl)
-    });
-
-    let field_module_impls = model.relation_fields().map(|field| {
-        let field_name_snake = snake_ident(field.name());
-        let relation_model_name_snake = snake_ident(field.related_model().name());
-
-        quote! {
-            (@field_module; #field_name_snake #selections_pattern_produce) => {
-                $crate::#module_path #relation_model_name_snake::#variant_ident!(@definitions; ; $($selections)+);
-            };
-        }
-    });
-
-    let selection_field_to_selection_param_impls = selection_fields.clone().map(|field| {
-        let field_name_snake = snake_ident(field.name());
-
-        let field_module = quote!(#model_module::#field_name_snake);
-
-        match field.refine() {
-            RefinedFieldWalker::Relation(relation_field) =>{
-                let relation_model_name_snake = snake_ident(relation_field.related_model().name());
-
-                let relation_model_module = quote!($crate::#module_path #relation_model_name_snake);
-
-                match relation_field.ast_field().arity {
-                    FieldArity::List => {
-                        quote! {
-                            (@selection_field_to_selection_param; #field_name_snake $(#filters_pattern_produce)? #selections_pattern_produce) => {{
-                                Into::<#model_module::#selection_param>::into(
-                                    #field_module::#variant_pascal::$selection_mode(
-                                        #relation_model_module::ManyArgs::new($crate::#module_path #relation_model_name_snake::#variant_ident!(
-                                            @filters_to_args;
-                                            $($($filters)+)?
-                                        )) $($(.$arg($($arg_params)*))*)?,
-                                        #relation_model_module::select!(
-                                            @selections_to_params;
-                                            #selections_pattern_consume
-                                        ).into_iter().collect()
-                                    )
-                                )
-                            }};
-                            (@selection_field_to_selection_param; #field_name_snake $(#filters_pattern_produce)?) => {{
-                                Into::<#model_module::#selection_param>::into(
-                                    #field_module::#variant_pascal::Fetch(
-                                        #relation_model_module::ManyArgs::new($crate::#module_path #relation_model_name_snake::#variant_ident!(
-                                            @filters_to_args;
-                                            $($($filters)+)?
-                                        )) $($(.$arg($($arg_params)*))*)?
-                                    ),
-                                )
-                            }};
-                        }
-                    },
-                    _ => quote! {
-                        (@selection_field_to_selection_param; #field_name_snake $(#filters_pattern_produce)? #selections_pattern_produce) => {{
-                            Into::<#model_module::#selection_param>::into(
-                                #field_module::#variant_pascal::$selection_mode(
-                                    #relation_model_module::select!(
-                                        @selections_to_params;
-                                        #selections_pattern_consume
-                                    ).into_iter().collect()
-                                )
-                            )
-                        }};
-                        (@selection_field_to_selection_param; #field_name_snake $(#filters_pattern_produce)?) => {{
-                            Into::<#model_module::#selection_param>::into(
-                                #field_module::#variant_pascal::Fetch
-                            )
-                        }};
-                    }
-                }
-            },
-            RefinedFieldWalker::Scalar(scalar_field) => match scalar_field.scalar_field_type() {
-                ScalarFieldType::CompositeType(_) => quote!(),
-                _ => {
-                    quote! {
-                        (@selection_field_to_selection_param; #field_name_snake) => {
-                            Into::<#model_module::#selection_param>::into(
-                                #field_module::#variant_pascal
-                            )
-                        };
-                    }
-                }
-            }
-        }
-    });
-
-    let data_struct_scalar_fields = base_fields.clone().map(|f| {
-        let field_name_snake = snake_ident(f.name());
-        let field_type = f.type_tokens(module_path);
-
-        let specta_rename = cfg!(feature = "specta").then(|| {
-            quote!(#[specta(rename_from_path = $crate::#module_path #model_name_snake::#field_name_snake::NAME)])
-        });
-
-        quote! {
-            #specta_rename
-            pub #field_name_snake: #field_type
-        }
-    });
-
-    let fields_enum_variants = selection_fields.clone().map(|f| {
-        let i = snake_ident(f.name());
-        quote!(#i)
-    });
-
-    let field_serde_names = model
-        .fields()
-        .filter(|f| f.ast_field().field_type.as_unsupported().is_none())
-        .map(|f| {
-            let field_name_str = f.name();
-            let field_name_snake = snake_ident(f.name());
-
-            quote!((@field_serde_name; #field_name_snake) => { #field_name_str };)
-        });
-
-    let base_field_names_snake = base_fields
-        .clone()
-        .map(|f| snake_ident(f.name()))
-        .collect::<Vec<_>>();
-
-    let deserialize_impl = {
-        let field_names_str = model.fields().map(|f| f.name());
-
-        quote! {
-            #[allow(warnings)]
-            enum Field {
-                $($field),+,
-                #(#base_field_names_snake),*
-            }
-
-            impl<'de> ::serde::Deserialize<'de> for Field {
-                fn deserialize<D>(deserializer: D) -> std::result::Result<Field, D::Error>
-                where
-                    D: ::serde::Deserializer<'de>,
-                {
-                    struct FieldVisitor;
-
-                    impl<'de> ::serde::de::Visitor<'de> for FieldVisitor {
-                        type Value = Field;
-
-                        fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
-                            formatter.write_str(&[
-                                $(#model_module::$field::NAME),+,
-                                #(#model_module::#base_field_names_snake::NAME),*
-                            ].into_iter().collect::<Vec<_>>().join(", "))
-                        }
-
-                        fn visit_str<E>(self, value: &str) -> std::result::Result<Field, E>
-                        where
-                            E: ::serde::de::Error,
-                        {
-                            match value {
-                                $(#model_module::$field::NAME => Ok(Field::$field)),*,
-                                #(#model_module::#base_field_names_snake::NAME => Ok(Field::#base_field_names_snake),)*
-                                _ => Err(::serde::de::Error::unknown_field(value, FIELDS)),
-                            }
-                        }
-                    }
-
-                    deserializer.deserialize_identifier(FieldVisitor)
-                }
-            }
-
-            struct DataVisitor;
-
-            impl<'de> ::serde::de::Visitor<'de> for DataVisitor {
-                type Value = Data;
-
-                fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
-                    formatter.write_str("struct Data")
-                }
-
-                fn visit_map<V>(self, mut map: V) -> std::result::Result<Data, V::Error>
-                where
-                    V: ::serde::de::MapAccess<'de>,
-                {
-                    $(let mut $field = None;)*
-                    #(let mut #base_field_names_snake = None;)*
-
-                    while let Some(key) = map.next_key()? {
-                        match key {
-                            #(Field::#base_field_names_snake => {
-                                if #base_field_names_snake.is_some() {
-                                    return Err(::serde::de::Error::duplicate_field(
-                                        #model_module::#base_field_names_snake::NAME
-                                    ));
-                                }
-                                #base_field_names_snake = Some(map.next_value()?);
-                            })*
-                            $(Field::$field => {
-                                if $field.is_some() {
-                                    return Err(::serde::de::Error::duplicate_field(
-                                        #model_module::$field::NAME
-                                    ));
-                                }
-                                $field = Some(map.next_value()?);
-                            })*
-                        }
-                    }
-
-                    $(let $field = $field.ok_or_else(||
-                        serde::de::Error::missing_field(#model_module::$field::NAME)
-                    )?;)*
-                    #(let #base_field_names_snake = #base_field_names_snake.ok_or_else(||
-                        serde::de::Error::missing_field(#model_module::#base_field_names_snake::NAME)
-                    )?;)*
-
-                    Ok(Data { #(#base_field_names_snake,)* $($field),* })
-                }
-            }
-
-            const FIELDS: &'static [&'static str] = &[#(#field_names_str),*];
-            deserializer.deserialize_struct("Data", FIELDS, DataVisitor)
-        }
-    };
-
-    let serialize_impl = {
-        quote! {
-            use ::serde::ser::SerializeStruct;
-
-            let mut state = serializer.serialize_struct(
-                "Data",
-                [
-                    $(stringify!($field),)+
-                    #(stringify!(#base_field_names_snake)),*
-                ].len()
-            )?;
-            $(state.serialize_field(#model_module::$field::NAME, &self.$field)?;)*
-            #(state.serialize_field(#model_module::#base_field_names_snake::NAME, &self.#base_field_names_snake)?;)*
-            state.end()
-        }
-    };
-
-    let all_fields_str = selection_fields
-        .clone()
-        .map(|f| snake_ident(f.name()).to_string())
-        .collect::<Vec<_>>()
-        .join(", ");
-
-    let data_struct_attrs = quote! {
-        #[allow(warnings)]
-        #[derive(std::fmt::Debug, Clone)]
-    };
-
-    let specta_macro_arms = cfg!(feature = "specta").then(|| {
-        let data_struct_attrs = quote! {
-            #data_struct_attrs
-            #[derive(::prisma_client_rust::specta::Type)]
-            #[specta(
-                rename_from_path = SPECTA_TYPE_NAME,
-                crate = "prisma_client_rust::specta"
-            )]
-        };
-
-        quote! {
-            (@specta_data_struct; $struct:item;) => {
-                #data_struct_attrs
-                #[specta(inline)]
-                $struct
-            };
-            (@specta_data_struct; $struct:item; $name:ident) => {
-                #data_struct_attrs
-                $struct
-            };
-        }
-    });
-
-    let data_struct = {
-        let specta_rename = cfg!(feature = "specta").then(|| {
-            quote!(#[specta(rename_from_path =
-                #model_module::$field::NAME
-            )])
-        });
-
-        quote! {
-            pub struct Data {
-                #(#data_struct_scalar_fields,)*
-                $(
-                    #specta_rename
-                    pub $field: #model_module::#variant_ident!(@field_type; $field $(#selections_pattern_consume)?),
-                )+
-            }
-        }
-    };
-    let data_struct = cfg!(feature = "specta")
-        .then(|| {
-            quote! {
-                const SPECTA_TYPE_NAME: &'static str = prisma_client_rust::macros::to_pascal_case!(
-                    $($module_name)?
-                );
-                #model_module::#variant_ident!(@specta_data_struct; #data_struct; $($module_name)?);
-            }
-        })
-        .unwrap_or(quote!( #data_struct_attrs #data_struct ));
-
-    let selection = {
-        let scalar_selections = matches!(variant, Variant::Include).then(||
-            quote! {
-                <$crate::#module_path #model_name_snake::Types as ::prisma_client_rust::ModelTypes>::scalar_selections()
-            }
-        );
-
-        quote!(Selection(
-            [
-                $crate::#module_path #model_name_snake::#variant_ident!(
-                    @selections_to_params; : #variant_ident
-                    { $(#selection_pattern_consume)+ }
-                )
-                    .into_iter()
-                    .map(|p| p.to_selection())
-                    .collect::<Vec<_>>(),
-                #scalar_selections
-            ]
-            .into_iter()
-            .flatten()
-            .collect::<Vec<_>>()
-        ))
-    };
-
-    let selection_struct = quote! {
-        pub struct Selection(Vec<::prisma_client_rust::Selection>);
-
-        impl ::prisma_client_rust::#selection_type for Selection {
-            type Data = Data;
-            type ModelData = #model_module::Data;
-
-            fn to_selections(self) -> Vec<::prisma_client_rust::Selection> {
-                self.0
-            }
-        }
-    };
-
-    quote! {
-        #[macro_export]
-        macro_rules! #macro_name {
-            ($(($($func_arg:ident: $func_arg_ty:ty),+) =>)? $module_name:ident { $(#selection_pattern_produce)+ }) => {
-                #[allow(warnings)]
-                pub mod $module_name {
-                    #model_module::#variant_ident!(@definitions; $module_name; $(#selection_pattern_consume)+);
-
-                    use super::*;
-
-                    #selection_struct
-
-                    pub fn #variant_ident($($($func_arg:$func_arg_ty),+)?) -> Selection {
-                        #selection
-                    }
-                }
-            };
-            ({ $(#selection_pattern_produce)+ }) => {{
-                #model_module::#variant_ident!(@definitions; ; $(#selection_pattern_consume)+);
-
-                #selection_struct
-
-                #selection
-            }};
-            (@definitions; $($module_name:ident)?; $(#selection_pattern_produce)+) => {
-                #[allow(warnings)]
-                enum Fields {
-                    #(#fields_enum_variants),*
-                }
-
-                #[allow(warnings)]
-                impl Fields {
-                    fn selections() {
-                        $(let _ = Fields::$field;)+
-                    }
-                }
-
-                #data_struct
-
-                impl ::serde::Serialize for Data {
-                    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
-                    where
-                        S: ::serde::Serializer,
-                    {
-                        #serialize_impl
-                    }
-                }
-
-                impl<'de> ::serde::Deserialize<'de> for Data {
-                    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
-                    where
-                        D: ::serde::Deserializer<'de>,
-                    {
-                        #deserialize_impl
-                    }
-                }
-
-                $($(pub mod $field {
-                    #model_module::$selection_mode!(@field_module; $field #selections_pattern_consume);
-                })?)+
-            };
-
-            #(#field_type_impls)*
-            (@field_type; $field:ident) => { #model_module::$field::Type };
-            (@field_type; $field:ident $($tokens:tt)*) => { compile_error!(stringify!(Cannot include nonexistent relation $field on model #model_name_pascal_str, available relations are #all_fields_str)) };
-
-            #(#field_module_impls)*
-            (@field_module; $($tokens:tt)*) => {};
-
-            #(#selection_field_to_selection_param_impls)*
-            (@selection_field_to_selection_param; $($tokens:tt)*) => { compile_error!(stringify!($($tokens)*)) }; // ::prisma_client_rust::Selection::builder("").build() };
-
-            (@selections_to_params; : $macro_name:ident {$(#selection_pattern_produce)+}) => {
-                [ $($crate::#module_path #model_name_snake::$macro_name!(@selection_field_to_selection_param; #selection_pattern_consume),)+]
-            };
-
-            (@filters_to_args;) => {
-                vec![]
-            };
-            (@filters_to_args; $($t:tt)*) => {
-                $($t)*
-            };
-
-            #(#field_serde_names)*
-
-            #specta_macro_arms
-        }
-        pub use #macro_name as #variant_ident;
-    }
-}
-
-fn field_module_enum(field: FieldWalker, variant: Variant) -> Option<TokenStream> {
-    let pcr = quote!(::prisma_client_rust);
-
-    let field_name_pascal = pascal_ident(field.name());
-    let field_name_str = field.name();
-
-    let variant_pascal = pascal_ident(&variant.to_string());
-    let variant_param = variant.param();
-
-    Some(match field.refine() {
-        RefinedFieldWalker::Relation(relation_field) => {
-            let relation_model_name_snake = snake_ident(relation_field.related_model().name());
-
-            let initial_nested_selections = match variant {
-                Variant::Include => {
-                    quote!(<#relation_model_name_snake::Types as #pcr::ModelTypes>::scalar_selections())
-                }
-                Variant::Select => quote!(vec![]),
-            };
-
-            match field.ast_field().arity {
-                FieldArity::List => quote! {
-                    pub enum #variant_pascal {
-                        Select(#relation_model_name_snake::ManyArgs, Vec<#relation_model_name_snake::SelectParam>),
-                        Include(#relation_model_name_snake::ManyArgs, Vec<#relation_model_name_snake::IncludeParam>),
-                        Fetch(#relation_model_name_snake::ManyArgs)
-                    }
-
-                    impl Into<super::#variant_param> for #variant_pascal {
-                        fn into(self) -> super::#variant_param {
-                            super::#variant_param::#field_name_pascal(self)
-                        }
-                    }
-
-                    impl #variant_pascal {
-                        pub fn to_selection(self) -> #pcr::Selection {
-                            let (args, selections) = match self {
-                                Self::Select(args, selections) => (
-                                    args.to_graphql().0,
-                                    selections.into_iter().map(|s| s.to_selection()).collect()
-                                ),
-                                Self::Include(args, selections) => (
-                                    args.to_graphql().0,
-                                    {
-                                        let mut nested_selections = #initial_nested_selections;
-                                        nested_selections.extend(selections.into_iter().map(|s| s.to_selection()));
-                                        nested_selections
-                                    }
-                                ),
-                                Self::Fetch(args) => (
-                                    args.to_graphql().0,
-                                    <#relation_model_name_snake::Types as #pcr::ModelTypes>::scalar_selections()
-                                )
-                            };
-
-                            #pcr::Selection::new(NAME, None, args, selections)
-                        }
-
-                        pub fn select(args: #relation_model_name_snake::ManyArgs, nested_selections: Vec<#relation_model_name_snake::SelectParam>) -> Self {
-                            Self::Select(args, nested_selections)
-                        }
-
-                        pub fn include(args: #relation_model_name_snake::ManyArgs, nested_selections: Vec<#relation_model_name_snake::IncludeParam>) -> Self {
-                            Self::Include(args, nested_selections)
-                        }
-                    }
-                },
-                _ => quote! {
-                    pub enum #variant_pascal {
-                        Select(Vec<#relation_model_name_snake::SelectParam>),
-                        Include(Vec<#relation_model_name_snake::IncludeParam>),
-                        Fetch
-                    }
-
-                    impl Into<super::#variant_param> for #variant_pascal {
-                        fn into(self) -> super::#variant_param {
-                            super::#variant_param::#field_name_pascal(self)
-                        }
-                    }
-
-                    impl #variant_pascal {
-                        pub fn to_selection(self) -> #pcr::Selection {
-                            let selections = match self {
-                                Self::Select(selections) => {
-                                    selections.into_iter().map(|s| s.to_selection()).collect()
-                                },
-                                Self::Include(selections) => {
-                                    let mut nested_selections = #initial_nested_selections;
-                                    nested_selections.extend(selections.into_iter().map(|s| s.to_selection()));
-                                    nested_selections
-                                },
-                                Self::Fetch => {
-                                    <#relation_model_name_snake::Types as #pcr::ModelTypes>::scalar_selections()
-                                }
-                            };
-
-                            #pcr::Selection::new(#field_name_str, None, [], selections)
-                        }
-
-                        pub fn select(nested_selections: Vec<#relation_model_name_snake::SelectParam>) -> Self {
-                            Self::Select(nested_selections)
-                        }
-
-                        pub fn include(nested_selections: Vec<#relation_model_name_snake::IncludeParam>) -> Self {
-                            Self::Include(nested_selections)
-                        }
-                    }
-                },
-            }
-        }
-        RefinedFieldWalker::Scalar(_) => quote! {
-            pub struct #variant_pascal;
-
-            impl Into<super::#variant_param> for #variant_pascal {
-                fn into(self) -> super::#variant_param {
-                    super::#variant_param::#field_name_pascal(self)
-                }
-            }
-
-            impl #variant_pascal {
-                pub fn to_selection(self) -> #pcr::Selection {
-                    #pcr::sel(NAME)
-                }
-            }
-        },
-    })
-}
-
-fn model_module_enum(model: ModelWalker, variant: Variant) -> TokenStream {
-    let pcr = quote!(::prisma_client_rust);
-
-    let variant_pascal = pascal_ident(&variant.to_string());
-
-    let variants = model
-        .fields()
-        .filter(|f| !f.ast_field().field_type.as_unsupported().is_some())
-        .map(|field| {
-            let field_name_snake = snake_ident(field.name());
-            let field_name_pascal = pascal_ident(field.name());
-
-            quote!(#field_name_pascal(#field_name_snake::#variant_pascal))
-        });
-
-    let field_names_pascal = model
-        .fields()
-        .filter(|f| !f.ast_field().field_type.as_unsupported().is_some())
-        .map(|field| pascal_ident(field.name()));
-
-    let variant_param = variant.param();
-
-    quote! {
-        pub enum #variant_param {
-            #(#variants),*
-        }
-
-        impl #variant_param {
-            pub fn to_selection(self) -> #pcr::Selection {
-                match self {
-                    #(Self::#field_names_pascal(data) => data.to_selection()),*
-                }
-            }
-        }
-    }
-}
-
-pub mod include {
-    use prisma_client_rust_sdk::prisma::prisma_models::walkers::{ModelWalker, RefinedFieldWalker};
-
-    use crate::models::ModelModulePart;
-
-    use super::*;
-
-    pub fn model_data(model: ModelWalker, module_path: &TokenStream) -> ModelModulePart {
-        let r#macro = super::model_macro(
-            model,
-            module_path,
-            Variant::Include,
-            model
-                .scalar_fields()
-                .filter(|f| !f.scalar_field_type().is_unsupported())
-                .collect::<Vec<_>>()
-                .into_iter(),
-            model
-                .fields()
-                .filter(|f| matches!(f.refine(), RefinedFieldWalker::Relation(_))),
-        );
-
-        let r#enum = super::model_module_enum(model, Variant::Include);
-
-        ModelModulePart {
-            data: quote! {
-                #r#macro
-                 #r#enum
-            },
-            fields: model
-                .fields()
-                .filter(|f| f.ast_field().field_type.as_unsupported().is_none())
-                .flat_map(|field| {
-                    super::field_module_enum(field, Variant::Include)
-                        .map(|e| (field.name().to_string(), e))
-                })
-                .collect(),
-        }
-    }
-}
-
-pub mod select {
-    use prisma_client_rust_sdk::prisma::prisma_models::walkers::ModelWalker;
-
-    use crate::models::ModelModulePart;
-
-    use super::*;
-
-    pub fn model_data(model: ModelWalker, module_path: &TokenStream) -> ModelModulePart {
-        let r#macro = super::model_macro(
-            model,
-            module_path,
-            Variant::Select,
-            vec![].into_iter(),
-            model
-                .fields()
-                .filter(|f| f.ast_field().field_type.as_unsupported().is_none()),
-        );
-
-        let r#enum = super::model_module_enum(model, Variant::Select);
-
-        ModelModulePart {
-            data: quote! {
-                #r#macro
-                #r#enum
-            },
-            fields: model
-                .fields()
-                .filter(|f| f.ast_field().field_type.as_unsupported().is_none())
-                .flat_map(|field| {
-                    super::field_module_enum(field, Variant::Select)
-                        .map(|e| (field.name().to_string(), e))
-                })
-                .collect(),
-        }
-    }
-}
diff --git a/crates/generator/src/models/mod.rs b/crates/generator/src/models/mod.rs
index 304f4fc0..782b857e 100644
--- a/crates/generator/src/models/mod.rs
+++ b/crates/generator/src/models/mod.rs
@@ -2,10 +2,10 @@ mod actions;
 mod create;
 mod data;
 mod filter;
-mod include_select;
 mod order_by;
 mod pagination;
 mod partial_unchecked;
+mod select_include;
 mod set_params;
 mod types;
 mod where_params;
@@ -13,7 +13,6 @@ mod with_params;
 
 use std::collections::BTreeMap;
 
-use include_select::*;
 use prisma_client_rust_sdk::{
     prelude::*,
     prisma::{
@@ -21,6 +20,7 @@ use prisma_client_rust_sdk::{
         psl::parser_database::ScalarFieldType,
     },
 };
+use select_include::*;
 
 pub struct RequiredField<'a> {
     pub push_wrapper: TokenStream,
@@ -84,7 +84,6 @@ pub fn modules(args: &GenerateArgs, module_path: &TokenStream) -> Vec<Module> {
         .walk_models()
         .map(|model| {
             let model_name = model.name();
-            let model_name_snake = snake_ident(model_name);
 
             let actions_struct = actions::struct_definition(model, args);
 
diff --git a/crates/generator/src/models/partial_unchecked.rs b/crates/generator/src/models/partial_unchecked.rs
index 00f8fa07..3c8045fb 100644
--- a/crates/generator/src/models/partial_unchecked.rs
+++ b/crates/generator/src/models/partial_unchecked.rs
@@ -7,7 +7,7 @@ pub fn r#macro(model: ModelWalker, module_path: &TokenStream) -> TokenStream {
     let model_name_snake_raw = snake_ident_raw(model.name());
     let macro_name = format_ident!("_partial_unchecked_{model_name_snake_raw}");
 
-    let model_module = quote!($crate::#module_path #model_name_snake);
+    let model_module = quote!(#module_path #model_name_snake);
 
     let struct_fields = model.scalar_fields().map(|scalar_field| {
         let field_name_str = scalar_field.name();
@@ -24,26 +24,17 @@ pub fn r#macro(model: ModelWalker, module_path: &TokenStream) -> TokenStream {
         quote! {
             #[serde(rename = #field_name_str)]
             #double_option_attrs
-            pub #field_name_snake: $crate::#module_path #model_name_snake::#field_name_snake::Type
+            pub #field_name_snake: #module_path #model_name_snake::#field_name_snake::Type
         }
     });
 
     quote! {
-        #[macro_export]
-        macro_rules! #macro_name {
-            ($struct_name:ident {
-                $($scalar_field:ident)+
-            }) => {
-                ::prisma_client_rust::macros::partial_unchecked! {
-                    #model_module
-                    struct $struct_name {
-                        #(#struct_fields),*
-                    }
-                    [$($scalar_field),+]
-                }
-            };
-        }
-
-        pub use #macro_name as partial_unchecked;
+        ::prisma_client_rust::macros::partial_unchecked_factory!(
+            #macro_name,
+            #model_module,
+            struct Data {
+                #(#struct_fields),*
+            }
+         );
     }
 }
diff --git a/crates/generator/src/models/select_include.rs b/crates/generator/src/models/select_include.rs
new file mode 100644
index 00000000..901fdcb3
--- /dev/null
+++ b/crates/generator/src/models/select_include.rs
@@ -0,0 +1,293 @@
+use prisma_client_rust_generator_shared::select_include::{SelectableFields, Variant};
+use prisma_client_rust_sdk::prisma::prisma_models::{
+    walkers::{FieldWalker, ModelWalker, RefinedFieldWalker},
+    FieldArity,
+};
+use syn::{parse_quote, ItemStruct};
+
+use crate::prelude::*;
+
+fn model_macro<'a>(
+    model: ModelWalker<'a>,
+    module_path: &TokenStream,
+    variant: Variant,
+    // Fields that can be picked from
+    selection_fields: impl Iterator<Item = FieldWalker<'a>>,
+) -> TokenStream {
+    let model_name_snake = snake_ident(model.name());
+    let model_name_snake_raw = snake_ident_raw(model.name());
+    let macro_name = format_ident!("_{variant}_{model_name_snake_raw}");
+
+    let factory_name = format_ident!("{variant}_factory");
+
+    let data_struct: ItemStruct = {
+        let fields = model.fields().map(|field| {
+            let field_name_str = field.name();
+            let field_name_snake = snake_ident(field_name_str);
+
+            quote! {
+                #[serde(rename = #field_name_str)]
+                #field_name_snake: #field_name_snake::Type
+            }
+        });
+
+        parse_quote! {
+            struct Data {
+                #(#fields),*
+            }
+        }
+    };
+
+    let selectable_fields = SelectableFields::new(selection_fields, module_path);
+
+    quote! {
+        ::prisma_client_rust::macros::#factory_name!(
+            #macro_name,
+            #variant,
+            #module_path #model_name_snake,
+            #data_struct,
+            #selectable_fields
+        );
+    }
+}
+
+fn field_module_enum(field: FieldWalker, variant: Variant) -> Option<TokenStream> {
+    let pcr = quote!(::prisma_client_rust);
+
+    let field_name_pascal = pascal_ident(field.name());
+    let field_name_str = field.name();
+
+    let variant_pascal = pascal_ident(&variant.to_string());
+    let variant_param = variant.param();
+
+    Some(match field.refine() {
+        RefinedFieldWalker::Relation(relation_field) => {
+            let relation_model_name_snake = snake_ident(relation_field.related_model().name());
+
+            let initial_nested_selections = match variant {
+                Variant::Include => {
+                    quote!(<#relation_model_name_snake::Types as #pcr::ModelTypes>::scalar_selections())
+                }
+                Variant::Select => quote!(vec![]),
+            };
+
+            match field.ast_field().arity {
+                FieldArity::List => quote! {
+                    pub enum #variant_pascal {
+                        Select(#relation_model_name_snake::ManyArgs, Vec<#relation_model_name_snake::SelectParam>),
+                        Include(#relation_model_name_snake::ManyArgs, Vec<#relation_model_name_snake::IncludeParam>),
+                        Fetch(#relation_model_name_snake::ManyArgs)
+                    }
+
+                    impl Into<super::#variant_param> for #variant_pascal {
+                        fn into(self) -> super::#variant_param {
+                            super::#variant_param::#field_name_pascal(self)
+                        }
+                    }
+
+                    impl #variant_pascal {
+                        pub fn select(args: #relation_model_name_snake::ManyArgs, nested_selections: Vec<#relation_model_name_snake::SelectParam>) -> Self {
+                            Self::Select(args, nested_selections)
+                        }
+
+                        pub fn include(args: #relation_model_name_snake::ManyArgs, nested_selections: Vec<#relation_model_name_snake::IncludeParam>) -> Self {
+                            Self::Include(args, nested_selections)
+                        }
+                    }
+
+                    impl Into<#pcr::Selection> for #variant_pascal {
+                        fn into(self) -> #pcr::Selection {
+                             let (args, selections) = match self {
+                                 Self::Select(args, selections) => (
+                                     args.to_graphql().0,
+                                     selections.into_iter().map(Into::into).collect()
+                                 ),
+                                 Self::Include(args, selections) => (
+                                     args.to_graphql().0,
+                                     {
+                                         let mut nested_selections = #initial_nested_selections;
+                                         nested_selections.extend(selections.into_iter().map(Into::into));
+                                         nested_selections
+                                     }
+                                 ),
+                                 Self::Fetch(args) => (
+                                     args.to_graphql().0,
+                                     <#relation_model_name_snake::Types as #pcr::ModelTypes>::scalar_selections()
+                                 )
+                             };
+
+                             #pcr::Selection::new(NAME, None, args, selections)
+                        }
+                    }
+                },
+                _ => quote! {
+                    pub enum #variant_pascal {
+                        Select(Vec<#relation_model_name_snake::SelectParam>),
+                        Include(Vec<#relation_model_name_snake::IncludeParam>),
+                        Fetch
+                    }
+
+                    impl Into<super::#variant_param> for #variant_pascal {
+                        fn into(self) -> super::#variant_param {
+                            super::#variant_param::#field_name_pascal(self)
+                        }
+                    }
+
+                    impl #variant_pascal {
+                        pub fn select(nested_selections: Vec<#relation_model_name_snake::SelectParam>) -> Self {
+                            Self::Select(nested_selections)
+                        }
+
+                        pub fn include(nested_selections: Vec<#relation_model_name_snake::IncludeParam>) -> Self {
+                            Self::Include(nested_selections)
+                        }
+                    }
+
+                    impl Into<#pcr::Selection> for #variant_pascal {
+                        fn into(self) -> #pcr::Selection {
+                            let selections = match self {
+                                Self::Select(selections) => {
+                                    selections.into_iter().map(Into::into).collect()
+                                },
+                                Self::Include(selections) => {
+                                    let mut nested_selections = #initial_nested_selections;
+                                    nested_selections.extend(selections.into_iter().map(Into::into));
+                                    nested_selections
+                                },
+                                Self::Fetch => {
+                                    <#relation_model_name_snake::Types as #pcr::ModelTypes>::scalar_selections()
+                                }
+                            };
+
+                            #pcr::Selection::new(#field_name_str, None, [], selections)
+                        }
+                    }
+                },
+            }
+        }
+        RefinedFieldWalker::Scalar(_) => quote! {
+            pub struct #variant_pascal;
+
+            impl Into<super::#variant_param> for #variant_pascal {
+                fn into(self) -> super::#variant_param {
+                    super::#variant_param::#field_name_pascal(self)
+                }
+            }
+
+            impl Into<#pcr::Selection> for #variant_pascal {
+                fn into(self) -> #pcr::Selection {
+                    #pcr::sel(NAME)
+                }
+            }
+        },
+    })
+}
+
+fn model_module_enum(model: ModelWalker, variant: Variant) -> TokenStream {
+    let pcr = quote!(::prisma_client_rust);
+
+    let variant_pascal = pascal_ident(&variant.to_string());
+
+    let variants = model
+        .fields()
+        .filter(|f| !f.ast_field().field_type.as_unsupported().is_some())
+        .map(|field| {
+            let field_name_snake = snake_ident(field.name());
+            let field_name_pascal = pascal_ident(field.name());
+
+            quote!(#field_name_pascal(#field_name_snake::#variant_pascal))
+        });
+
+    let field_names_pascal = model
+        .fields()
+        .filter(|f| !f.ast_field().field_type.as_unsupported().is_some())
+        .map(|field| pascal_ident(field.name()));
+
+    let variant_param = variant.param();
+
+    quote! {
+        pub enum #variant_param {
+            #(#variants),*
+        }
+
+        impl Into<#pcr::Selection> for #variant_param {
+            fn into(self) -> #pcr::Selection {
+                match self {
+                    #(Self::#field_names_pascal(data) => data.into()),*
+                }
+            }
+        }
+    }
+}
+
+pub mod include {
+    use prisma_client_rust_sdk::prisma::prisma_models::walkers::{ModelWalker, RefinedFieldWalker};
+
+    use crate::models::ModelModulePart;
+
+    use super::*;
+
+    pub fn model_data(model: ModelWalker, module_path: &TokenStream) -> ModelModulePart {
+        let r#macro = super::model_macro(
+            model,
+            module_path,
+            Variant::Include,
+            model
+                .fields()
+                .filter(|f| matches!(f.refine(), RefinedFieldWalker::Relation(_))),
+        );
+
+        let r#enum = super::model_module_enum(model, Variant::Include);
+
+        ModelModulePart {
+            data: quote! {
+                #r#macro
+                 #r#enum
+            },
+            fields: model
+                .fields()
+                .filter(|f| f.ast_field().field_type.as_unsupported().is_none())
+                .flat_map(|field| {
+                    super::field_module_enum(field, Variant::Include)
+                        .map(|e| (field.name().to_string(), e))
+                })
+                .collect(),
+        }
+    }
+}
+
+pub mod select {
+    use prisma_client_rust_sdk::prisma::prisma_models::walkers::ModelWalker;
+
+    use crate::models::ModelModulePart;
+
+    use super::*;
+
+    pub fn model_data(model: ModelWalker, module_path: &TokenStream) -> ModelModulePart {
+        let r#macro = super::model_macro(
+            model,
+            module_path,
+            Variant::Select,
+            model
+                .fields()
+                .filter(|f| f.ast_field().field_type.as_unsupported().is_none()),
+        );
+
+        let r#enum = super::model_module_enum(model, Variant::Select);
+
+        ModelModulePart {
+            data: quote! {
+                #r#macro
+                #r#enum
+            },
+            fields: model
+                .fields()
+                .filter(|f| f.ast_field().field_type.as_unsupported().is_none())
+                .flat_map(|field| {
+                    super::field_module_enum(field, Variant::Select)
+                        .map(|e| (field.name().to_string(), e))
+                })
+                .collect(),
+        }
+    }
+}
diff --git a/crates/lib/Cargo.toml b/crates/lib/Cargo.toml
index 2c0664d8..08ef6f68 100644
--- a/crates/lib/Cargo.toml
+++ b/crates/lib/Cargo.toml
@@ -8,7 +8,7 @@ rust-version = "1.62"
 
 [features]
 rspc = ["dep:rspc", "specta"]
-specta = ["dep:specta", "prisma-client-rust-macros/specta"]
+specta = ["dep:specta", "prisma-client-rust-generator-macros/specta"]
 sqlite-create-many = ["psl/sqlite-create-many"]
 migrations = ["schema-core", "dep:include_dir", "dep:tempdir", "tokio/fs", "dep:tracing"]
 mocking = ["tokio"]
@@ -34,7 +34,7 @@ base64 = "0.13.0"
 tokio = { version = "1.21.0", optional = true }
 futures = "0.3"
 paste = "1.0.11"
-prisma-client-rust-macros = { path = "../macros" }
+prisma-client-rust-generator-macros = { path = "../generator-macros" }
 
 prisma-models = { workspace = true }
 user-facing-errors = { workspace = true }
diff --git a/crates/lib/src/lib.rs b/crates/lib/src/lib.rs
index 7f256e05..9a08ac68 100644
--- a/crates/lib/src/lib.rs
+++ b/crates/lib/src/lib.rs
@@ -37,7 +37,7 @@ pub use raw::*;
 pub use traits::*;
 pub use transaction::*;
 
-pub use prisma_client_rust_macros as macros;
+pub use prisma_client_rust_generator_macros as macros;
 
 #[cfg(feature = "specta")]
 pub use specta;
diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs
deleted file mode 100644
index ec7fce96..00000000
--- a/crates/macros/src/lib.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-mod filter;
-mod partial_unchecked;
-
-#[proc_macro]
-pub fn to_pascal_case(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    let converted = convert_case::Casing::to_case(&input.to_string(), convert_case::Case::Pascal);
-
-    proc_macro::TokenTree::Literal(proc_macro::Literal::string(&converted)).into()
-}
-#[proc_macro]
-pub fn partial_unchecked(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    partial_unchecked::proc_macro(input)
-}
-
-#[proc_macro]
-pub fn filter(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    filter::proc_macro(input)
-}
-
-#[proc_macro]
-pub fn filter_factory(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    filter::proc_macro_factory(input)
-}
diff --git a/crates/macros/src/partial_unchecked.rs b/crates/macros/src/partial_unchecked.rs
deleted file mode 100644
index 7fd800d4..00000000
--- a/crates/macros/src/partial_unchecked.rs
+++ /dev/null
@@ -1,80 +0,0 @@
-use proc_macro2::Ident;
-use quote::quote;
-use syn::{
-    bracketed, parse::Parse, parse_macro_input, punctuated::Punctuated, ItemStruct, Path, Token,
-};
-
-struct PartialUncheckedInput {
-    model_module: Path,
-    data: ItemStruct,
-    selection: Punctuated<Ident, Token![,]>,
-}
-
-impl Parse for PartialUncheckedInput {
-    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
-        Ok(Self {
-            model_module: input.parse()?,
-            data: input.parse()?,
-            selection: {
-                let content;
-                bracketed!(content in input);
-                Punctuated::<Ident, Token![,]>::parse_terminated(&content)?
-            },
-        })
-    }
-}
-
-pub fn proc_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    let PartialUncheckedInput {
-        model_module,
-        data,
-        selection,
-    } = parse_macro_input!(input as PartialUncheckedInput);
-
-    let fields = data
-        .fields
-        .iter()
-        .filter(|f| selection.iter().any(|s| s == f.ident.as_ref().unwrap()))
-        .map(|field| {
-            let attrs = &field.attrs;
-            let ident = &field.ident;
-            let ty = &field.ty;
-
-            let specta_attrs = cfg!(feature = "specta").then(|| quote!(#[specta(optional)]));
-
-            quote! {
-                #(#attrs)*
-                #specta_attrs
-                pub #ident: Option<#ty>
-            }
-        });
-
-    let specta_attrs = cfg!(feature = "specta").then(|| {
-        quote! {
-            #[derive(::prisma_client_rust::specta::Type)]
-            #[specta(crate = "prisma_client_rust::specta")]
-        }
-    });
-
-    let ident = &data.ident;
-
-    let selection = selection.iter().collect::<Vec<_>>();
-
-    quote! {
-        #[derive(serde::Deserialize)]
-        #specta_attrs
-        #[allow(unused)]
-        pub struct #ident {
-           #(#fields),*
-        }
-
-        impl #ident {
-            pub fn to_params(self) -> Vec<#model_module::UncheckedSetParam> {
-                [
-                    #(self.#selection.map(#model_module::#selection::set)),*
-                ].into_iter().flatten().collect()
-            }
-        }
-    }
-    .into()
-}
diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs
index 2962f7ef..7137736f 100644
--- a/crates/sdk/src/lib.rs
+++ b/crates/sdk/src/lib.rs
@@ -34,7 +34,10 @@ pub mod prelude {
     pub use super::{args::*, prisma::*, *};
     pub use proc_macro2::*;
     pub use quote::*;
-    pub use syn::Ident;
+    pub use syn::{
+        parse::{Parse, ParseStream},
+        Ident,
+    };
 
     pub fn ident(name: &str) -> Ident {
         format_ident!("{name}")
diff --git a/examples/rspc/bindings.ts b/examples/rspc/bindings.ts
index 66df900e..a8c9e3ed 100644
--- a/examples/rspc/bindings.ts
+++ b/examples/rspc/bindings.ts
@@ -6,13 +6,13 @@ export type Procedures = {
         { key: "posts", input: never, result: Post[] } | 
         { key: "userNames", input: never, result: { displayName: string }[] } | 
         { key: "users", input: never, result: User[] } | 
-        { key: "usersWithPosts", input: never, result: { id: string; displayName: string; posts: { id: string; content: string; user: { id: string; displayName: string } }[] }[] },
+        { key: "usersWithPosts", input: never, result: { id: string; displayName: string; posts: Post[] }[] },
     mutations: never,
     subscriptions: never
 };
 
+export type NamedUserInclude = { id: string; displayName: string; posts: Post[] }
+
 export type Post = { id: string; content: string; userId: string }
 
 export type User = { id: string; displayName: string }
-
-export type NamedUserInclude = { id: string; displayName: string; posts: Post[] }


Test patch:
diff --git a/integration-tests/tests/order.rs b/integration-tests/tests/order.rs
index eb93a1c4..082c8c40 100644
--- a/integration-tests/tests/order.rs
+++ b/integration-tests/tests/order.rs
@@ -1,5 +1,3 @@
-use prisma_client_rust::or;
-
 use crate::{db::*, utils::*};
 
 #[tokio::test]
@@ -64,7 +62,7 @@ async fn relation() -> TestResult {
             .exec()
             .await?;
 
-        let post = client
+        client
             .post()
             .create(
                 format!("Some Post {index}"),
diff --git a/integration-tests/tests/specta.rs b/integration-tests/tests/specta.rs
index f586158f..c0c7e0a9 100644
--- a/integration-tests/tests/specta.rs
+++ b/integration-tests/tests/specta.rs
@@ -11,7 +11,7 @@ fn ts_export() {
 
     let ts = specta::ts::export::<user_include::Data>(&Default::default()).unwrap();
 
-    assert_eq!(ts, "export type UserInclude = { id: string; name: string; email: string | null; createdAt: string; underscored_: number | null; posts: Post[] }");
+    assert_eq!(ts, "export type UserInclude = { id: string; name: string; email: string | null; createdAt: string; posts: Post[]; underscored_: number | null }");
 
     user::select!(user_select { id posts });