Skip to content

Commit 09e92e1

Browse files
committed
feat: add more enum context
1 parent 95f4d69 commit 09e92e1

File tree

8 files changed

+116
-21
lines changed

8 files changed

+116
-21
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,18 @@ The `--entities-module` (`-e`) option is **optional**. When omitted, the module
146146

147147
Views are automatically detected via the `#[sqlx_gen(kind = "view")]` annotation — write methods (`insert`, `update`, `delete`) are never generated for views even if requested.
148148

149+
### Pool field visibility
150+
151+
By default, the `pool` field in generated repositories is private. Use `--pool-visibility` (`-p`) to change it:
152+
153+
```sh
154+
# Public pool field
155+
sqlx-gen generate crud -f src/models/users.rs -d postgres -m '*' -p pub
156+
157+
# Crate-visible pool field
158+
sqlx-gen generate crud -f src/models/users.rs -d postgres -m '*' -p 'pub(crate)'
159+
```
160+
149161
### Compile-time checked macros
150162

151163
By default, the CRUD generator uses `sqlx::query_as::<_, T>()` with `.bind()` chains (runtime). Pass `--query-macro` (`-q`) to generate `sqlx::query_as!()` / `sqlx::query!()` macros instead, which are checked at compile time (requires `DATABASE_URL` at build time).
@@ -180,6 +192,7 @@ Generated files are automatically formatted with `rustfmt`. The Rust edition is
180192
| `--output-dir` | `-o` | Output directory | `src/crud` |
181193
| `--methods` | `-m` | Methods to generate (comma-separated): `*`, `get_all`, `paginate`, `get`, `insert`, `update`, `delete` | required |
182194
| `--query-macro` | `-q` | Use `sqlx::query_as!()` macros (compile-time checked) | `false` |
195+
| `--pool-visibility` | `-p` | Visibility of the `pool` field: `private`, `pub`, `pub(crate)` | `private` |
183196
| `--dry-run` | `-n` | Print to stdout, don't write files | `false` |
184197

185198
## Example Output

crates/sqlx_gen/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sqlx-gen"
3-
version = "0.4.3"
3+
version = "0.4.4"
44
edition = "2021"
55
description = "Generate Rust structs from database schema introspection"
66
license = "MIT"

