Skip to main content

cmakefmt/
error.rs

1// SPDX-FileCopyrightText: Copyright 2026 Puneet Matharu
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5//! Structured error types returned by parsing, config loading, and formatting.
6
7use thiserror::Error;
8
9/// Structured config/spec deserialization failure metadata used for
10/// user-facing diagnostics.
11#[derive(Debug, Clone)]
12pub struct FileParseError {
13    /// Parser format name, such as `TOML` or `YAML`.
14    pub format: &'static str,
15    /// Human-readable parser message.
16    pub message: Box<str>,
17    /// Optional 1-based line number.
18    pub line: Option<usize>,
19    /// Optional 1-based column number.
20    pub column: Option<usize>,
21}
22
23/// Errors that can be returned by parsing, config loading, spec loading, or
24/// formatting operations.
25#[derive(Debug, Error)]
26pub enum Error {
27    /// A syntax error reported by the CMake parser.
28    #[error("parse error: {0}")]
29    Parse(#[from] Box<pest::error::Error<crate::parser::Rule>>),
30
31    /// A parser error annotated with source text and line-offset context.
32    #[error("parse error in {display_name}: {source}")]
33    ParseContext {
34        /// Human-facing source name, for example a path or `<stdin>`.
35        display_name: String,
36        /// The source text that failed to parse.
37        source_text: Box<str>,
38        /// The 1-based source line number where this parser chunk started.
39        start_line: usize,
40        /// Whether earlier barrier/fence handling affected how this chunk was parsed.
41        barrier_context: bool,
42        /// The underlying pest parser error.
43        source: Box<pest::error::Error<crate::parser::Rule>>,
44    },
45
46    /// A user config parse error.
47    #[error("config error in {path}: {source_message}")]
48    Config {
49        /// The config file that failed to deserialize.
50        path: std::path::PathBuf,
51        /// Structured parser details for the failure.
52        details: FileParseError,
53        /// Cached display string used by `thiserror`.
54        source_message: Box<str>,
55    },
56
57    /// A built-in or user override spec parse error.
58    #[error("spec error in {path}: {source_message}")]
59    Spec {
60        /// The spec file that failed to deserialize.
61        path: std::path::PathBuf,
62        /// Structured parser details for the failure.
63        details: FileParseError,
64        /// Cached display string used by `thiserror`.
65        source_message: Box<str>,
66    },
67
68    /// A filesystem or stream I/O failure.
69    #[error("I/O error: {0}")]
70    Io(#[from] std::io::Error),
71
72    /// A higher-level formatter or CLI error that does not fit another
73    /// structured variant.
74    #[error("formatter error: {0}")]
75    Formatter(String),
76
77    /// A formatted line exceeded the configured line width and
78    /// `require_valid_layout` is enabled.
79    #[error(
80        "line {line_no} is {width} characters wide, exceeding the configured limit of {limit}"
81    )]
82    LayoutTooWide {
83        /// 1-based line number in the formatted output.
84        line_no: usize,
85        /// Actual character width of the offending line.
86        width: usize,
87        /// Configured [`Config::line_width`] limit.
88        limit: usize,
89    },
90}
91
92/// Convenience alias for crate-level results.
93pub type Result<T> = std::result::Result<T, Error>;
94
95impl Error {
96    /// Attach a human-facing source name to a contextual parser error.
97    pub fn with_display_name(self, display_name: impl Into<String>) -> Self {
98        match self {
99            Self::ParseContext {
100                source_text,
101                start_line,
102                barrier_context,
103                source,
104                ..
105            } => Self::ParseContext {
106                display_name: display_name.into(),
107                source_text,
108                start_line,
109                barrier_context,
110                source,
111            },
112            other => other,
113        }
114    }
115}