Skip to main content

prax_cli/
cli.rs

1//! CLI argument definitions using clap.
2
3use clap::{Args, Parser, Subcommand, ValueEnum};
4use std::path::PathBuf;
5
6/// Prax CLI - A modern ORM for Rust
7#[derive(Parser, Debug)]
8#[command(name = "prax")]
9#[command(author = "Joseph R. Quinn")]
10#[command(version)]
11#[command(about = "Prax CLI - A modern ORM for Rust", long_about = None)]
12#[command(propagate_version = true)]
13pub struct Cli {
14    /// Subcommand to execute
15    #[command(subcommand)]
16    pub command: Command,
17}
18
19/// Available CLI commands
20#[derive(Subcommand, Debug)]
21pub enum Command {
22    /// Initialize a new Prax project
23    Init(InitArgs),
24
25    /// Generate Rust client code from schema
26    Generate(GenerateArgs),
27
28    /// Schema validation and formatting
29    Validate(ValidateArgs),
30
31    /// Format schema file
32    Format(FormatArgs),
33
34    /// Database migration commands
35    Migrate(MigrateArgs),
36
37    /// Direct database operations
38    Db(DbArgs),
39
40    /// Import schema from Prisma or Diesel
41    Import(ImportArgs),
42
43    /// Display version information
44    Version,
45}
46
47// =============================================================================
48// Init Command
49// =============================================================================
50
51/// Arguments for the `init` command
52#[derive(Args, Debug)]
53pub struct InitArgs {
54    /// Path to initialize the project (defaults to current directory)
55    #[arg(default_value = ".")]
56    pub path: PathBuf,
57
58    /// Database provider to use
59    #[arg(short, long, default_value = "postgresql")]
60    pub provider: DatabaseProvider,
61
62    /// Database connection URL
63    #[arg(short, long)]
64    pub url: Option<String>,
65
66    /// Skip generating example schema
67    #[arg(long)]
68    pub no_example: bool,
69
70    /// Accept all defaults without prompting
71    #[arg(short, long)]
72    pub yes: bool,
73}
74
75/// Supported database providers
76#[derive(ValueEnum, Debug, Clone, Copy, Default)]
77pub enum DatabaseProvider {
78    #[default]
79    Postgresql,
80    Mysql,
81    Sqlite,
82}
83
84impl std::fmt::Display for DatabaseProvider {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        match self {
87            DatabaseProvider::Postgresql => write!(f, "postgresql"),
88            DatabaseProvider::Mysql => write!(f, "mysql"),
89            DatabaseProvider::Sqlite => write!(f, "sqlite"),
90        }
91    }
92}
93
94// =============================================================================
95// Generate Command
96// =============================================================================
97
98/// Arguments for the `generate` command
99#[derive(Args, Debug)]
100pub struct GenerateArgs {
101    /// Path to schema file
102    #[arg(short, long)]
103    pub schema: Option<PathBuf>,
104
105    /// Output directory for generated code
106    #[arg(short, long)]
107    pub output: Option<PathBuf>,
108
109    /// Features to generate (e.g., serde, graphql)
110    #[arg(short, long, value_delimiter = ',')]
111    pub features: Vec<String>,
112
113    /// Watch for schema changes and regenerate (not yet implemented — runs once)
114    #[arg(short, long)]
115    pub watch: bool,
116}
117
118// =============================================================================
119// Validate Command
120// =============================================================================
121
122/// Arguments for the `validate` command
123#[derive(Args, Debug)]
124pub struct ValidateArgs {
125    /// Path to schema file
126    #[arg(short, long)]
127    pub schema: Option<PathBuf>,
128}
129
130// =============================================================================
131// Format Command
132// =============================================================================
133
134/// Arguments for the `format` command.
135///
136/// Formatting is syntactic-only and per-file: it does not validate
137/// relations or types. Use `prax validate` to type-check.
138#[derive(Args, Debug)]
139pub struct FormatArgs {
140    /// Path to schema file
141    #[arg(short, long)]
142    pub schema: Option<PathBuf>,
143
144    /// Check formatting without writing changes (does not validate)
145    #[arg(short, long)]
146    pub check: bool,
147}
148
149// =============================================================================
150// Migrate Command
151// =============================================================================
152
153/// Arguments for the `migrate` command
154#[derive(Args, Debug)]
155pub struct MigrateArgs {
156    #[command(subcommand)]
157    pub command: MigrateSubcommand,
158}
159
160/// Migrate subcommands
161#[derive(Subcommand, Debug)]
162pub enum MigrateSubcommand {
163    /// Create and apply migrations during development
164    Dev(MigrateDevArgs),
165
166    /// Deploy pending migrations to production
167    Deploy,
168
169    /// Reset database and re-apply all migrations
170    Reset(MigrateResetArgs),
171
172    /// Show migration status
173    Status,
174
175    /// Resolve migration issues
176    Resolve(MigrateResolveArgs),
177
178    /// Generate migration diff without applying
179    Diff(MigrateDiffArgs),
180
181    /// Rollback the last applied migration
182    Rollback(MigrateRollbackArgs),
183
184    /// View migration history
185    History(MigrateHistoryArgs),
186}
187
188/// Arguments for `migrate dev`
189#[derive(Args, Debug)]
190pub struct MigrateDevArgs {
191    /// Name for the migration
192    #[arg(short, long)]
193    pub name: Option<String>,
194
195    /// Create migration without applying
196    #[arg(long)]
197    pub create_only: bool,
198
199    /// Skip seed after migration
200    #[arg(long)]
201    pub skip_seed: bool,
202
203    /// Path to schema file
204    #[arg(short, long)]
205    pub schema: Option<PathBuf>,
206
207    /// Emit destructive statements (DROP TABLE/COLUMN/TYPE). Default is
208    /// additive-only: drops are omitted so a stale schema never silently
209    /// destroys data.
210    #[arg(long)]
211    pub allow_destructive: bool,
212}
213
214/// Arguments for `migrate reset`
215#[derive(Args, Debug)]
216pub struct MigrateResetArgs {
217    /// Skip confirmation prompt
218    #[arg(short, long)]
219    pub force: bool,
220
221    /// Run seed after reset
222    #[arg(long)]
223    pub seed: bool,
224
225    /// Skip applying migrations (just reset)
226    #[arg(long)]
227    pub skip_migrations: bool,
228}
229
230/// Arguments for `migrate resolve`
231#[derive(Args, Debug)]
232pub struct MigrateResolveArgs {
233    /// Name of the migration to resolve
234    pub migration: String,
235
236    /// Mark migration as applied
237    #[arg(long)]
238    pub applied: bool,
239
240    /// Mark migration as rolled back
241    #[arg(long)]
242    pub rolled_back: bool,
243}
244
245/// Arguments for `migrate diff`
246#[derive(Args, Debug)]
247pub struct MigrateDiffArgs {
248    /// Path to schema file
249    #[arg(short, long)]
250    pub schema: Option<PathBuf>,
251
252    /// Output path for generated SQL
253    #[arg(short, long)]
254    pub output: Option<PathBuf>,
255
256    /// Compare against a specific migration
257    #[arg(long)]
258    pub from_migration: Option<String>,
259
260    /// Emit destructive statements (DROP TABLE/COLUMN/TYPE). Default is
261    /// additive-only: drops are omitted.
262    #[arg(long)]
263    pub allow_destructive: bool,
264}
265
266/// Arguments for `migrate rollback`
267#[derive(Args, Debug)]
268pub struct MigrateRollbackArgs {
269    /// Reason for rollback
270    #[arg(long)]
271    pub reason: Option<String>,
272
273    /// User performing the rollback
274    #[arg(long)]
275    pub user: Option<String>,
276
277    /// Rollback to a specific migration
278    #[arg(long)]
279    pub to: Option<String>,
280}
281
282/// Arguments for `migrate history`
283#[derive(Args, Debug)]
284pub struct MigrateHistoryArgs {
285    /// Show history for a specific migration
286    #[arg(long)]
287    pub migration: Option<String>,
288}
289
290// =============================================================================
291// Db Command
292// =============================================================================
293
294/// Arguments for the `db` command
295#[derive(Args, Debug)]
296pub struct DbArgs {
297    #[command(subcommand)]
298    pub command: DbSubcommand,
299}
300
301/// Db subcommands
302#[derive(Subcommand, Debug)]
303pub enum DbSubcommand {
304    /// Push schema to database without migrations
305    Push(DbPushArgs),
306
307    /// Introspect database and generate schema
308    Pull(DbPullArgs),
309
310    /// Seed database with initial data
311    Seed(DbSeedArgs),
312
313    /// Execute raw SQL
314    Execute(DbExecuteArgs),
315}
316
317/// Arguments for `db push`
318#[derive(Args, Debug)]
319pub struct DbPushArgs {
320    /// Path to schema file
321    #[arg(short, long)]
322    pub schema: Option<PathBuf>,
323
324    /// Accept data loss from destructive changes
325    #[arg(long)]
326    pub accept_data_loss: bool,
327
328    /// Skip confirmation prompts
329    #[arg(short, long)]
330    pub force: bool,
331
332    /// Reset database before push
333    #[arg(long)]
334    pub reset: bool,
335}
336
337/// Arguments for `db pull`
338#[derive(Args, Debug)]
339pub struct DbPullArgs {
340    /// Output path for generated schema
341    #[arg(short, long)]
342    pub output: Option<PathBuf>,
343
344    /// Overwrite existing schema without prompting
345    #[arg(short, long)]
346    pub force: bool,
347
348    /// Include views in introspection
349    #[arg(long)]
350    pub include_views: bool,
351
352    /// Include materialized views in introspection
353    #[arg(long)]
354    pub include_materialized_views: bool,
355
356    /// Schema/namespace to introspect (default: public for PostgreSQL, dbo for MSSQL)
357    #[arg(long)]
358    pub schema: Option<String>,
359
360    /// Filter tables by pattern (glob-style, e.g., "user*")
361    #[arg(long)]
362    pub tables: Option<String>,
363
364    /// Exclude tables by pattern (glob-style, e.g., "_prisma*")
365    #[arg(long)]
366    pub exclude: Option<String>,
367
368    /// Print schema to stdout instead of writing to file
369    #[arg(long)]
370    pub print: bool,
371
372    /// Output format
373    #[arg(long, default_value = "prax")]
374    pub format: OutputFormat,
375
376    /// Number of documents to sample for MongoDB schema inference
377    #[arg(long, default_value = "100")]
378    pub sample_size: usize,
379
380    /// Include column comments in schema
381    #[arg(long)]
382    pub comments: bool,
383}
384
385/// Output format for schema introspection
386#[derive(ValueEnum, Debug, Clone, Copy, Default)]
387pub enum OutputFormat {
388    /// Prax schema format (.prax)
389    #[default]
390    Prax,
391    /// JSON format
392    Json,
393    /// SQL DDL format
394    Sql,
395}
396
397/// Arguments for `db seed`
398#[derive(Args, Debug)]
399pub struct DbSeedArgs {
400    /// Path to seed file
401    #[arg(short, long)]
402    pub seed_file: Option<PathBuf>,
403
404    /// Reset database before seeding
405    #[arg(long)]
406    pub reset: bool,
407
408    /// Environment to run seed for (development, staging, production)
409    #[arg(short, long, default_value = "development")]
410    pub environment: String,
411
412    /// Force seeding even if environment config says not to
413    #[arg(short, long)]
414    pub force: bool,
415}
416
417/// Arguments for `db execute`
418#[derive(Args, Debug)]
419pub struct DbExecuteArgs {
420    /// SQL to execute
421    #[arg(short, long)]
422    pub sql: Option<String>,
423
424    /// Path to SQL file
425    #[arg(short, long)]
426    pub file: Option<PathBuf>,
427
428    /// Read SQL from stdin
429    #[arg(long)]
430    pub stdin: bool,
431
432    /// Skip confirmation prompt
433    #[arg(short = 'y', long)]
434    pub force: bool,
435}
436
437// =============================================================================
438// Import Command
439// =============================================================================
440
441/// Arguments for the `import` command
442#[derive(Args, Debug)]
443pub struct ImportArgs {
444    /// Source ORM to import from
445    #[arg(long, value_enum)]
446    pub from: ImportSource,
447
448    /// Input schema file path
449    #[arg(short, long)]
450    pub input: PathBuf,
451
452    /// Output Prax schema file path
453    #[arg(short, long)]
454    pub output: Option<PathBuf>,
455
456    /// Database provider for the imported schema
457    #[arg(short = 'P', long)]
458    pub provider: Option<DatabaseProvider>,
459
460    /// Database connection URL for the imported schema
461    #[arg(short, long)]
462    pub url: Option<String>,
463
464    /// Print to stdout instead of writing to file
465    #[arg(long)]
466    pub print: bool,
467
468    /// Overwrite existing output file without prompting
469    #[arg(short, long)]
470    pub force: bool,
471}
472
473/// Source ORM for import
474#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
475pub enum ImportSource {
476    /// Prisma schema (.prisma files)
477    Prisma,
478    /// Diesel schema (schema.rs files with table! macros)
479    Diesel,
480    /// SeaORM entity (entity files with DeriveEntityModel)
481    SeaOrm,
482}