hawdb_core/error/
resource.rs1use std::fmt::{Display, Formatter};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ResourceBudgetKind {
9 MemoryBytes,
10 SpillBytes,
11 SpillRuns,
12 MetadataEntries,
13}
14
15impl ResourceBudgetKind {
16 pub const fn as_str(self) -> &'static str {
17 match self {
18 Self::MemoryBytes => "memory_bytes",
19 Self::SpillBytes => "spill_bytes",
20 Self::SpillRuns => "spill_runs",
21 Self::MetadataEntries => "metadata_entries",
22 }
23 }
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum ResourceBudgetScope {
29 Account,
30 Query,
31 Operator,
32 SharedPool,
33 FileSystem,
34}
35
36impl ResourceBudgetScope {
37 pub const fn as_str(self) -> &'static str {
38 match self {
39 Self::Account => "account",
40 Self::Query => "query",
41 Self::Operator => "operator",
42 Self::SharedPool => "shared_pool",
43 Self::FileSystem => "file_system",
44 }
45 }
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct ResourceBudgetError {
61 pub resource: ResourceBudgetKind,
62 pub scope: ResourceBudgetScope,
63 pub owner: String,
64 pub memory_class: Option<&'static str>,
65 pub requested: u64,
66 pub used: u64,
67 pub budget: u64,
68 pub budget_option: &'static str,
69 pub message: String,
71}
72
73impl Display for ResourceBudgetError {
74 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
75 formatter.write_str(&self.message)
76 }
77}
78
79impl std::error::Error for ResourceBudgetError {}
80
81pub(super) fn find_resource_budget_error(
82 error: &(dyn std::error::Error + 'static),
83) -> Option<ResourceBudgetError> {
84 let mut current = Some(error);
85 while let Some(error) = current {
86 if let Some(resource) = error.downcast_ref::<ResourceBudgetError>() {
87 return Some(resource.clone());
88 }
89 if let Some(error) = error.downcast_ref::<std::io::Error>()
91 && let Some(inner) = error.get_ref()
92 && let Some(resource) = find_resource_budget_error(inner)
93 {
94 return Some(resource);
95 }
96 current = error.source();
97 }
98 None
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::HawDBError;
105
106 #[test]
107 fn resource_metadata_survives_nested_io_and_storage_contexts() {
108 let cause = ResourceBudgetError {
109 resource: ResourceBudgetKind::MemoryBytes,
110 scope: ResourceBudgetScope::Account,
111 owner: "source".into(),
112 memory_class: Some("external_read"),
113 requested: 8,
114 used: 4,
115 budget: 10,
116 budget_option: "source grant",
117 message: "source budget refused".into(),
118 };
119 let expected = HawDBError::from(cause.clone());
120 for inner in [
121 std::io::Error::other(cause),
122 std::io::Error::other(expected.clone()),
123 ] {
124 let nested = std::io::Error::other(std::io::Error::other(inner));
125 assert_eq!(
126 HawDBError::from_storage_error_with_context(nested, "read node"),
127 expected
128 );
129 }
130 assert!(matches!(
131 HawDBError::from_storage_error(std::io::Error::other("ordinary I/O failure")),
132 HawDBError::Storage(_)
133 ));
134 }
135}