1use std::error;
21use std::fmt;
22use std::io;
23use std::num::NonZeroUsize;
24use std::path::Path;
25use std::path::PathBuf;
26use std::slice::EscapeAscii;
27
28use nom::Err;
29
30#[expect(clippy::error_impl_error)]
31#[derive(Debug)]
32pub enum Error {
33 IoError(io::Error),
34 NoFilename(PathBuf),
35 ParsingIncomplete(MoreDataNeeded),
36 ParsingError(Box<[u8]>, ParsingErrorKind),
37 DecodeError(Box<[u8]>),
38 UnresolvedRecordIds(PathBuf),
39 PluginMetadataNotFound(String),
40}
41
42impl From<Err<nom::error::Error<&[u8]>>> for Error {
43 fn from(error: Err<nom::error::Error<&[u8]>>) -> Self {
44 match error {
45 Err::Incomplete(nom::Needed::Unknown) => {
46 Error::ParsingIncomplete(MoreDataNeeded::UnknownSize)
47 }
48 Err::Incomplete(nom::Needed::Size(size)) => {
49 Error::ParsingIncomplete(MoreDataNeeded::Size(size))
50 }
51 Err::Error(err) | Err::Failure(err) => Error::ParsingError(
52 err.input.into(),
53 ParsingErrorKind::GenericParserError(err.code.description().to_owned()),
54 ),
55 }
56 }
57}
58
59impl From<io::Error> for Error {
60 fn from(error: io::Error) -> Self {
61 Error::IoError(error)
62 }
63}
64
65impl fmt::Display for Error {
66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 match self {
68 Error::IoError(x) => x.fmt(f),
69 Error::NoFilename(path) => {
70 write!(
71 f,
72 "The plugin path \"{}\" has no filename part",
73 escape_ascii(path)
74 )
75 }
76 Error::ParsingIncomplete(MoreDataNeeded::UnknownSize) => write!(
77 f,
78 "An unknown number of bytes of additional input was expected by the plugin parser"
79 ),
80 Error::ParsingIncomplete(MoreDataNeeded::Size(size)) => write!(
81 f,
82 "{size} bytes of additional input was expected by the plugin parser"
83 ),
84 Error::ParsingError(input, kind) => write!(
85 f,
86 "An error was encountered while parsing the plugin content \"{}\": {kind}",
87 input.escape_ascii()
88 ),
89 Error::DecodeError(bytes) => write!(
90 f,
91 "Plugin string content could not be decoded from Windows-1252, content is \"{}\"",
92 bytes.escape_ascii()
93 ),
94 Error::UnresolvedRecordIds(path) => {
95 write!(
96 f,
97 "Record IDs are unresolved for plugin at \"{}\"",
98 escape_ascii(path)
99 )
100 }
101 Error::PluginMetadataNotFound(plugin) => {
102 write!(f, "Plugin metadata for \"{plugin}\" not found")
103 }
104 }
105 }
106}
107
108impl error::Error for Error {
109 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
110 match self {
111 Error::IoError(x) => Some(x),
112 _ => None,
113 }
114 }
115}
116
117fn escape_ascii(path: &Path) -> EscapeAscii<'_> {
118 path.as_os_str().as_encoded_bytes().escape_ascii()
119}
120
121#[derive(Debug)]
122pub enum ParsingErrorKind {
123 UnexpectedRecordType(Vec<u8>),
125 SubrecordDataTooShort(usize),
127 GenericParserError(String),
129}
130
131impl fmt::Display for ParsingErrorKind {
132 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133 match self {
134 ParsingErrorKind::UnexpectedRecordType(v) => {
135 write!(f, "Expected record type \"{}\"", v.escape_ascii())
136 }
137 ParsingErrorKind::SubrecordDataTooShort(s) => write!(
138 f,
139 "Subrecord data field too short, expected at least {s} bytes",
140 ),
141 ParsingErrorKind::GenericParserError(e) => write!(f, "Error in parser: {e}"),
142 }
143 }
144}
145
146#[derive(Debug, PartialEq, Eq, Clone, Copy)]
147pub enum MoreDataNeeded {
148 UnknownSize,
150 Size(NonZeroUsize),
152}