Skip to main content

gix_config/parse/
error.rs

1use std::fmt::Display;
2
3use crate::parse::Error;
4
5#[derive(PartialEq, Debug)]
6pub(crate) enum Kind {
7    Parse {
8        line_number: usize,
9        last_attempted_parser: ParseNode,
10        parsed_until: bstr::BString,
11    },
12    InputTooLarge {
13        actual: usize,
14    },
15}
16
17/// A list of parsers that parsing can fail on. This is used for pretty-printing errors
18#[derive(PartialEq, Debug, Clone, Copy)]
19pub(crate) enum ParseNode {
20    SectionHeader,
21    Name,
22    Value,
23}
24
25impl Display for ParseNode {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        match self {
28            Self::SectionHeader => write!(f, "section header"),
29            Self::Name => write!(f, "name"),
30            Self::Value => write!(f, "value"),
31        }
32    }
33}
34
35impl Error {
36    pub(crate) fn parse(line_number: usize, last_attempted_parser: ParseNode, parsed_until: bstr::BString) -> Self {
37        Self {
38            kind: Kind::Parse {
39                line_number,
40                last_attempted_parser,
41                parsed_until,
42            },
43        }
44    }
45
46    pub(crate) fn input_too_large(actual: usize) -> Self {
47        Self {
48            kind: Kind::InputTooLarge { actual },
49        }
50    }
51
52    /// The one-indexed line number where the error occurred. This is determined
53    /// by the number of newlines that were successfully parsed.
54    #[must_use]
55    pub const fn line_number(&self) -> usize {
56        match self.kind {
57            Kind::Parse { line_number, .. } => line_number + 1,
58            Kind::InputTooLarge { .. } => 1,
59        }
60    }
61
62    /// The data that was left unparsed, which contains the cause of the parse error.
63    #[must_use]
64    pub fn remaining_data(&self) -> &[u8] {
65        match &self.kind {
66            Kind::Parse { parsed_until, .. } => parsed_until,
67            Kind::InputTooLarge { .. } => &[],
68        }
69    }
70}
71
72impl Display for Error {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        let (line_number, last_attempted_parser, parsed_until) = match &self.kind {
75            Kind::InputTooLarge { actual } => {
76                return write!(
77                    f,
78                    "Configuration input is {actual} bytes large, but at most {} bytes are supported",
79                    u32::MAX
80                );
81            }
82            Kind::Parse {
83                line_number,
84                last_attempted_parser,
85                parsed_until,
86            } => (line_number, last_attempted_parser, parsed_until),
87        };
88        write!(
89            f,
90            "Got an unexpected token on line {} while trying to parse a {}: ",
91            line_number + 1,
92            last_attempted_parser,
93        )?;
94
95        let data_size = parsed_until.len();
96        let data = std::str::from_utf8(parsed_until);
97        match (data, data_size) {
98            (Ok(data), _) if data_size > 10 => {
99                write!(
100                    f,
101                    "'{}' ... ({} characters omitted)",
102                    data.chars().take(10).collect::<String>(),
103                    data_size - 10
104                )
105            }
106            (Ok(data), _) => write!(f, "'{data}'"),
107            (Err(_), _) => parsed_until.fmt(f),
108        }
109    }
110}
111
112impl std::error::Error for Error {}