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/// - `EmptyResolvedTag` reports a `Tagged` wrapper whose explicit resolved tag
23/// is empty. Use `None` to represent the absence of a tag.
24/// - `InvalidGlobalTagUri` reports a non-local `Tagged` identity that is not an
25/// absolute URI.
26/// - `CoreTypeTagAsEnumVariant` reports a YAML core type tag whose
27/// name collides with a non-unit enum variant.
28/// - `Unexpected` is used internally for invariant violations (e.g., around
29/// anchors). It should not normally surface; if it does, please file a bug.
30#[non_exhaustive]
31#[derive(Debug)]
32pub enum Error {
33 /// Free-form error.
34 Message { msg: String },
35 /// Wrapper for formatting errors.
36 Format { error: fmt::Error },
37 /// Wrapper for I/O errors.
38 IO { error: io::Error },
39 /// This is used with anchors and should normally not surface, please report bug if it does.
40 Unexpected { msg: String },
41 /// Options used would produce invalid YAML (0 indentation, etc)
42 InvalidOptions(String),
43 /// A [`crate::SingleQuoted`] value contains a character that cannot be represented safely in
44 /// YAML single-quoted style.
45 SingleQuotedRequiresEscaping { ch: char },
46 /// A [`crate::Tagged`] value uses an empty string as an explicit resolved tag identity.
47 ///
48 /// Use `None` to represent a value without an explicit tag.
49 EmptyResolvedTag,
50 /// A non-local [`crate::Tagged`] identity is not a valid absolute URI.
51 InvalidGlobalTagUri { tag: String },
52 /// A YAML core type tag cannot act as the discriminator for a same-named non-unit enum
53 /// variant.
54 CoreTypeTagAsEnumVariant { tag: String, variant: String },
55}
56
57impl serde_core::ser::Error for Error {
58 fn custom<T: fmt::Display>(msg: T) -> Self {
59 Error::Message {
60 msg: msg.to_string(),
61 }
62 }
63}
64
65impl From<fmt::Error> for Error {
66 fn from(error: fmt::Error) -> Self {
67 Error::Format { error }
68 }
69}
70
71impl From<io::Error> for Error {
72 fn from(error: io::Error) -> Self {
73 Error::IO { error }
74 }
75}
76
77impl From<String> for Error {
78 fn from(message: String) -> Self {
79 Error::Message { msg: message }
80 }
81}
82
83impl From<&String> for Error {
84 fn from(message: &String) -> Self {
85 Error::Message {
86 msg: message.clone(),
87 }
88 }
89}
90
91impl From<&str> for Error {
92 fn from(message: &str) -> Self {
93 Error::Message {
94 msg: message.to_string(),
95 }
96 }
97}
98
99impl Error {
100 #[cold]
101 #[inline(never)]
102 pub(crate) fn unexpected(message: &str) -> Self {
103 Error::Unexpected {
104 msg: message.to_string(),
105 }
106 }
107}
108
109impl fmt::Display for Error {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 match self {
112 Error::Message { msg } => f.write_str(msg),
113 Error::Format { error } => write!(f, "formatting error: {error}"),
114 Error::IO { error } => write!(f, "I/O error: {error}"),
115 Error::Unexpected { msg } => write!(f, "unexpected internal error: {msg}"),
116 Error::InvalidOptions(msg) => write!(f, "invalid serialization options: {msg}"),
117 Error::SingleQuotedRequiresEscaping { ch } => {
118 // Debug formatting keeps rejected control characters escaped in the error message.
119 write!(
120 f,
121 "Single quotes cannot be used for a string containing {ch:?}. Use double quoting for values that require YAML escape sequences"
122 )
123 }
124 Error::EmptyResolvedTag => {
125 f.write_str("cannot serialize an explicit YAML tag with an empty resolved identity")
126 }
127 Error::InvalidGlobalTagUri { tag } => write!(
128 f,
129 "cannot serialize resolved YAML tag {tag:?}: non-local tag identities must be absolute URIs"
130 ),
131 Error::CoreTypeTagAsEnumVariant { tag, variant } => write!(
132 f,
133 "cannot use YAML core type tag {tag:?} to select enum variant {variant:?}"
134 ),
135 }
136 }
137}
138
139impl std::error::Error for Error {
140 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
141 match self {
142 Error::Format { error } => Some(error),
143 Error::IO { error } => Some(error),
144 Error::Message { .. }
145 | Error::Unexpected { .. }
146 | Error::InvalidOptions(_)
147 | Error::SingleQuotedRequiresEscaping { .. }
148 | Error::EmptyResolvedTag
149 | Error::InvalidGlobalTagUri { .. }
150 | Error::CoreTypeTagAsEnumVariant { .. } => None,
151 }
152 }
153}