faucet_cli/cli.rs
1//! Argument parser shared by `main.rs` and the integration tests.
2
3use clap::{Parser, Subcommand};
4use std::path::PathBuf;
5
6/// `faucet` — config-driven runner for faucet-stream pipelines.
7#[derive(Debug, Parser)]
8#[command(name = "faucet", version, about, long_about = None)]
9pub struct Cli {
10 /// Override the global log level (also honors `FAUCET_LOG`).
11 #[arg(long, global = true, env = "FAUCET_LOG", default_value = "info")]
12 pub log_level: String,
13
14 #[command(subcommand)]
15 pub command: Command,
16}
17
18/// Top-level subcommands.
19#[derive(Debug, Subcommand)]
20pub enum Command {
21 /// Execute a pipeline config end-to-end.
22 Run(RunArgs),
23 /// Bulk-snapshot a database table, then stream CDC from a position captured
24 /// before the snapshot (a true mirror with `write_mode: upsert`).
25 /// Long-running when `replication.continuous` is true (Ctrl-C / SIGTERM to stop).
26 Replicate(ReplicateArgs),
27 /// Parse + validate a pipeline config without running it.
28 Validate(ValidateArgs),
29 /// Print the JSON Schema for a specific connector.
30 Schema(SchemaArgs),
31 /// List every compiled-in source, sink, and transform with a one-line description.
32 List,
33 /// Run only the source side and print records to stdout (uses the stdout sink).
34 Preview(PreviewArgs),
35 /// Scaffold a starter `pipeline.yaml` to disk.
36 Init(InitArgs),
37 /// Probe every connector in a config (auth / network / permissions) and
38 /// print a green/red checklist. Exits non-zero if any probe fails.
39 Doctor(DoctorArgs),
40 /// Run fixture-based offline pipeline tests from one or more spec files.
41 /// No real source or sink is touched. Exits non-zero if any case fails.
42 Test(TestArgs),
43 /// Inspect, replay, or discard dead-letter-queue envelopes written by a
44 /// pipeline's `dlq:` sink.
45 Dlq(DlqArgs),
46 /// Validate a config's `contract:` block and print a summary, or export
47 /// it in a machine-readable format (`--export`).
48 #[cfg(feature = "contract")]
49 Contract(ContractArgs),
50 /// Validate a config's `masking:` block and print which rules apply to
51 /// each destination sink.
52 #[cfg(feature = "masking")]
53 Masking(MaskingArgs),
54 /// Run a pipeline on a cron schedule (long-running; Ctrl-C / SIGTERM to stop).
55 #[cfg(feature = "schedule")]
56 Schedule(ScheduleArgs),
57 /// Run a long-running HTTP control plane (submit / poll / cancel pipeline runs).
58 #[cfg(feature = "serve")]
59 Serve(ServeArgs),
60 /// Send a synthetic notification through a config's `notifications:` rules
61 /// to validate channel setup end-to-end (no pipeline runs).
62 #[cfg(feature = "notify")]
63 Notify(NotifyArgs),
64 /// Browse the Data Movement Catalog accumulated by a config's `catalog:`
65 /// store — datasets, schema timelines, volume/freshness, lineage.
66 #[cfg(feature = "catalog")]
67 Catalog(CatalogArgs),
68}
69
70/// `faucet catalog` arguments.
71#[cfg(feature = "catalog")]
72#[derive(Debug, Parser)]
73pub struct CatalogArgs {
74 #[command(subcommand)]
75 pub command: CatalogCommand,
76}
77
78/// `faucet catalog` subcommands.
79#[cfg(feature = "catalog")]
80#[derive(Debug, Subcommand)]
81pub enum CatalogCommand {
82 /// List every catalogued dataset (newest activity first).
83 Datasets(CatalogDatasetsArgs),
84 /// Show one dataset's detail: schema timeline, volume points, edges.
85 Show(CatalogShowArgs),
86 /// Print the dataset lineage graph (optionally rooted at a dataset).
87 Lineage(CatalogLineageArgs),
88}
89
90/// Shared config-loading flags for the `faucet catalog` subcommands.
91#[cfg(feature = "catalog")]
92#[derive(Debug, Parser)]
93pub struct CatalogConfigArgs {
94 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config with a
95 /// `catalog:` block naming the store. If omitted, auto-discover
96 /// `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
97 #[arg(long)]
98 pub config: Option<PathBuf>,
99 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
100 /// Defaults to `.env` in cwd if present.
101 #[arg(long, conflicts_with = "no_env_file")]
102 pub env_file: Option<PathBuf>,
103 /// Skip auto-loading `.env` from cwd.
104 #[arg(long)]
105 pub no_env_file: bool,
106 /// Select a named overlay from the config's `profiles:` block.
107 /// Overrides the `FAUCET_PROFILE` env var.
108 #[arg(long, env = "FAUCET_PROFILE")]
109 pub profile: Option<String>,
110 /// Emit machine-readable JSON instead of the human summary.
111 #[arg(long)]
112 pub json: bool,
113}
114
115/// `faucet catalog datasets` arguments.
116#[cfg(feature = "catalog")]
117#[derive(Debug, Parser)]
118pub struct CatalogDatasetsArgs {
119 #[command(flatten)]
120 pub common: CatalogConfigArgs,
121 /// Only datasets of this connector kind (e.g. `postgres`, `csv`).
122 #[arg(long)]
123 pub kind: Option<String>,
124 /// Case-insensitive substring match on the dataset URI.
125 #[arg(long)]
126 pub q: Option<String>,
127 /// Max datasets to list.
128 #[arg(long, default_value_t = 100)]
129 pub limit: usize,
130}
131
132/// `faucet catalog show <id>` arguments.
133#[cfg(feature = "catalog")]
134#[derive(Debug, Parser)]
135pub struct CatalogShowArgs {
136 /// Dataset id (from `faucet catalog datasets`), or a unique prefix of one.
137 pub id: String,
138 #[command(flatten)]
139 pub common: CatalogConfigArgs,
140}
141
142/// `faucet catalog lineage` arguments.
143#[cfg(feature = "catalog")]
144#[derive(Debug, Parser)]
145pub struct CatalogLineageArgs {
146 #[command(flatten)]
147 pub common: CatalogConfigArgs,
148 /// Dataset id to root the graph at (whole graph when omitted).
149 #[arg(long)]
150 pub root: Option<String>,
151 /// BFS hop bound around --root.
152 #[arg(long, default_value_t = 5)]
153 pub depth: u32,
154}
155
156/// `faucet notify test` arguments.
157#[cfg(feature = "notify")]
158#[derive(Debug, Parser)]
159pub struct NotifyArgs {
160 #[command(subcommand)]
161 pub command: NotifyCommand,
162}
163
164/// `faucet notify` subcommands.
165#[cfg(feature = "notify")]
166#[derive(Debug, Subcommand)]
167pub enum NotifyCommand {
168 /// Fire one synthetic event at every matching rule in the config.
169 Test(NotifyTestArgs),
170}
171
172/// `faucet notify test <config>` arguments.
173#[cfg(feature = "notify")]
174#[derive(Debug, Parser)]
175pub struct NotifyTestArgs {
176 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config with a
177 /// `notifications:` block. If omitted, auto-discover in cwd.
178 pub config: Option<PathBuf>,
179 /// Which event to synthesize (defaults to `run_failure`).
180 #[arg(long, default_value = "run_failure")]
181 pub event: String,
182 /// Path to a `.env` file for `${env:VAR}` interpolation.
183 #[arg(long, conflicts_with = "no_env_file")]
184 pub env_file: Option<PathBuf>,
185 /// Disable `.env` auto-discovery.
186 #[arg(long)]
187 pub no_env_file: bool,
188}
189
190/// `faucet test` arguments.
191#[derive(Debug, Parser)]
192pub struct TestArgs {
193 /// One or more test-spec files (`.yaml`, `.yml`, or `.json`), e.g.
194 /// `faucet test tests/*.yaml`.
195 #[arg(required = true)]
196 pub specs: Vec<PathBuf>,
197 /// Run only cases whose name contains this substring.
198 #[arg(long)]
199 pub filter: Option<String>,
200 /// Emit a machine-readable JSON report instead of the human checklist.
201 #[arg(long)]
202 pub json: bool,
203 /// Default `${now.*}` clock for cases without their own `clock:` field
204 /// (RFC3339 like `2026-01-31T00:00:00Z`, or a date `2026-01-31`).
205 /// Defaults to process start (UTC).
206 #[arg(long)]
207 pub clock: Option<String>,
208 /// Path to a `.env` file to load for `${env:VAR}` interpolation in
209 /// referenced pipeline configs. Defaults to `.env` in cwd if present.
210 #[arg(long, conflicts_with = "no_env_file")]
211 pub env_file: Option<PathBuf>,
212 /// Skip auto-loading `.env` from cwd.
213 #[arg(long)]
214 pub no_env_file: bool,
215 /// Select a named overlay from each referenced config's `profiles:` block.
216 /// Overrides the `FAUCET_PROFILE` env var.
217 #[arg(long, env = "FAUCET_PROFILE")]
218 pub profile: Option<String>,
219 /// Resolve `${vault:…}` / `${aws-sm:…}` / … secret directives in
220 /// referenced configs (requires network + credentials). By default tests
221 /// load configs offline and leave secret directives unresolved — safe
222 /// because the real source/sink configs holding them are never used.
223 #[arg(long)]
224 pub resolve_secrets: bool,
225}
226
227/// `faucet dlq` arguments.
228#[derive(Debug, Parser)]
229pub struct DlqArgs {
230 #[command(subcommand)]
231 pub command: DlqCommand,
232}
233
234/// `faucet dlq` subcommands.
235#[derive(Debug, Subcommand)]
236pub enum DlqCommand {
237 /// Read a DLQ location back and print a per-reason / per-error-kind
238 /// breakdown plus a sample of quarantined records.
239 Inspect(DlqInspectArgs),
240 /// Re-feed quarantined records through a pipeline config (transforms →
241 /// quality → contract → sink). Rows that fail again land in a *fresh* DLQ.
242 Replay(DlqReplayArgs),
243 /// Remove processed envelopes from a DLQ location (archive by default,
244 /// or `--delete`), filtered by reason and/or age.
245 Discard(DlqDiscardArgs),
246}
247
248/// `faucet dlq inspect <location>` arguments.
249#[derive(Debug, Parser)]
250pub struct DlqInspectArgs {
251 /// DLQ location: a `.jsonl` file, a directory of `*.jsonl` files, or a glob.
252 pub location: String,
253 /// Only include envelopes with this DLQ reason (`partial` / `dlq_all` /
254 /// `quality` / `schema_drift` / `contract`).
255 #[arg(long)]
256 pub reason: Option<String>,
257 /// Number of sample records to show. Default: 5.
258 #[arg(long, default_value_t = 5)]
259 pub limit: usize,
260 /// Emit a machine-readable JSON summary instead of the human report.
261 #[arg(long)]
262 pub json: bool,
263}
264
265/// `faucet dlq replay <config> --from <location>` arguments.
266#[derive(Debug, Parser)]
267pub struct DlqReplayArgs {
268 /// Path to the pipeline config whose sink / transforms / quality / contract
269 /// the replayed records flow through. If omitted, auto-discover in cwd.
270 pub config: Option<PathBuf>,
271 /// DLQ location to replay from: a `.jsonl` file, a directory, or a glob.
272 #[arg(long)]
273 pub from: String,
274 /// Only replay envelopes with this DLQ reason.
275 #[arg(long)]
276 pub reason: Option<String>,
277 /// Where replayed rows that fail *again* are quarantined. Defaults to a
278 /// `replay-failed.jsonl` sibling of the source (never the source itself).
279 #[arg(long)]
280 pub failed_dlq: Option<String>,
281 /// Which root row of the config to replay through. Defaults to the first root.
282 #[arg(long)]
283 pub row: Option<String>,
284 /// Report what would be replayed without writing to the sink.
285 #[arg(long)]
286 pub dry_run: bool,
287 /// Emit a machine-readable JSON result instead of the human summary.
288 #[arg(long)]
289 pub json: bool,
290 /// Path to a `.env` file for `${env:VAR}` interpolation in the config.
291 #[arg(long, conflicts_with = "no_env_file")]
292 pub env_file: Option<PathBuf>,
293 /// Skip auto-loading `.env` from cwd.
294 #[arg(long)]
295 pub no_env_file: bool,
296 /// Select a named overlay from the config's `profiles:` block.
297 #[arg(long, env = "FAUCET_PROFILE")]
298 pub profile: Option<String>,
299}
300
301/// `faucet dlq discard <location>` arguments.
302#[derive(Debug, Parser)]
303pub struct DlqDiscardArgs {
304 /// DLQ location: a `.jsonl` file, a directory of `*.jsonl` files, or a glob.
305 pub location: String,
306 /// Only discard envelopes with this DLQ reason.
307 #[arg(long)]
308 pub reason: Option<String>,
309 /// Only discard envelopes older than this: an RFC3339 timestamp
310 /// (`2026-06-01T00:00:00Z`) or a relative age (`7d`, `24h`, `30m`).
311 #[arg(long)]
312 pub before: Option<String>,
313 /// Permanently delete matching envelopes instead of archiving them to a
314 /// `<file>.archived.jsonl` sibling.
315 #[arg(long)]
316 pub delete: bool,
317 /// Emit a machine-readable JSON result instead of the human summary.
318 #[arg(long)]
319 pub json: bool,
320}
321
322/// `faucet doctor` arguments.
323#[derive(Debug, Parser)]
324pub struct DoctorArgs {
325 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config. If omitted,
326 /// auto-discover `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
327 pub config: Option<PathBuf>,
328 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
329 /// Defaults to `.env` in cwd if present.
330 #[arg(long, conflicts_with = "no_env_file")]
331 pub env_file: Option<PathBuf>,
332 /// Skip auto-loading `.env` from cwd.
333 #[arg(long)]
334 pub no_env_file: bool,
335 /// Per-probe timeout in seconds.
336 #[arg(long, default_value_t = 10)]
337 pub timeout_secs: u64,
338 /// Emit machine-readable JSON instead of the human checklist.
339 #[arg(long)]
340 pub json: bool,
341 /// Select a named overlay from the config's `profiles:` block and deep-merge
342 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
343 #[arg(long, env = "FAUCET_PROFILE")]
344 pub profile: Option<String>,
345}
346
347/// `faucet contract` arguments.
348#[cfg(feature = "contract")]
349#[derive(Debug, Parser)]
350pub struct ContractArgs {
351 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config with a
352 /// `pipeline.contract:` block. If omitted, auto-discover
353 /// `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
354 pub config: Option<PathBuf>,
355 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
356 /// Defaults to `.env` in cwd if present.
357 #[arg(long, conflicts_with = "no_env_file")]
358 pub env_file: Option<PathBuf>,
359 /// Skip auto-loading `.env` from cwd.
360 #[arg(long)]
361 pub no_env_file: bool,
362 /// Select a named overlay from the config's `profiles:` block and deep-merge
363 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
364 #[arg(long, env = "FAUCET_PROFILE")]
365 pub profile: Option<String>,
366 /// Export the contract in a machine-readable format instead of the
367 /// human summary: the canonical contract JSON, a standalone JSON Schema,
368 /// or an OpenLineage schema facet.
369 #[arg(long, value_enum)]
370 pub export: Option<ContractExportFormat>,
371}
372
373/// Arguments for `faucet masking`.
374#[cfg(feature = "masking")]
375#[derive(Debug, Parser)]
376pub struct MaskingArgs {
377 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config with a
378 /// `pipeline.masking:` block. If omitted, auto-discover
379 /// `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
380 pub config: Option<PathBuf>,
381 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
382 /// Defaults to `.env` in cwd if present.
383 #[arg(long, conflicts_with = "no_env_file")]
384 pub env_file: Option<PathBuf>,
385 /// Skip auto-loading `.env` from cwd.
386 #[arg(long)]
387 pub no_env_file: bool,
388 /// Select a named overlay from the config's `profiles:` block and deep-merge
389 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
390 #[arg(long, env = "FAUCET_PROFILE")]
391 pub profile: Option<String>,
392}
393
394/// Export format for `faucet contract --export`.
395#[cfg(feature = "contract")]
396#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
397pub enum ContractExportFormat {
398 /// The canonical contract document as JSON.
399 Contract,
400 /// A standalone JSON Schema (draft 2020-12) for the promised records.
401 JsonSchema,
402 /// An OpenLineage `SchemaDatasetFacet` JSON document.
403 Openlineage,
404}
405
406/// `faucet schedule` arguments.
407#[cfg(feature = "schedule")]
408#[derive(Debug, Parser)]
409pub struct ScheduleArgs {
410 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config with a `schedule:`
411 /// block. If omitted, auto-discover `faucet.yaml` / `.yml` / `.json` in cwd.
412 pub config: Option<PathBuf>,
413 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
414 /// Defaults to `.env` in cwd if present.
415 #[arg(long, conflicts_with = "no_env_file")]
416 pub env_file: Option<PathBuf>,
417 /// Skip auto-loading `.env` from cwd.
418 #[arg(long)]
419 pub no_env_file: bool,
420 /// Run exactly one pipeline run immediately, then exit (ignores cron timing).
421 /// Useful for platform-driven invocation (k8s CronJob / systemd OnCalendar).
422 #[arg(long)]
423 pub once: bool,
424 /// Select a named overlay from the config's `profiles:` block and deep-merge
425 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
426 #[arg(long, env = "FAUCET_PROFILE")]
427 pub profile: Option<String>,
428}
429
430/// `faucet serve` arguments.
431#[cfg(feature = "serve")]
432#[derive(Debug, Clone, Parser)]
433pub struct ServeArgs {
434 /// Bind address. Defaults to loopback; set 0.0.0.0:PORT to expose externally.
435 #[arg(long, env = "FAUCET_SERVE_LISTEN", default_value = "127.0.0.1:8080")]
436 pub listen: String,
437 /// Bearer token required on /v1/* requests. Prefer the env var (avoids `ps` leakage).
438 #[arg(long, env = "FAUCET_SERVE_AUTH_TOKEN", conflicts_with = "no_auth")]
439 pub auth_token: Option<String>,
440 /// Explicitly disable authentication. Required if no token is set, so an
441 /// unauthenticated server is never accidental.
442 #[arg(long)]
443 pub no_auth: bool,
444 /// Path to an RBAC auth config (YAML/JSON) defining principals — each a
445 /// `{ name, token, role }` where role is `viewer` / `operator` / `admin`.
446 /// Enables role-based access control + an audit log. Mutually exclusive with
447 /// `--auth-token` / `--no-auth`.
448 #[arg(long, conflicts_with_all = ["auth_token", "no_auth"])]
449 pub auth_config: Option<std::path::PathBuf>,
450 /// Max pipeline runs executing at once. Default: min(16, cpu count).
451 #[arg(long)]
452 pub max_concurrent_runs: Option<usize>,
453 /// Max queued (not-yet-running) runs before POST /v1/runs returns 429.
454 /// Default: 8 × max-concurrent-runs.
455 #[arg(long)]
456 pub max_queued_runs: Option<usize>,
457 /// Workspace-default config merged under every submitted run.
458 #[arg(long)]
459 pub default_config: Option<std::path::PathBuf>,
460 /// Run-history backend URL: omitted = in-memory; postgres://… ; sqlite:… .
461 #[arg(long)]
462 pub history: Option<String>,
463 /// CORS allow-list origin (repeatable). Omitted = CORS disabled.
464 #[arg(long)]
465 pub cors_origin: Vec<String>,
466 /// Max POST /v1/runs body size in bytes (413 on exceed).
467 #[arg(long, default_value_t = 1_048_576)]
468 pub body_limit_bytes: usize,
469 /// SIGTERM/SIGINT drain window in seconds.
470 #[arg(long, default_value_t = 60)]
471 pub shutdown_grace_secs: u64,
472 /// Retain terminal run records this long (seconds).
473 #[arg(long, default_value_t = 604_800)]
474 pub retain_terminal_runs_secs: u64,
475 /// Idempotency-key replay window (seconds).
476 #[arg(long, default_value_t = 86_400)]
477 pub idempotency_retention_secs: u64,
478 /// Run-ownership lease TTL in seconds (multi-instance orphan fencing). A run
479 /// is owned by the instance executing it and its lease is heartbeated at
480 /// ~⅓ of this interval; only a run whose lease has expired (owner presumed
481 /// dead) is recovered as failed. Make this comfortably larger than expected
482 /// GC/IO stalls so a healthy-but-slow instance is never falsely reclaimed.
483 /// Only relevant with a persistent (postgres/sqlite) history backend.
484 #[arg(long, default_value_t = 30)]
485 pub lease_ttl_secs: u64,
486 /// Per-probe timeout for `doctor_first` preflight (seconds).
487 #[arg(long, default_value_t = 10)]
488 pub probe_timeout_secs: u64,
489 /// Path to a `.env` file loaded for the server's own startup interpolation.
490 #[arg(long, conflicts_with = "no_env_file")]
491 pub env_file: Option<std::path::PathBuf>,
492 /// Skip auto-loading `.env` from cwd at startup.
493 #[arg(long)]
494 pub no_env_file: bool,
495 /// Disable serving the embedded web console (only meaningful in a build that
496 /// includes the `serve-ui` feature; the API is unaffected).
497 #[arg(long)]
498 pub no_ui: bool,
499 /// Enable clustered execution: run a claim loop that pulls Pending runs from
500 /// the shared history DB so N instances pull-balance and fail over. Requires
501 /// a postgres/sqlite --history backend.
502 #[arg(long)]
503 pub cluster: bool,
504 /// Claim-loop poll interval (seconds) in cluster mode. Also the
505 /// cross-instance cancel-propagation lag. Must be > 0.
506 #[arg(long, default_value_t = 2)]
507 pub cluster_poll_secs: u64,
508 /// Max failover re-runs of an orphaned run before it is marked Failed
509 /// (poison). Must be > 0.
510 #[arg(long, default_value_t = 3)]
511 pub cluster_max_attempts: u32,
512 /// Path to a triggers file (YAML/JSON) defining event-driven pipeline
513 /// triggers (object-arrival / webhook / queue-depth). Requires a build with
514 /// the `triggers` feature. See `faucet schema triggers`.
515 #[arg(long)]
516 pub triggers: Option<std::path::PathBuf>,
517}
518
519/// `faucet run` arguments.
520#[derive(Debug, Parser)]
521pub struct RunArgs {
522 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config.
523 /// If omitted (and `--from-env` is not set), auto-discover
524 /// `faucet.yaml` / `faucet.yml` / `faucet.json` in the current directory.
525 /// Mutually exclusive with `--from-env`.
526 #[arg(conflicts_with = "from_env")]
527 pub config: Option<PathBuf>,
528 /// Build the pipeline entirely from `FAUCET_*` environment variables —
529 /// no YAML required. See `cli/README.md` for the variable schema.
530 #[arg(long)]
531 pub from_env: bool,
532 /// Path to a `.env` file to load before reading variables. Works in both
533 /// YAML mode (for `${env:VAR}` interpolation) and `--from-env` mode.
534 /// When omitted, `.env` in the current directory is auto-loaded if present.
535 /// Existing process-env values always win over file-supplied ones.
536 #[arg(long, conflicts_with = "no_env_file")]
537 pub env_file: Option<PathBuf>,
538 /// Skip auto-loading `.env` from the current directory.
539 #[arg(long)]
540 pub no_env_file: bool,
541 /// Stop after fetching from the source — write nothing to the sink.
542 #[arg(long)]
543 pub dry_run: bool,
544 /// Stop after writing this many records to the sink. Default: unlimited.
545 #[arg(long)]
546 pub limit: Option<usize>,
547 /// Override the state-store directory (file backend only).
548 #[arg(long)]
549 pub state_path: Option<PathBuf>,
550 /// Override the `${now.*}` interpolation clock (RFC3339 like
551 /// `2026-01-31T00:00:00Z`, or a date `2026-01-31`). Default: process start (UTC).
552 /// Use for backfills.
553 #[arg(long)]
554 pub clock: Option<String>,
555 /// Select a named overlay from the config's `profiles:` block and deep-merge
556 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
557 /// Not applicable in `--from-env` mode (no config file to compose).
558 #[arg(long, env = "FAUCET_PROFILE")]
559 pub profile: Option<String>,
560}
561
562/// `faucet replicate` arguments.
563#[derive(Debug, Parser)]
564pub struct ReplicateArgs {
565 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config with a
566 /// `replication:` block. If omitted, auto-discover
567 /// `faucet.yaml` / `.yml` / `.json` in cwd.
568 pub config: Option<PathBuf>,
569 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
570 /// Defaults to `.env` in cwd if present.
571 #[arg(long, conflicts_with = "no_env_file")]
572 pub env_file: Option<PathBuf>,
573 /// Skip auto-loading `.env` from cwd.
574 #[arg(long)]
575 pub no_env_file: bool,
576 /// Select a named overlay from the config's `profiles:` block and deep-merge
577 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
578 #[arg(long, env = "FAUCET_PROFILE")]
579 pub profile: Option<String>,
580}
581
582/// `faucet validate` arguments.
583#[derive(Debug, Parser)]
584pub struct ValidateArgs {
585 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config. If omitted,
586 /// auto-discover `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
587 pub config: Option<PathBuf>,
588 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
589 /// Defaults to `.env` in cwd if present.
590 #[arg(long, conflicts_with = "no_env_file")]
591 pub env_file: Option<PathBuf>,
592 /// Skip auto-loading `.env` from cwd.
593 #[arg(long)]
594 pub no_env_file: bool,
595 /// Validate grammar and structure only — skip fetching from secrets
596 /// managers (no network / credentials needed).
597 #[arg(long)]
598 pub no_secrets: bool,
599 /// Select a named overlay from the config's `profiles:` block and deep-merge
600 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
601 #[arg(long, env = "FAUCET_PROFILE")]
602 pub profile: Option<String>,
603 /// Print the fully-composed config (after extends/!include/profile, before
604 /// `${...}` interpolation) and exit. For debugging composition precedence.
605 /// `--no-secrets` is redundant here (no interpolation or secret fetch occurs).
606 #[arg(long)]
607 pub show_composed: bool,
608}
609
610/// `faucet schema` arguments.
611#[derive(Debug, Parser)]
612pub struct SchemaArgs {
613 #[command(subcommand)]
614 pub target: SchemaTarget,
615}
616
617/// Schema subcommand target — which connector or system component to describe.
618#[derive(Debug, Subcommand)]
619pub enum SchemaTarget {
620 /// JSON Schema for a source connector config.
621 Source {
622 /// Connector name (e.g. `rest`, `graphql`, `postgres`).
623 name: String,
624 },
625 /// JSON Schema for a sink connector config.
626 Sink {
627 /// Connector name (e.g. `jsonl`, `bigquery`, `postgres`).
628 name: String,
629 },
630 /// JSON Schema for a transform's inline config.
631 Transform {
632 /// Transform name (e.g. `flatten`, `keys_case`, `cast`).
633 /// Run `faucet list` to see what is compiled in.
634 name: String,
635 },
636 /// JSON Schema for the DLQ (Dead Letter Queue) specification.
637 Dlq,
638 /// JSON Schema for the `replication:` (snapshot→CDC) block.
639 Replication,
640 /// JSON Schema for the top-level `execution:` block.
641 Execution,
642 /// JSON Schema for the top-level `resilience:` block.
643 Resilience,
644 /// JSON Schema for the top-level `sla:` (freshness/volume SLA) block.
645 Sla,
646 /// JSON Schema for the `quality:` block.
647 #[cfg(feature = "quality")]
648 Quality,
649 /// JSON Schema for the `contract:` block.
650 #[cfg(feature = "contract")]
651 Contract,
652 /// JSON Schema for the `masking:` (PII masking) block.
653 #[cfg(feature = "masking")]
654 Masking,
655 /// JSON Schema for the `faucet test` spec file.
656 Test,
657 /// Grammar reference for secrets-manager interpolation directives.
658 Secrets,
659 /// JSON Schema for the `schedule:` block.
660 #[cfg(feature = "schedule")]
661 Schedule,
662 /// JSON Schema for the `lineage:` (OpenLineage) block.
663 #[cfg(feature = "lineage")]
664 Lineage,
665 /// JSON Schema for the `--triggers` file (event-driven pipeline triggers).
666 #[cfg(feature = "triggers")]
667 Triggers,
668 /// JSON Schema for the `notifications:` (incident-routing) block.
669 #[cfg(feature = "notify")]
670 Notifications,
671 /// JSON Schema for the `catalog:` (Data Movement Catalog store) block.
672 #[cfg(feature = "catalog")]
673 Catalog,
674}
675
676/// `faucet preview` arguments.
677#[derive(Debug, Parser)]
678pub struct PreviewArgs {
679 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config. If omitted,
680 /// auto-discover `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
681 pub config: Option<PathBuf>,
682 /// Stop after this many records. Default: 10.
683 #[arg(long, default_value_t = 10)]
684 pub limit: usize,
685 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
686 /// Defaults to `.env` in cwd if present.
687 #[arg(long, conflicts_with = "no_env_file")]
688 pub env_file: Option<PathBuf>,
689 /// Skip auto-loading `.env` from cwd.
690 #[arg(long)]
691 pub no_env_file: bool,
692 /// Select a named overlay from the config's `profiles:` block and deep-merge
693 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
694 #[arg(long, env = "FAUCET_PROFILE")]
695 pub profile: Option<String>,
696}
697
698/// `faucet init` arguments.
699#[derive(Debug, Parser)]
700pub struct InitArgs {
701 /// Name written into the generated file's `name:` field. Defaults to
702 /// `my-pipeline` when omitted.
703 pub name: Option<String>,
704 /// Source connector kind to scaffold (e.g. `rest`, `postgres`, `s3`).
705 /// Defaults to `rest`. Run `faucet list` to see what is compiled in.
706 #[arg(long)]
707 pub source: Option<String>,
708 /// Sink connector kind to scaffold (e.g. `jsonl`, `bigquery`).
709 /// Defaults to `jsonl`. Run `faucet list` to see what is compiled in.
710 #[arg(long)]
711 pub sink: Option<String>,
712 /// Output file path. Defaults to `pipeline.yaml`.
713 #[arg(long, short = 'o', default_value = "pipeline.yaml")]
714 pub output: PathBuf,
715 /// Overwrite the output file if it already exists.
716 #[arg(long)]
717 pub force: bool,
718 /// Prompt for the source and sink kinds interactively instead of using
719 /// `--source` / `--sink`. Requires the `cli-interactive` build feature
720 /// and a TTY on stdin; falls back to the arg-driven path otherwise.
721 #[arg(long)]
722 pub interactive: bool,
723 /// Name of the template under which to register the scaffolded source
724 /// and sink. The generated config uses `pipeline.sources.<TEMPLATE>` and
725 /// `pipeline.sinks.<TEMPLATE>`. Defaults to `default` so a matrix row
726 /// without a `ref:` field still resolves through the new schema.
727 #[arg(long, default_value = "default")]
728 pub template: String,
729 /// (singer only) Run `<executable> --discover` to fetch the tap's catalog,
730 /// write it next to the output, and scaffold the config with the discovered
731 /// streams listed. Requires `--source singer` and `--executable`.
732 #[arg(long)]
733 pub discover: bool,
734 /// (singer only) The Singer tap executable to discover with (used by
735 /// `--discover`), e.g. `tap-github` or `/opt/taps/tap-csv`.
736 #[arg(long)]
737 pub executable: Option<String>,
738}