weavatrix_refactor_plan/
error.rs1use serde::{Deserialize, Serialize};
2use std::fmt;
3use weavatrix_edit::{EditError, ErrorCode as EditErrorCode};
4
5#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
7#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
8pub enum PlanErrorCode {
9 BasePlanInvalid,
10 SchemaMismatch,
11 InvalidPlan,
12 EvidenceMalformed,
13 EvidenceMissing,
14 EvidenceInvalid,
15 EvidenceTooLarge,
16 PlanTooLarge,
17 UnsafePath,
18 OperationConflict,
19 ExtensionConflict,
20 UnsafeNumber,
21 JsonEncoding,
22 NotTextOnly,
23}
24
25impl PlanErrorCode {
26 #[must_use]
28 pub const fn as_str(self) -> &'static str {
29 match self {
30 Self::BasePlanInvalid => "BASE_PLAN_INVALID",
31 Self::SchemaMismatch => "SCHEMA_MISMATCH",
32 Self::InvalidPlan => "INVALID_PLAN",
33 Self::EvidenceMalformed => "EVIDENCE_MALFORMED",
34 Self::EvidenceMissing => "EVIDENCE_MISSING",
35 Self::EvidenceInvalid => "EVIDENCE_INVALID",
36 Self::EvidenceTooLarge => "EVIDENCE_TOO_LARGE",
37 Self::PlanTooLarge => "PLAN_TOO_LARGE",
38 Self::UnsafePath => "UNSAFE_PATH",
39 Self::OperationConflict => "OPERATION_CONFLICT",
40 Self::ExtensionConflict => "EXTENSION_CONFLICT",
41 Self::UnsafeNumber => "UNSAFE_NUMBER",
42 Self::JsonEncoding => "JSON_ENCODING",
43 Self::NotTextOnly => "NOT_TEXT_ONLY",
44 }
45 }
46}
47
48impl fmt::Display for PlanErrorCode {
49 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50 formatter.write_str(self.as_str())
51 }
52}
53
54#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
56#[serde(rename_all = "camelCase")]
57pub struct PlanError {
58 code: PlanErrorCode,
59 message: Box<str>,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 field: Option<Box<str>>,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 operation_index: Option<usize>,
64 #[serde(skip_serializing_if = "Option::is_none")]
65 edit_index: Option<usize>,
66 #[serde(skip_serializing_if = "Option::is_none")]
67 related_edit_index: Option<usize>,
68 #[serde(skip_serializing_if = "Option::is_none")]
69 path: Option<Box<str>>,
70 #[serde(skip_serializing_if = "Option::is_none")]
71 base_code: Option<EditErrorCode>,
72}
73
74impl PlanError {
75 pub(crate) fn new(code: PlanErrorCode, message: impl Into<String>) -> Self {
76 Self {
77 code,
78 message: message.into().into_boxed_str(),
79 field: None,
80 operation_index: None,
81 edit_index: None,
82 related_edit_index: None,
83 path: None,
84 base_code: None,
85 }
86 }
87
88 pub(crate) fn at_field(mut self, field: impl Into<String>) -> Self {
89 self.field = Some(field.into().into_boxed_str());
90 self
91 }
92
93 pub(crate) const fn at_operation(mut self, operation_index: usize) -> Self {
94 self.operation_index = Some(operation_index);
95 self
96 }
97
98 pub(crate) fn at_path(mut self, path: impl Into<String>) -> Self {
99 self.path = Some(path.into().into_boxed_str());
100 self
101 }
102
103 pub(crate) const fn with_code(mut self, code: PlanErrorCode) -> Self {
104 self.code = code;
105 self
106 }
107
108 pub(crate) fn from_edit(error: &EditError) -> Self {
109 Self {
110 code: PlanErrorCode::BasePlanInvalid,
111 message: error.message().into(),
112 field: None,
113 operation_index: None,
114 edit_index: error.edit_index(),
115 related_edit_index: error.related_edit_index(),
116 path: None,
117 base_code: Some(error.code()),
118 }
119 }
120
121 #[must_use]
123 pub const fn code(&self) -> PlanErrorCode {
124 self.code
125 }
126
127 #[must_use]
129 pub fn message(&self) -> &str {
130 &self.message
131 }
132
133 #[must_use]
135 pub fn field(&self) -> Option<&str> {
136 self.field.as_deref()
137 }
138
139 #[must_use]
141 pub const fn operation_index(&self) -> Option<usize> {
142 self.operation_index
143 }
144
145 #[must_use]
147 pub const fn edit_index(&self) -> Option<usize> {
148 self.edit_index
149 }
150
151 #[must_use]
153 pub const fn related_edit_index(&self) -> Option<usize> {
154 self.related_edit_index
155 }
156
157 #[must_use]
159 pub fn path(&self) -> Option<&str> {
160 self.path.as_deref()
161 }
162
163 #[must_use]
165 pub const fn base_code(&self) -> Option<EditErrorCode> {
166 self.base_code
167 }
168}
169
170impl fmt::Display for PlanError {
171 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
172 write!(formatter, "{}: {}", self.code, self.message)
173 }
174}
175
176impl std::error::Error for PlanError {}