Skip to main content

copybook_codec/options/
parse_error.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2//! Typed errors for parsing codec option values.
3
4use std::fmt;
5
6const FLOAT_FORMAT_SPELLINGS: &[&str] = &["ieee-be", "ieee", "ieee-big-endian", "ibm-hex", "ibm"];
7const RECORD_FORMAT_SPELLINGS: &[&str] = &["fixed", "rdw"];
8const JSON_NUMBER_MODE_SPELLINGS: &[&str] = &["lossless", "native"];
9const RAW_MODE_SPELLINGS: &[&str] = &["off", "record", "field", "record+rdw"];
10
11/// Codec option family whose textual value could not be parsed.
12///
13/// This discriminator is intentionally separate from the stable `CBK*` runtime
14/// error taxonomy: parsing a configuration token happens before a codec
15/// operation and does not identify a record-processing failure.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17#[non_exhaustive]
18pub enum CodecOptionKind {
19    /// Floating-point representation selected by [`super::FloatFormat`].
20    FloatFormat,
21    /// Record framing selected by [`super::RecordFormat`].
22    RecordFormat,
23    /// JSON number representation selected by [`super::JsonNumberMode`].
24    JsonNumberMode,
25    /// Raw-data capture selected by [`super::RawMode`].
26    RawMode,
27}
28
29impl CodecOptionKind {
30    /// Return every accepted spelling for this option family.
31    #[must_use]
32    #[inline]
33    pub const fn accepted_spellings(self) -> &'static [&'static str] {
34        match self {
35            Self::FloatFormat => FLOAT_FORMAT_SPELLINGS,
36            Self::RecordFormat => RECORD_FORMAT_SPELLINGS,
37            Self::JsonNumberMode => JSON_NUMBER_MODE_SPELLINGS,
38            Self::RawMode => RAW_MODE_SPELLINGS,
39        }
40    }
41
42    #[inline]
43    const fn display_name(self) -> &'static str {
44        match self {
45            Self::FloatFormat => "float format",
46            Self::RecordFormat => "record format",
47            Self::JsonNumberMode => "JSON number mode",
48            Self::RawMode => "raw mode",
49        }
50    }
51}
52
53/// Error returned when a textual codec option is unsupported.
54///
55/// The original input is retained verbatim for diagnostics. Use [`Self::kind`]
56/// and [`Self::accepted_spellings`] for programmatic recovery. Serde errors
57/// remain Serde errors; this type is the `FromStr` contract only.
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct ParseCodecOptionError {
60    kind: CodecOptionKind,
61    input: String,
62}
63
64impl ParseCodecOptionError {
65    #[inline]
66    pub(super) fn new(kind: CodecOptionKind, input: &str) -> Self {
67        Self {
68            kind,
69            input: input.to_owned(),
70        }
71    }
72
73    /// Return the option family that rejected the input.
74    #[must_use]
75    #[inline]
76    pub const fn kind(&self) -> CodecOptionKind {
77        self.kind
78    }
79
80    /// Return the original, unnormalized input.
81    #[must_use]
82    #[inline]
83    pub fn input(&self) -> &str {
84        &self.input
85    }
86
87    /// Return every spelling accepted by the rejected option family.
88    #[must_use]
89    #[inline]
90    pub const fn accepted_spellings(&self) -> &'static [&'static str] {
91        self.kind.accepted_spellings()
92    }
93}
94
95impl fmt::Display for ParseCodecOptionError {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        write!(
98            f,
99            "unsupported {} `{}`",
100            self.kind.display_name(),
101            self.input
102        )
103    }
104}
105
106impl std::error::Error for ParseCodecOptionError {}