hive_router_plan_executor/execution/
error.rs1use strum::IntoStaticStr;
2
3use crate::{
4 headers::errors::HeaderRuleRuntimeError,
5 projection::error::ProjectionError,
6 response::graphql_error::{GraphQLError, GraphQLErrorExtensions},
7};
8
9#[derive(thiserror::Error, Debug, Clone, IntoStaticStr)]
10pub enum PlanExecutionErrorKind {
11 #[error("Projection failure: {0}")]
12 #[strum(serialize = "PROJECTION_FAILURE")]
13 ProjectionFailure(#[from] ProjectionError),
14
15 #[error(transparent)]
16 #[strum(serialize = "HEADER_PROPAGATION_FAILURE")]
17 HeaderPropagation(#[from] HeaderRuleRuntimeError),
18}
19
20#[derive(thiserror::Error, Debug, Clone)]
26#[error("{kind}")]
27pub struct PlanExecutionError {
28 #[source]
29 kind: PlanExecutionErrorKind,
30 context: PlanExecutionErrorContext,
31}
32
33#[derive(Debug, Clone)]
34pub struct PlanExecutionErrorContext {
35 subgraph_name: Option<String>,
36 affected_path: Option<String>,
37}
38
39pub struct LazyPlanContext<SN, AP> {
40 pub subgraph_name: SN,
41 pub affected_path: AP,
42}
43
44impl PlanExecutionError {
45 pub(crate) fn new<SN, AP>(
46 kind: PlanExecutionErrorKind,
47 lazy_context: LazyPlanContext<SN, AP>,
48 ) -> Self
49 where
50 SN: FnOnce() -> Option<String>,
51 AP: FnOnce() -> Option<String>,
52 {
53 Self {
54 kind,
55 context: PlanExecutionErrorContext {
56 subgraph_name: (lazy_context.subgraph_name)(),
57 affected_path: (lazy_context.affected_path)(),
58 },
59 }
60 }
61
62 pub fn error_code(&self) -> &'static str {
63 (&self.kind).into()
64 }
65
66 pub fn subgraph_name(&self) -> &Option<String> {
67 &self.context.subgraph_name
68 }
69
70 pub fn affected_path(&self) -> &Option<String> {
71 &self.context.affected_path
72 }
73}
74
75impl From<PlanExecutionError> for GraphQLError {
76 fn from(val: PlanExecutionError) -> Self {
77 let message = val.to_string();
78 GraphQLError {
79 extensions: GraphQLErrorExtensions {
80 code: Some(val.error_code().into()),
81 service_name: val.context.subgraph_name,
82 affected_path: val.context.affected_path,
83 extensions: Default::default(),
84 },
85 message,
86 locations: None,
87 path: None,
88 }
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}