Skip to main content

wowlab_engine_application/enemies/
error.rs

1//! Typed errors for enemy-stat resolution.
2
3use wowlab_engine_ports::ResolverError;
4
5use super::provenance::EnemyStatField;
6
7wowlab_engine_macros::define_error! {
8/// Per-component failure to resolve one enemy stat.
9#[derive(Clone, Debug, PartialEq)]
10pub struct EnemyStatError {
11    kind: EnemyStatErrorKind,
12}
13
14#[derive(Clone, Debug, thiserror::Error, PartialEq)]
15enum EnemyStatErrorKind {
16    #[error("missing enemy data for {field}: {key}")]
17    Missing { field: EnemyStatField, key: String },
18    #[error("ambiguous enemy data for {field}: candidate rows {candidate_row_ids:?}")]
19    Ambiguous {
20        field: EnemyStatField,
21        candidate_row_ids: Vec<i32>,
22    },
23    #[error("invalid enemy data for {field} from {key}: {found}")]
24    Invalid {
25        field: EnemyStatField,
26        key: String,
27        found: f64,
28    },
29}
30}
31
32impl EnemyStatError {
33    pub(super) fn missing(field: EnemyStatField, key: String) -> Self {
34        Self {
35            kind: EnemyStatErrorKind::Missing { field, key },
36        }
37    }
38
39    pub(super) fn ambiguous(field: EnemyStatField, candidate_row_ids: Vec<i32>) -> Self {
40        Self {
41            kind: EnemyStatErrorKind::Ambiguous {
42                field,
43                candidate_row_ids,
44            },
45        }
46    }
47
48    pub(super) fn invalid(field: EnemyStatField, key: String, found: f64) -> Self {
49        Self {
50            kind: EnemyStatErrorKind::Invalid { field, key, found },
51        }
52    }
53
54    #[must_use]
55    pub fn is_missing(&self, field: EnemyStatField) -> bool {
56        matches!(self.kind, EnemyStatErrorKind::Missing { field: found, .. } if found == field)
57    }
58
59    #[must_use]
60    pub fn is_ambiguous(&self, field: EnemyStatField) -> bool {
61        matches!(self.kind, EnemyStatErrorKind::Ambiguous { field: found, .. } if found == field)
62    }
63}
64
65wowlab_engine_macros::define_error! {
66/// Failure to resolve the enemy-stat service result.
67#[derive(Debug)]
68pub struct EnemyStatResolutionError {
69    kind: EnemyStatResolutionErrorKind,
70}
71
72#[derive(Debug, thiserror::Error)]
73enum EnemyStatResolutionErrorKind {
74    #[error(transparent)]
75    Data(EnemyStatError),
76    #[error("authored {name} {value} exceeds the i32 data key range")]
77    InputOutOfRange {
78        name: &'static str,
79        value: u32,
80        #[source]
81        source: std::num::TryFromIntError,
82    },
83    #[error("data backend failed during enemy stat resolution at {key}")]
84    Resolver {
85        key: String,
86        #[source]
87        source: ResolverError,
88    },
89}
90}
91
92impl EnemyStatResolutionError {
93    pub(super) fn input_out_of_range(
94        name: &'static str,
95        value: u32,
96        source: std::num::TryFromIntError,
97    ) -> Self {
98        Self {
99            kind: EnemyStatResolutionErrorKind::InputOutOfRange {
100                name,
101                value,
102                source,
103            },
104        }
105    }
106
107    pub(super) fn resolver(key: String, source: ResolverError) -> Self {
108        Self {
109            kind: EnemyStatResolutionErrorKind::Resolver { key, source },
110        }
111    }
112
113    pub fn into_data(self) -> Result<EnemyStatError, Self> {
114        match self.kind {
115            EnemyStatResolutionErrorKind::Data(error) => Ok(error),
116            kind => Err(Self { kind }),
117        }
118    }
119}
120
121impl From<EnemyStatError> for EnemyStatResolutionError {
122    fn from(error: EnemyStatError) -> Self {
123        Self {
124            kind: EnemyStatResolutionErrorKind::Data(error),
125        }
126    }
127}