1#![allow(clippy::large_enum_variant)]
2#![allow(clippy::doc_overindented_list_items)]
3
4use google::rpc::bad_request::FieldViolation;
5use sui::rpc::v2::ErrorReason;
6
7pub mod google;
8pub mod sui;
9
10type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
11
12#[derive(Debug)]
13pub struct TryFromProtoError {
14 field_violation: Box<FieldViolation>,
15 source: Option<BoxError>,
16}
17
18impl std::fmt::Display for TryFromProtoError {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 write!(f, "error converting from protobuf: ")?;
21
22 write!(f, "field: {}", self.field_violation.field)?;
23
24 if !self.field_violation.reason.is_empty() {
25 write!(f, " reason: {}", self.field_violation.reason)?;
26 }
27
28 if !self.field_violation.description.is_empty() {
29 write!(f, " description: {}", self.field_violation.description)?;
30 }
31
32 Ok(())
33 }
34}
35
36impl std::error::Error for TryFromProtoError {
37 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
38 self.source.as_deref().map(|s| s as _)
39 }
40}
41
42impl TryFromProtoError {
43 pub fn nested<T: AsRef<str>>(mut self, field: T) -> Self {
44 let fv = std::mem::take(&mut *self.field_violation);
45 *self.field_violation = fv.nested(field.as_ref());
46 self
47 }
48
49 pub fn nested_at<T: AsRef<str>>(mut self, field: T, index: usize) -> Self {
50 let fv = std::mem::take(&mut *self.field_violation);
51 *self.field_violation = fv.nested_at(field.as_ref(), index);
52 self
53 }
54
55 pub fn missing<T: AsRef<str>>(field: T) -> Self {
56 let field = field.as_ref();
57
58 Self {
59 field_violation: Box::new(
60 FieldViolation::new(field).with_reason(ErrorReason::FieldMissing),
61 ),
62 source: None,
63 }
64 }
65
66 pub fn invalid<T: AsRef<str>, E: Into<BoxError>>(field: T, error: E) -> Self {
67 let field = field.as_ref();
68 let error = error.into();
69
70 Self {
71 field_violation: Box::new(
72 FieldViolation::new(field)
73 .with_reason(ErrorReason::FieldInvalid)
74 .with_description(error.to_string()),
75 ),
76 source: Some(error),
77 }
78 }
79
80 pub fn field_violation(&self) -> &FieldViolation {
81 &self.field_violation
82 }
83}
84
85pub fn timestamp_ms_to_proto(timestamp_ms: u64) -> prost_types::Timestamp {
90 let timestamp = std::time::Duration::from_millis(timestamp_ms);
91 prost_types::Timestamp {
92 seconds: timestamp.as_secs() as i64,
93 nanos: timestamp.subsec_nanos() as i32,
94 }
95}
96
97pub fn proto_to_timestamp_ms(timestamp: prost_types::Timestamp) -> Result<u64, TryFromProtoError> {
98 let seconds = std::time::Duration::from_secs(
99 timestamp
100 .seconds
101 .try_into()
102 .map_err(|e| TryFromProtoError::invalid("seconds", e))?,
103 );
104 let nanos = std::time::Duration::from_nanos(
105 timestamp
106 .nanos
107 .try_into()
108 .map_err(|e| TryFromProtoError::invalid("nanos", e))?,
109 );
110
111 (seconds + nanos)
112 .as_millis()
113 .try_into()
114 .map_err(|e| TryFromProtoError::invalid("seconds + nanos", e))
115}