mz_deploy/cli.rs
1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Command-line interface for mz-deploy.
11//!
12//! This module defines the CLI structure and shared types used across commands.
13//!
14//! ## Submodules
15//!
16//! - **[`commands`]** — One module per CLI subcommand (`stage`, `apply`, `compile`, etc.),
17//! each exposing a `run()` entry point.
18//! - **[`executor`]** — Orchestrates the full command lifecycle: loads configuration,
19//! establishes database connections, and dispatches to the appropriate command module.
20//! - **`error`** — [`CliError`] enum that unifies all user-facing errors with optional
21//! hints, re-exported at this level for convenience.
22//!
23//! ## Top-level Items
24//!
25//! - [`display_error`] — Renders a [`CliError`] to stderr with rustc-style colored
26//! formatting and hint messages.
27
28pub mod commands;
29mod error;
30pub mod executor;
31pub mod extended_help;
32pub mod git;
33pub mod progress;
34mod render;
35
36pub use error::CliError;
37
38/// Display a CLI error and exit with status code 1.
39///
40/// For errors that carry source positions (parse, validation, typecheck),
41/// emits rustc-style output with a caret under the offending token via
42/// [`annotate_snippets`]. Other errors fall back to plain `Display`. The
43/// optional [`CliError::hint`] is appended in either path.
44#[allow(clippy::print_stderr)]
45pub fn display_error(error: &CliError) {
46 use owo_colors::{OwoColorize, Stream, Style};
47
48 let positional = render::to_positional(error);
49 if positional.is_empty() {
50 let error_style = Style::new().bright_red().bold();
51 eprintln!(
52 "{}: {}",
53 "error".if_supports_color(Stream::Stderr, |t| error_style.style(t)),
54 error
55 );
56 } else {
57 for pd in &positional {
58 eprintln!("{}", render::render(pd));
59 }
60 }
61
62 if let Some(hint) = error.hint() {
63 let eq_style = Style::new().bright_blue().bold();
64 eprintln!(
65 " {} {}",
66 "=".if_supports_color(Stream::Stderr, |t| eq_style.style(t)),
67 format!("help: {}", hint).if_supports_color(Stream::Stderr, |t| t.bold())
68 );
69 }
70
71 std::process::exit(1);
72}