Skip to main content

plugx_input/
error.rs

1//! Errors returned when [`Input`] fails to deserialize.
2//!
3//! Enabled by the `serde` or `rkyv` Cargo feature. Serde maps these to custom
4//! deserializer errors; rkyv uses [`DeserializeError::InvalidArchive`].
5
6use crate::position::InputPath;
7use std::convert::Infallible;
8use thiserror::Error;
9
10/// Allowed top-level [`Input`] kinds, used in type-mismatch messages.
11pub const EXPECTED_INPUT_TYPES: &str = "boolean, integer, float, string, list, or map";
12
13/// Failure while decoding external data into [`Input`].
14#[derive(Debug, Clone, PartialEq, Eq, Error)]
15pub enum DeserializeError {
16    /// Value at [`InputPath`] was not a supported [`Input`] variant.
17    #[error("{}", invalid_type_message(.path, .expected, .found))]
18    InvalidType {
19        path: InputPath,
20        expected: &'static str,
21        found: String,
22    },
23    /// Integer literal does not fit in [`isize`] (serde `i128`/`u128` paths).
24    #[error("{}", integer_out_of_range_message(.path, *.value))]
25    IntegerOutOfRange { path: InputPath, value: i128 },
26    /// Archived bytes could not be validated or decoded (`rkyv` feature).
27    #[error("{path}: invalid archived data: {message}")]
28    InvalidArchive { path: InputPath, message: String },
29}
30
31/// Build a type-mismatch error with [`EXPECTED_INPUT_TYPES`] (`serde` feature).
32#[cfg(feature = "serde")]
33pub fn invalid_type(path: InputPath, found: impl Into<String>) -> DeserializeError {
34    DeserializeError::InvalidType {
35        path,
36        expected: EXPECTED_INPUT_TYPES,
37        found: found.into(),
38    }
39}
40
41fn invalid_type_message(path: &InputPath, expected: &str, found: &str) -> String {
42    if path.is_empty() {
43        format!("expected {expected}, found {found}")
44    } else {
45        format!("{path}: expected {expected}, found {found}")
46    }
47}
48
49fn integer_out_of_range_message(path: &InputPath, value: i128) -> String {
50    if path.is_empty() {
51        format!("integer {value} is out of range for isize")
52    } else {
53        format!("{path}: integer {value} is out of range for isize")
54    }
55}
56
57/// Convert a deserialized integer to [`isize`], if it fits (`serde` feature).
58#[cfg(feature = "serde")]
59pub fn i128_to_isize(value: i128) -> Option<isize> {
60    value.try_into().ok()
61}
62
63/// [`Input`] serialization is infallible for supported serializers/archives.
64pub type InputSerializeError = Infallible;