hive_router_plan_executor/execution/
error.rs1use strum::IntoStaticStr;
2
3use crate::{
4 executors::error::SubgraphExecutorError, headers::errors::HeaderRuleRuntimeError,
5 projection::error::ProjectionError, response::graphql_error::GraphQLError,
6};
7
8#[derive(thiserror::Error, Debug, Clone, IntoStaticStr)]
9pub enum PlanExecutionErrorKind {
10 #[error("Projection failure: {0}")]
11 #[strum(serialize = "PROJECTION_FAILURE")]
12 ProjectionFailure(#[from] ProjectionError),
13
14 #[error(transparent)]
15 #[strum(serialize = "HEADER_PROPAGATION_FAILURE")]
16 HeaderPropagation(#[from] HeaderRuleRuntimeError),
17
18 #[error(transparent)]
19 #[strum(serialize = "SUBGRAPH_EXECUTION_FAILURE")]
20 SubgraphExecutor(#[from] SubgraphExecutorError),
21}
22
23#[derive(thiserror::Error, Debug, Clone)]
29#[error("{kind}")]
30pub struct PlanExecutionError {
31 #[source]
32 kind: PlanExecutionErrorKind,
33 context: PlanExecutionErrorContext,
34}
35
36#[derive(Debug, Clone)]
37pub struct PlanExecutionErrorContext {
38 subgraph_name: Option<String>,
39 affected_path: Option<String>,
40}
41
42pub struct LazyPlanContext<SN, AP> {
43 pub subgraph_name: SN,
44 pub affected_path: AP,
45}
46
47impl PlanExecutionError {
48 pub(crate) fn new<SN, AP>(
49 kind: PlanExecutionErrorKind,
50 lazy_context: LazyPlanContext<SN, AP>,
51 ) -> Self
52 where
53 SN: FnOnce() -> Option<String>,
54 AP: FnOnce() -> Option<String>,
55 {
56 Self {
57 kind,
58 context: PlanExecutionErrorContext {
59 subgraph_name: (lazy_context.subgraph_name)(),
60 affected_path: (lazy_context.affected_path)(),
61 },
62 }
63 }
64
65 pub fn error_code(&self) -> &'static str {
66 if let PlanExecutionErrorKind::SubgraphExecutor(subgraph_error) = &self.kind {
67 return subgraph_error.error_code();
68 }
69 (&self.kind).into()
70 }
71
72 pub fn subgraph_name(&self) -> &Option<String> {
73 &self.context.subgraph_name
74 }
75
76 pub fn affected_path(&self) -> &Option<String> {
77 &self.context.affected_path
78 }
79}
80
81impl From<PlanExecutionError> for GraphQLError {
88 fn from(val: PlanExecutionError) -> Self {
89 let mut error = GraphQLError::from_message_and_code(val.to_string(), val.error_code());
90
91 if let Some(subgraph_name) = val.context.subgraph_name {
96 error = error.add_subgraph_name(subgraph_name);
97 }
98 if let Some(affected_path) = val.context.affected_path {
99 error = error.add_affected_path(affected_path);
100 }
101 error
102 }
103}
104
105pub trait IntoPlanExecutionError<T> {
110 fn with_plan_context<SN, AP>(
111 self,
112 context: LazyPlanContext<SN, AP>,
113 ) -> Result<T, PlanExecutionError>
114 where
115 SN: FnOnce() -> Option<String>,
116 AP: FnOnce() -> Option<String>;
117}
118
119impl<T> IntoPlanExecutionError<T> for Result<T, ProjectionError> {
120 fn with_plan_context<SN, AP>(
121 self,
122 context: LazyPlanContext<SN, AP>,
123 ) -> Result<T, PlanExecutionError>
124 where
125 SN: FnOnce() -> Option<String>,
126 AP: FnOnce() -> Option<String>,
127 {
128 self.map_err(|source| {
129 let kind = PlanExecutionErrorKind::ProjectionFailure(source);
130 PlanExecutionError::new(kind, context)
131 })
132 }
133}
134
135impl<T> IntoPlanExecutionError<T> for Result<T, HeaderRuleRuntimeError> {
136 fn with_plan_context<SN, AP>(
137 self,
138 context: LazyPlanContext<SN, AP>,
139 ) -> Result<T, PlanExecutionError>
140 where
141 SN: FnOnce() -> Option<String>,
142 AP: FnOnce() -> Option<String>,
143 {
144 self.map_err(|source| {
145 let kind = PlanExecutionErrorKind::HeaderPropagation(source);
146 PlanExecutionError::new(kind, context)
147 })
148 }
149}
150
151impl<T> IntoPlanExecutionError<T> for Result<T, SubgraphExecutorError> {
152 fn with_plan_context<SN, AP>(
153 self,
154 context: LazyPlanContext<SN, AP>,
155 ) -> Result<T, PlanExecutionError>
156 where
157 SN: FnOnce() -> Option<String>,
158 AP: FnOnce() -> Option<String>,
159 {
160 self.map_err(|source| {
161 let kind = PlanExecutionErrorKind::SubgraphExecutor(source);
162 PlanExecutionError::new(kind, context)
163 })
164 }
165}