1use std::fmt;
2
3pub type PluginResult<T> = Result<T, PluginError>;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum PluginErrorKind {
7 InvalidInput,
8 Domain,
9 LimitExceeded,
10 Cancelled,
11 Internal,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct PluginError {
16 kind: PluginErrorKind,
17 detail: String,
18 field: Option<String>,
19}
20
21impl PluginError {
22 pub fn new(kind: PluginErrorKind, detail: impl Into<String>) -> Self {
23 Self {
24 kind,
25 detail: bound_utf8(detail.into(), 4096),
26 field: None,
27 }
28 }
29
30 pub fn invalid_input(detail: impl Into<String>) -> Self {
31 Self::new(PluginErrorKind::InvalidInput, detail)
32 }
33
34 pub fn domain(detail: impl Into<String>) -> Self {
35 Self::new(PluginErrorKind::Domain, detail)
36 }
37
38 pub fn limit_exceeded(detail: impl Into<String>) -> Self {
39 Self::new(PluginErrorKind::LimitExceeded, detail)
40 }
41
42 pub fn cancelled() -> Self {
43 Self::new(PluginErrorKind::Cancelled, "plugin call cancelled")
44 }
45
46 pub fn internal(detail: impl Into<String>) -> Self {
47 Self::new(PluginErrorKind::Internal, detail)
48 }
49
50 pub fn with_field(mut self, field: impl Into<String>) -> Self {
51 self.field = Some(bound_utf8(field.into(), 255));
52 self
53 }
54
55 pub fn kind(&self) -> PluginErrorKind {
56 self.kind
57 }
58
59 pub fn detail(&self) -> &str {
60 &self.detail
61 }
62
63 pub fn field(&self) -> Option<&str> {
64 self.field.as_deref()
65 }
66}
67
68impl fmt::Display for PluginError {
69 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
70 formatter.write_str(&self.detail)
71 }
72}
73
74impl std::error::Error for PluginError {}
75
76fn bound_utf8(mut value: String, max_bytes: usize) -> String {
77 if value.len() <= max_bytes {
78 return value;
79 }
80 let mut end = max_bytes;
81 while !value.is_char_boundary(end) {
82 end -= 1;
83 }
84 value.truncate(end);
85 value
86}