1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use std::fmt;

use super::Access;
use crate::{ReflectKind, VariantType};

/// The kind of [`AccessError`], along with some kind-specific information.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum AccessErrorKind {
    /// An error that occurs when a certain type doesn't
    /// contain the value referenced by the [`Access`].
    MissingField(ReflectKind),

    /// An error that occurs when using an [`Access`] on the wrong type.
    /// (i.e. a [`ListIndex`](Access::ListIndex) on a struct, or a [`TupleIndex`](Access::TupleIndex) on a list)
    IncompatibleTypes {
        /// The [`ReflectKind`] that was expected based on the [`Access`].
        expected: ReflectKind,
        /// The actual [`ReflectKind`] that was found.
        actual: ReflectKind,
    },

    /// An error that occurs when using an [`Access`] on the wrong enum variant.
    /// (i.e. a [`ListIndex`](Access::ListIndex) on a struct variant, or a [`TupleIndex`](Access::TupleIndex) on a unit variant)
    IncompatibleEnumVariantTypes {
        /// The [`VariantType`] that was expected based on the [`Access`].
        expected: VariantType,
        /// The actual [`VariantType`] that was found.
        actual: VariantType,
    },
}

impl AccessErrorKind {
    pub(super) fn with_access(self, access: Access, offset: Option<usize>) -> AccessError {
        AccessError {
            kind: self,
            access,
            offset,
        }
    }
}

/// An error originating from an [`Access`] of an element within a type.
///
/// Use the `Display` impl of this type to get information on the error.
///
/// Some sample messages:
///
/// ```text
/// Error accessing element with `.alpha` access (offset 14): The struct accessed doesn't have an "alpha" field
/// Error accessing element with '[0]' access: Expected index access to access a list, found a struct instead.
/// Error accessing element with '.4' access: Expected variant index access to access a Tuple variant, found a Unit variant instead.
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccessError<'a> {
    pub(super) kind: AccessErrorKind,
    pub(super) access: Access<'a>,
    pub(super) offset: Option<usize>,
}

impl<'a> AccessError<'a> {
    /// Returns the kind of [`AccessError`].
    pub const fn kind(&self) -> &AccessErrorKind {
        &self.kind
    }

    /// The returns the [`Access`] that this [`AccessError`] occurred in.
    pub const fn access(&self) -> &Access {
        &self.access
    }

    /// If the [`Access`] was created with a parser or an offset was manually provided,
    /// returns the offset of the [`Access`] in its path string.
    pub const fn offset(&self) -> Option<&usize> {
        self.offset.as_ref()
    }
}
impl std::fmt::Display for AccessError<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let AccessError {
            kind,
            access,
            offset,
        } = self;

        write!(f, "Error accessing element with `{access}` access")?;
        if let Some(offset) = offset {
            write!(f, "(offset {offset})")?;
        }
        write!(f, ": ")?;

        match kind {
            AccessErrorKind::MissingField(type_accessed) => {
                match access {
                    Access::Field(field) => write!(
                        f,
                        "The {type_accessed} accessed doesn't have {} `{}` field",
                        if let Some("a" | "e" | "i" | "o" | "u") = field.get(0..1) {
                            "an"
                        } else {
                            "a"
                        },
                        access.display_value()
                    ),
                    Access::FieldIndex(_) => write!(
                        f,
                        "The {type_accessed} accessed doesn't have field index `{}`",
                        access.display_value(),
                    ),
                    Access::TupleIndex(_) | Access::ListIndex(_) => write!(
                        f,
                        "The {type_accessed} accessed doesn't have index `{}`",
                        access.display_value()
                    )
                }
            }
            AccessErrorKind::IncompatibleTypes { expected, actual } => write!(
                f,
                "Expected {} access to access a {expected}, found a {actual} instead.",
                access.kind()
            ),
            AccessErrorKind::IncompatibleEnumVariantTypes { expected, actual } => write!(
                f,
                "Expected variant {} access to access a {expected:?} variant, found a {actual:?} variant instead.",
                access.kind()
            ),
        }
    }
}
impl std::error::Error for AccessError<'_> {}