Skip to main content

serde_saphyr/ser/
error.rs

1use std::{fmt, io};
2
3/// Error type used by the YAML serializer.
4///
5/// This type is re-exported as `serde_saphyr::SerializeError` and
6/// `serde_saphyr::ser::Error`, and is returned by
7/// the public serialization APIs (for example `serde_saphyr::to_string`).
8///
9/// It implements `serde::ser::Error`, which allows user `Serialize` impls and
10/// Serde derives to report failures via `S::Error::custom(...)`. Such
11/// free‑form messages are stored in the `Message` variant.
12///
13/// Other variants wrap concrete underlying failures that can occur while
14/// serializing:
15/// - `Format` wraps a `std::fmt::Error` produced when writing to a
16///   `fmt::Write` target.
17/// - `IO` wraps a `std::io::Error` produced when writing to an `io::Write`
18///   target.
19/// - `SingleQuotedRequiresEscaping` reports a `SingleQuoted` wrapper value
20///   that needs YAML escape sequences and therefore cannot be emitted in
21///   single-quoted style.
22/// - `Unexpected` is used internally for invariant violations (e.g., around
23///   anchors). It should not normally surface; if it does, please file a bug.
24#[non_exhaustive]
25#[derive(Debug)]
26pub enum Error {
27    /// Free-form error.
28    Message { msg: String },
29    /// Wrapper for formatting errors.
30    Format { error: fmt::Error },
31    /// Wrapper for I/O errors.
32    IO { error: io::Error },
33    /// This is used with anchors and should normally not surface, please report bug if it does.
34    Unexpected { msg: String },
35    /// Options used would produce invalid YAML (0 indentation, etc)
36    InvalidOptions(String),
37    /// A [`crate::SingleQuoted`] value contains a character that cannot be represented safely in
38    /// YAML single-quoted style.
39    SingleQuotedRequiresEscaping { ch: char },
40}
41
42impl serde_core::ser::Error for Error {
43    fn custom<T: fmt::Display>(msg: T) -> Self {
44        Error::Message {
45            msg: msg.to_string(),
46        }
47    }
48}
49
50impl From<fmt::Error> for Error {
51    fn from(error: fmt::Error) -> Self {
52        Error::Format { error }
53    }
54}
55
56impl From<io::Error> for Error {
57    fn from(error: io::Error) -> Self {
58        Error::IO { error }
59    }
60}
61
62impl From<String> for Error {
63    fn from(message: String) -> Self {
64        Error::Message { msg: message }
65    }
66}
67
68impl From<&String> for Error {
69    fn from(message: &String) -> Self {
70        Error::Message {
71            msg: message.clone(),
72        }
73    }
74}
75
76impl From<&str> for Error {
77    fn from(message: &str) -> Self {
78        Error::Message {
79            msg: message.to_string(),
80        }
81    }
82}
83
84impl Error {
85    #[cold]
86    #[inline(never)]
87    pub(crate) fn unexpected(message: &str) -> Self {
88        Error::Unexpected {
89            msg: message.to_string(),
90        }
91    }
92}
93
94impl fmt::Display for Error {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        match self {
97            Error::Message { msg } => f.write_str(msg),
98            Error::Format { error } => write!(f, "formatting error: {error}"),
99            Error::IO { error } => write!(f, "I/O error: {error}"),
100            Error::Unexpected { msg } => write!(f, "unexpected internal error: {msg}"),
101            Error::InvalidOptions(msg) => write!(f, "invalid serialization options: {msg}"),
102            Error::SingleQuotedRequiresEscaping { ch } => {
103                // Debug formatting keeps rejected control characters escaped in the error message.
104                write!(
105                    f,
106                    "Single quotes cannot be used for a string containing {ch:?}. Use double quoting for values that require YAML escape sequences"
107                )
108            }
109        }
110    }
111}
112
113impl std::error::Error for Error {
114    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
115        match self {
116            Error::Format { error } => Some(error),
117            Error::IO { error } => Some(error),
118            Error::Message { .. }
119            | Error::Unexpected { .. }
120            | Error::InvalidOptions(_)
121            | Error::SingleQuotedRequiresEscaping { .. } => None,
122        }
123    }
124}