iota_grpc_types/proto/
mod.rs1#![allow(clippy::large_enum_variant)]
6#![allow(clippy::doc_overindented_list_items)]
7#![allow(clippy::module_inception)]
8
9use google::rpc::bad_request::FieldViolation;
10use iota::grpc::v1::error_reason::ErrorReason;
11
12pub(crate) mod google;
13pub(crate) mod iota;
14
15pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
16
17#[derive(Debug)]
18pub struct TryFromProtoError {
19 field_violation: FieldViolation,
20 source: Option<BoxError>,
21}
22
23impl std::fmt::Display for TryFromProtoError {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 write!(f, "error converting from protobuf: ")?;
26
27 write!(f, "field: {}", self.field_violation.field)?;
28
29 if !self.field_violation.reason.is_empty() {
30 write!(f, " reason: {}", self.field_violation.reason)?;
31 }
32
33 if !self.field_violation.description.is_empty() {
34 write!(f, " description: {}", self.field_violation.description)?;
35 }
36
37 Ok(())
38 }
39}
40
41impl std::error::Error for TryFromProtoError {
42 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
43 self.source.as_deref().map(|s| s as _)
44 }
45}
46
47impl TryFromProtoError {
48 pub fn nested<T: AsRef<str>>(mut self, field: T) -> Self {
49 let field = field.as_ref();
50 self.field_violation = self.field_violation.nested(field);
51 self
52 }
53
54 pub fn nested_at<T: AsRef<str>>(mut self, field: T, index: usize) -> Self {
55 let field = field.as_ref();
56 self.field_violation = self.field_violation.nested_at(field, index);
57 self
58 }
59
60 pub fn missing<T: AsRef<str>>(field: T) -> Self {
61 let field = field.as_ref();
62
63 Self {
64 field_violation: FieldViolation::new(field).with_reason(ErrorReason::FieldMissing),
65 source: None,
66 }
67 }
68
69 pub fn invalid<T: AsRef<str>, E: Into<BoxError>>(field: T, error: E) -> Self {
70 let field = field.as_ref();
71 let error = error.into();
72
73 Self {
74 field_violation: FieldViolation::new(field)
75 .with_reason(ErrorReason::FieldInvalid)
76 .with_description(error.to_string()),
77 source: Some(error),
78 }
79 }
80
81 pub fn field_violation(&self) -> &FieldViolation {
82 &self.field_violation
83 }
84}
85
86macro_rules! get_inner_field {
94 ($field:expr, $FIELD:expr, try_into) => {{
97 <_ as core::convert::TryInto<_>>::try_into(
98 $field
99 .as_ref()
100 .ok_or_else(|| $crate::proto::TryFromProtoError::missing($FIELD.name))?,
101 )
102 .map_err(|e: $crate::proto::TryFromProtoError| e.nested($FIELD.name))
103 }};
104 ($field:expr, $FIELD:expr, $inner:ident) => {{
106 $field
107 .as_ref()
108 .ok_or_else(|| $crate::proto::TryFromProtoError::missing($FIELD.name))?
109 .$inner()
110 .map_err(|e| e.nested($FIELD.name))
111 }};
112}
113
114pub(crate) use get_inner_field;
115
116#[derive(Debug)]
117pub enum GrpcConversionError {
118 UnsupportedArgumentType { arg_type: String },
119 BcsSerializationFailed { message: String },
120}
121
122impl std::fmt::Display for GrpcConversionError {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 match self {
125 Self::UnsupportedArgumentType { arg_type } => {
126 write!(
127 f,
128 "Unsupported argument type for gRPC conversion: {}",
129 arg_type
130 )
131 }
132 Self::BcsSerializationFailed { message } => {
133 write!(f, "Failed to serialize BCS data: {}", message)
134 }
135 }
136 }
137}
138
139impl std::error::Error for GrpcConversionError {}
140
141pub fn timestamp_ms_to_proto(timestamp_ms: u64) -> prost_types::Timestamp {
145 let timestamp = std::time::Duration::from_millis(timestamp_ms);
146 prost_types::Timestamp {
147 seconds: timestamp.as_secs() as i64,
148 nanos: timestamp.subsec_nanos() as i32,
149 }
150}
151
152#[allow(clippy::result_large_err)]
153pub fn proto_to_timestamp_ms(timestamp: prost_types::Timestamp) -> Result<u64, TryFromProtoError> {
154 let seconds = std::time::Duration::from_secs(
155 timestamp
156 .seconds
157 .try_into()
158 .map_err(|e| TryFromProtoError::invalid("seconds", e))?,
159 );
160 let nanos = std::time::Duration::from_nanos(
161 timestamp
162 .nanos
163 .try_into()
164 .map_err(|e| TryFromProtoError::invalid("nanos", e))?,
165 );
166
167 (seconds + nanos)
168 .as_millis()
169 .try_into()
170 .map_err(|e| TryFromProtoError::invalid("seconds + nanos", e))
171}
172
173pub fn prost_to_json(value: &prost_types::Value) -> serde_json::Value {
178 use prost_types::value::Kind;
179
180 match &value.kind {
181 None => serde_json::Value::Null,
182 Some(Kind::NullValue(_)) => serde_json::Value::Null,
183 Some(Kind::NumberValue(n)) => serde_json::json!(*n),
184 Some(Kind::StringValue(s)) => serde_json::Value::String(s.clone()),
185 Some(Kind::BoolValue(b)) => serde_json::Value::Bool(*b),
186 Some(Kind::StructValue(s)) => {
187 let map: serde_json::Map<String, serde_json::Value> = s
188 .fields
189 .iter()
190 .map(|(k, v)| (k.clone(), prost_to_json(v)))
191 .collect();
192 serde_json::Value::Object(map)
193 }
194 Some(Kind::ListValue(l)) => {
195 let arr: Vec<serde_json::Value> = l.values.iter().map(prost_to_json).collect();
196 serde_json::Value::Array(arr)
197 }
198 }
199}