crates/sqlx_gen/src/cli.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ pub struct CrudArgs {
132132
#[arg(short = 'q', long)]
133133
pub query_macro: bool,
134134

135+
/// Visibility of the pool field in generated repository structs: private, pub, pub(crate)
136+
#[arg(short = 'p', long, default_value = "private")]
137+
pub pool_visibility: PoolVisibility,
138+
135139
/// Print to stdout without writing files
136140
#[arg(short = 'n', long)]
137141
pub dry_run: bool,
@@ -191,6 +195,30 @@ pub enum DatabaseKind {
191195
Sqlite,
192196
}
193197

198+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
199+
pub enum PoolVisibility {
200+
#[default]
201+
Private,
202+
Pub,
203+
PubCrate,
204+
}
205+
206+
impl std::str::FromStr for PoolVisibility {
207+
type Err = String;
208+
209+
fn from_str(s: &str) -> Result<Self, Self::Err> {
210+
match s {
211+
"private" => Ok(Self::Private),
212+
"pub" => Ok(Self::Pub),
213+
"pub(crate)" => Ok(Self::PubCrate),
214+
other => Err(format!(
215+
"Unknown pool visibility '{}'. Expected: private, pub, pub(crate)",
216+
other
217+
)),
218+
}
219+
}
220+
}
221+
194222
/// Which CRUD methods to generate. All fields default to `false`.
195223
/// Use `Methods::from_list` to parse from CLI input.
196224
#[derive(Debug, Clone, Default)]

crates/sqlx_gen/src/codegen/crud_gen.rs

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::collections::BTreeSet;
33
use proc_macro2::TokenStream;
44
use quote::{format_ident, quote};
55

6-
use crate::cli::{DatabaseKind, Methods};
6+
use crate::cli::{DatabaseKind, Methods, PoolVisibility};
77
use crate::codegen::entity_parser::{ParsedEntity, ParsedField};
88

99
pub fn generate_crud_from_parsed(
@@ -12,6 +12,7 @@ pub fn generate_crud_from_parsed(
1212
entity_module_path: &str,
1313
methods: &Methods,
1414
query_macro: bool,
15+
pool_visibility: PoolVisibility,
1516
) -> (TokenStream, BTreeSet<String>) {
1617
let mut imports = BTreeSet::new();
1718

@@ -502,11 +503,17 @@ pub fn generate_crud_from_parsed(
502503
method_tokens.push(method);
503504
}
504505

506+
let pool_vis: TokenStream = match pool_visibility {
507+
PoolVisibility::Private => quote! {},
508+
PoolVisibility::Pub => quote! { pub },
509+
PoolVisibility::PubCrate => quote! { pub(crate) },
510+
};
511+
505512
let tokens = quote! {
506513
#(#param_structs)*
507514

508515
pub struct #repo_ident {
509-
pool: #pool_type,
516+
#pool_vis pool: #pool_type,
510517
}
511518

512519
impl #repo_ident {
@@ -742,18 +749,18 @@ mod tests {
742749

743750
fn gen(entity: &ParsedEntity, db: DatabaseKind) -> String {
744751
let skip = Methods::all();
745-
let (tokens, _) = generate_crud_from_parsed(entity, db, "crate::models::users", &skip, false);
752+
let (tokens, _) = generate_crud_from_parsed(entity, db, "crate::models::users", &skip, false, PoolVisibility::Private);
746753
parse_and_format(&tokens)
747754
}
748755

749756
fn gen_macro(entity: &ParsedEntity, db: DatabaseKind) -> String {
750757
let skip = Methods::all();
751-
let (tokens, _) = generate_crud_from_parsed(entity, db, "crate::models::users", &skip, true);
758+
let (tokens, _) = generate_crud_from_parsed(entity, db, "crate::models::users", &skip, true, PoolVisibility::Private);
752759
parse_and_format(&tokens)
753760
}
754761

755762
fn gen_with_methods(entity: &ParsedEntity, db: DatabaseKind, methods: &Methods) -> String {
756-
let (tokens, _) = generate_crud_from_parsed(entity, db, "crate::models::users", methods, false);
763+
let (tokens, _) = generate_crud_from_parsed(entity, db, "crate::models::users", methods, false, PoolVisibility::Private);
757764
parse_and_format(&tokens)
758765
}
759766

@@ -777,6 +784,30 @@ mod tests {
777784
assert!(code.contains("pool: sqlx::PgPool") || code.contains("pool: sqlx :: PgPool"));
778785
}
779786

787+
#[test]
788+
fn test_repo_pool_field_pub() {
789+
let skip = Methods::all();
790+
let (tokens, _) = generate_crud_from_parsed(&standard_entity(), DatabaseKind::Postgres, "crate::models::users", &skip, false, PoolVisibility::Pub);
791+
let code = parse_and_format(&tokens);
792+
assert!(code.contains("pub pool: sqlx::PgPool") || code.contains("pub pool: sqlx :: PgPool"));
793+
}
794+
795+
#[test]
796+
fn test_repo_pool_field_pub_crate() {
797+
let skip = Methods::all();
798+
let (tokens, _) = generate_crud_from_parsed(&standard_entity(), DatabaseKind::Postgres, "crate::models::users", &skip, false, PoolVisibility::PubCrate);
799+
let code = parse_and_format(&tokens);
800+
assert!(code.contains("pub(crate) pool: sqlx::PgPool") || code.contains("pub(crate) pool: sqlx :: PgPool"));
801+
}
802+
803+
#[test]
804+
fn test_repo_pool_field_private() {
805+
let code = gen(&standard_entity(), DatabaseKind::Postgres);
806+
// Should NOT have `pub pool` or `pub(crate) pool`
807+
assert!(!code.contains("pub pool"));
808+
assert!(!code.contains("pub(crate) pool"));
809+
}
810+
780811
#[test]
781812
fn test_repo_pool_field_mysql() {
782813
let code = gen(&standard_entity(), DatabaseKind::Mysql);
@@ -1117,14 +1148,14 @@ mod tests {
11171148
#[test]
11181149
fn test_no_pool_import() {
11191150
let skip = Methods::all();
1120-
let (_, imports) = generate_crud_from_parsed(&standard_entity(), DatabaseKind::Postgres, "crate::models::users", &skip, false);
1151+
let (_, imports) = generate_crud_from_parsed(&standard_entity(), DatabaseKind::Postgres, "crate::models::users", &skip, false, PoolVisibility::Private);
11211152
assert!(!imports.iter().any(|i| i.contains("PgPool")));
11221153
}
11231154

11241155
#[test]
11251156
fn test_imports_contain_entity() {
11261157
let skip = Methods::all();
1127-
let (_, imports) = generate_crud_from_parsed(&standard_entity(), DatabaseKind::Postgres, "crate::models::users", &skip, false);
1158+
let (_, imports) = generate_crud_from_parsed(&standard_entity(), DatabaseKind::Postgres, "crate::models::users", &skip, false, PoolVisibility::Private);
11281159
assert!(imports.iter().any(|i| i.contains("crate::models::users::Users")));
11291160
}
11301161

@@ -1214,15 +1245,15 @@ mod tests {
12141245
],
12151246
};
12161247
let skip = Methods::all();
1217-
let (_, imports) = generate_crud_from_parsed(&entity, DatabaseKind::Postgres, "crate::models::users", &skip, false);
1248+
let (_, imports) = generate_crud_from_parsed(&entity, DatabaseKind::Postgres, "crate::models::users", &skip, false, PoolVisibility::Private);
12181249
assert!(imports.iter().any(|i| i.contains("chrono")));
12191250
assert!(imports.iter().any(|i| i.contains("uuid")));
12201251
}
12211252

12221253
#[test]
12231254
fn test_entity_imports_empty_when_no_imports() {
12241255
let skip = Methods::all();
1225-
let (_, imports) = generate_crud_from_parsed(&standard_entity(), DatabaseKind::Postgres, "crate::models::users", &skip, false);
1256+
let (_, imports) = generate_crud_from_parsed(&standard_entity(), DatabaseKind::Postgres, "crate::models::users", &skip, false, PoolVisibility::Private);
12261257
// Should only have pool + entity imports, no chrono/uuid
12271258
assert!(!imports.iter().any(|i| i.contains("chrono")));
12281259
assert!(!imports.iter().any(|i| i.contains("uuid")));
@@ -1343,7 +1374,7 @@ mod tests {
13431374

13441375
fn gen_macro_array(entity: &ParsedEntity, db: DatabaseKind) -> String {
13451376
let skip = Methods::all();
1346-
let (tokens, _) = generate_crud_from_parsed(entity, db, "crate::models::agent_connector", &skip, true);
1377+
let (tokens, _) = generate_crud_from_parsed(entity, db, "crate::models::agent_connector", &skip, true, PoolVisibility::Private);
13471378
parse_and_format(&tokens)
13481379
}
13491380

@@ -1426,7 +1457,7 @@ mod tests {
14261457
#[test]
14271458
fn test_sql_enum_macro_uses_runtime() {
14281459
let skip = Methods::all();
1429-
let (tokens, _) = generate_crud_from_parsed(&entity_with_sql_enum(), DatabaseKind::Postgres, "crate::models::task", &skip, true);
1460+
let (tokens, _) = generate_crud_from_parsed(&entity_with_sql_enum(), DatabaseKind::Postgres, "crate::models::task", &skip, true, PoolVisibility::Private);
14301461
let code = parse_and_format(&tokens);
14311462
// SELECT queries should use runtime query_as, not macro
14321463
assert!(code.contains("query_as::<"));
@@ -1436,7 +1467,7 @@ mod tests {
14361467
#[test]
14371468
fn test_sql_enum_macro_delete_still_uses_macro() {
14381469
let skip = Methods::all();
1439-
let (tokens, _) = generate_crud_from_parsed(&entity_with_sql_enum(), DatabaseKind::Postgres, "crate::models::task", &skip, true);
1470+
let (tokens, _) = generate_crud_from_parsed(&entity_with_sql_enum(), DatabaseKind::Postgres, "crate::models::task", &skip, true, PoolVisibility::Private);
14401471
let code = parse_and_format(&tokens);
14411472
// DELETE still uses query! macro
14421473
assert!(code.contains("query!"));
@@ -1489,15 +1520,15 @@ mod tests {
14891520
#[test]
14901521
fn test_vec_string_macro_insert_uses_as_slice() {
14911522
let skip = Methods::all();
1492-
let (tokens, _) = generate_crud_from_parsed(&entity_with_vec_string(), DatabaseKind::Postgres, "crate::models::prompt_history", &skip, true);
1523+
let (tokens, _) = generate_crud_from_parsed(&entity_with_vec_string(), DatabaseKind::Postgres, "crate::models::prompt_history", &skip, true, PoolVisibility::Private);
14931524
let code = parse_and_format(&tokens);
14941525
assert!(code.contains("as_slice()"));
14951526
}
14961527

14971528
#[test]
14981529
fn test_vec_string_macro_update_uses_as_slice() {
14991530
let skip = Methods::all();
1500-
let (tokens, _) = generate_crud_from_parsed(&entity_with_vec_string(), DatabaseKind::Postgres, "crate::models::prompt_history", &skip, true);
1531+
let (tokens, _) = generate_crud_from_parsed(&entity_with_vec_string(), DatabaseKind::Postgres, "crate::models::prompt_history", &skip, true, PoolVisibility::Private);
15011532
let code = parse_and_format(&tokens);
15021533
// Should have as_slice() for both insert and update
15031534
let count = code.matches("as_slice()").count();
@@ -1507,7 +1538,7 @@ mod tests {
15071538
#[test]
15081539
fn test_vec_string_non_macro_no_as_slice() {
15091540
let skip = Methods::all();
1510-
let (tokens, _) = generate_crud_from_parsed(&entity_with_vec_string(), DatabaseKind::Postgres, "crate::models::prompt_history", &skip, false);
1541+
let (tokens, _) = generate_crud_from_parsed(&entity_with_vec_string(), DatabaseKind::Postgres, "crate::models::prompt_history", &skip, false, PoolVisibility::Private);
15111542
let code = parse_and_format(&tokens);
15121543
// Runtime mode uses .bind() so no as_slice needed
15131544
assert!(!code.contains("as_slice()"));
@@ -1530,7 +1561,7 @@ mod tests {
15301561
"#;
15311562
let entity = parse_entity_source(source).unwrap();
15321563
let skip = Methods::all();
1533-
let (tokens, _) = generate_crud_from_parsed(&entity, DatabaseKind::Postgres, "crate::models::prompt_history", &skip, true);
1564+
let (tokens, _) = generate_crud_from_parsed(&entity, DatabaseKind::Postgres, "crate::models::prompt_history", &skip, true, PoolVisibility::Private);
15341565
let code = parse_and_format(&tokens);
15351566
assert!(code.contains("as_slice()"), "Expected as_slice() in generated code:\n{}", code);
15361567
}

crates/sqlx_gen/src/codegen/enum_gen.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,13 @@ pub fn generate_enum(
8585
quote! {}
8686
};
8787

88+
let schema_name_str = &enum_info.schema_name;
89+
let enum_name_str = &enum_info.name;
90+
8891
let tokens = quote! {
8992
#[doc = #doc]
9093
#[derive(#(#derive_tokens),*)]
91-
#[sqlx_gen(kind = "enum")]
94+
#[sqlx_gen(kind = "enum", schema = #schema_name_str, name = #enum_name_str)]
9295
#type_attr
9396
pub enum #enum_name {
9497
#(#variants)*
@@ -152,6 +155,25 @@ mod tests {
152155
assert!(code.contains("Enum: public.status"));
153156
}
154157

158+
#[test]
159+
fn test_sqlx_gen_attr_has_schema_and_name() {
160+
let e = make_enum("status", vec!["a"]);
161+
let code = gen(&e, DatabaseKind::Postgres);
162+
assert!(code.contains("sqlx_gen(kind = \"enum\", schema = \"public\", name = \"status\")"));
163+
}
164+
165+
#[test]
166+
fn test_sqlx_gen_attr_non_public_schema() {
167+
let e = EnumInfo {
168+
schema_name: "auth".to_string(),
169+
name: "role".to_string(),
170+
variants: vec!["admin".to_string(), "user".to_string()],
171+
default_variant: None,
172+
};
173+
let code = gen(&e, DatabaseKind::Postgres);
174+
assert!(code.contains("sqlx_gen(kind = \"enum\", schema = \"auth\", name = \"role\")"));
175+
}
176+
155177
// --- sqlx attributes ---
156178

157179
#[test]

crates/sqlx_gen/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ fn run_crud(args: CrudArgs) -> Result<()> {
139139
&entities_module,
140140
&methods,
141141
args.query_macro,
142+
args.pool_visibility,
142143
);
143144

144145
let code = codegen::format_tokens_with_imports(&tokens, &imports);

crates/sqlx_gen_macros/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sqlx-gen-macros"
3-
version = "0.4.3"
3+
version = "0.4.4"
44
edition = "2021"
55
description = "No-op attribute macros for sqlx-gen generated code"
66
license = "MIT"

0 commit comments

Comments
 (0)