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}