Skip to main content

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}