hive_router_plan_executor/execution/
error.rs1use strum::IntoStaticStr;
2
3use crate::{
4 headers::errors::HeaderRuleRuntimeError, projection::error::ProjectionError,
5 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
19#[derive(thiserror::Error, Debug, Clone)]
25#[error("{kind}")]
26pub struct PlanExecutionError {
27 #[source]
28 kind: PlanExecutionErrorKind,
29 context: PlanExecutionErrorContext,
30}
31
32#[derive(Debug, Clone)]
33pub struct PlanExecutionErrorContext {
34 subgraph_name: Option<String>,
35 affected_path: Option<String>,
36}
37
38pub struct LazyPlanContext<SN, AP> {
39 pub subgraph_name: SN,
40 pub affected_path: AP,
41}
42
43impl PlanExecutionError {
44 pub(crate) fn new<SN, AP>(
45 kind: PlanExecutionErrorKind,
46 lazy_context: LazyPlanContext<SN, AP>,
47 ) -> Self
48 where
49 SN: FnOnce() -> Option<String>,
50 AP: FnOnce() -> Option<String>,
51 {
52 Self {
53 kind,
54 context: PlanExecutionErrorContext {
55 subgraph_name: (lazy_context.subgraph_name)(),
56 affected_path: (lazy_context.affected_path)(),
57 },
58 }
59 }
60
61 pub fn error_code(&self) -> &'static str {
62 (&self.kind).into()
63 }
64
65 pub fn subgraph_name(&self) -> &Option<String> {
66 &self.context.subgraph_name
67 }
68
69 pub fn affected_path(&self) -> &Option<String> {
70 &self.context.affected_path
71 }
72}
73
74impl From<PlanExecutionError> for GraphQLError {
75 fn from(val: PlanExecutionError) -> Self {
76 let mut error = GraphQLError::from_message_and_code(val.to_string(), val.error_code());
77
78 if let Some(subgraph_name) = val.context.subgraph_name {
83 error = error.add_subgraph_name(subgraph_name);
84 }
85 if let Some(affected_path) = val.context.affected_path {
86 error = error.add_affected_path(affected_path);
87 }
88 error
89 }
90}
91
92pub trait IntoPlanExecutionError<T> {
97 fn with_plan_context<SN, AP>(
98 self,
99 context: LazyPlanContext<SN, AP>,
100 ) -> Result<T, PlanExecutionError>
101 where
102 SN: FnOnce() -> Option<String>,
103 AP: FnOnce() -> Option<String>;
104}
105
106impl<T> IntoPlanExecutionError<T> for Result<T, ProjectionError> {
107 fn with_plan_context<SN, AP>(
108 self,
109 context: LazyPlanContext<SN, AP>,
110 ) -> Result<T, PlanExecutionError>
111 where
112 SN: FnOnce() -> Option<String>,
113 AP: FnOnce() -> Option<String>,
114 {
115 self.map_err(|source| {
116 let kind = PlanExecutionErrorKind::ProjectionFailure(source);
117 PlanExecutionError::new(kind, context)
118 })
119 }
120}
121
122impl<T> IntoPlanExecutionError<T> for Result<T, HeaderRuleRuntimeError> {
123 fn with_plan_context<SN, AP>(
124 self,
125 context: LazyPlanContext<SN, AP>,
126 ) -> Result<T, PlanExecutionError>
127 where
128 SN: FnOnce() -> Option<String>,
129 AP: FnOnce() -> Option<String>,
130 {
131 self.map_err(|source| {
132 let kind = PlanExecutionErrorKind::HeaderPropagation(source);
133 PlanExecutionError::new(kind, context)
134 })
135 }
136}