iota_grpc_client/
response_ext.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4use iota_grpc_types::headers;
5use iota_sdk_types::Digest;
6
7/// Extension trait used to facilitate retrieval of IOTA specific data from
8/// responses
9pub trait ResponseExt: sealed::Sealed {
10    fn chain_id(&self) -> Option<Digest>;
11    fn chain(&self) -> Option<&str>;
12    fn epoch(&self) -> Option<u64>;
13    fn checkpoint_height(&self) -> Option<u64>;
14    fn timestamp_ms(&self) -> Option<u64>;
15    fn timestamp(&self) -> Option<&str>;
16    fn lowest_available_checkpoint(&self) -> Option<u64>;
17    fn lowest_available_checkpoint_objects(&self) -> Option<u64>;
18    fn server_version(&self) -> Option<&str>;
19}
20
21impl ResponseExt for http::header::HeaderMap {
22    fn chain_id(&self) -> Option<Digest> {
23        self.get(headers::X_IOTA_CHAIN_ID)
24            .map(http::header::HeaderValue::as_bytes)
25            .and_then(|s| Digest::from_base58(s).ok())
26    }
27
28    fn chain(&self) -> Option<&str> {
29        self.get(headers::X_IOTA_CHAIN)
30            .and_then(|h| h.to_str().ok())
31    }
32
33    fn epoch(&self) -> Option<u64> {
34        self.get(headers::X_IOTA_EPOCH)
35            .and_then(|h| h.to_str().ok())
36            .and_then(|s| s.parse().ok())
37    }
38
39    fn checkpoint_height(&self) -> Option<u64> {
40        self.get(headers::X_IOTA_CHECKPOINT_HEIGHT)
41            .and_then(|h| h.to_str().ok())
42            .and_then(|s| s.parse().ok())
43    }
44
45    fn timestamp_ms(&self) -> Option<u64> {
46        self.get(headers::X_IOTA_TIMESTAMP_MS)
47            .and_then(|h| h.to_str().ok())
48            .and_then(|s| s.parse().ok())
49    }
50
51    fn timestamp(&self) -> Option<&str> {
52        self.get(headers::X_IOTA_TIMESTAMP)
53            .and_then(|h| h.to_str().ok())
54    }
55
56    fn lowest_available_checkpoint(&self) -> Option<u64> {
57        self.get(headers::X_IOTA_LOWEST_AVAILABLE_CHECKPOINT)
58            .and_then(|h| h.to_str().ok())
59            .and_then(|s| s.parse().ok())
60    }
61
62    fn lowest_available_checkpoint_objects(&self) -> Option<u64> {
63        self.get(headers::X_IOTA_LOWEST_AVAILABLE_CHECKPOINT_OBJECTS)
64            .and_then(|h| h.to_str().ok())
65            .and_then(|s| s.parse().ok())
66    }
67
68    fn server_version(&self) -> Option<&str> {
69        self.get(headers::X_IOTA_SERVER)
70            .and_then(|h| h.to_str().ok())
71    }
72}
73
74impl ResponseExt for tonic::metadata::MetadataMap {
75    fn chain_id(&self) -> Option<Digest> {
76        self.as_ref().chain_id()
77    }
78
79    fn chain(&self) -> Option<&str> {
80        self.as_ref().chain()
81    }
82
83    fn epoch(&self) -> Option<u64> {
84        self.as_ref().epoch()
85    }
86
87    fn checkpoint_height(&self) -> Option<u64> {
88        self.as_ref().checkpoint_height()
89    }
90
91    fn timestamp_ms(&self) -> Option<u64> {
92        self.as_ref().timestamp_ms()
93    }
94
95    fn timestamp(&self) -> Option<&str> {
96        self.as_ref().timestamp()
97    }
98
99    fn lowest_available_checkpoint(&self) -> Option<u64> {
100        self.as_ref().lowest_available_checkpoint()
101    }
102
103    fn lowest_available_checkpoint_objects(&self) -> Option<u64> {
104        self.as_ref().lowest_available_checkpoint_objects()
105    }
106
107    fn server_version(&self) -> Option<&str> {
108        self.as_ref().server_version()
109    }
110}
111
112impl<T> ResponseExt for tonic::Response<T> {
113    fn chain_id(&self) -> Option<Digest> {
114        self.metadata().chain_id()
115    }
116
117    fn chain(&self) -> Option<&str> {
118        self.metadata().chain()
119    }
120
121    fn epoch(&self) -> Option<u64> {
122        self.metadata().epoch()
123    }
124
125    fn checkpoint_height(&self) -> Option<u64> {
126        self.metadata().checkpoint_height()
127    }
128
129    fn timestamp_ms(&self) -> Option<u64> {
130        self.metadata().timestamp_ms()
131    }
132
133    fn timestamp(&self) -> Option<&str> {
134        self.metadata().timestamp()
135    }
136
137    fn lowest_available_checkpoint(&self) -> Option<u64> {
138        self.metadata().lowest_available_checkpoint()
139    }
140
141    fn lowest_available_checkpoint_objects(&self) -> Option<u64> {
142        self.metadata().lowest_available_checkpoint_objects()
143    }
144
145    fn server_version(&self) -> Option<&str> {
146        self.metadata().server_version()
147    }
148}
149
150impl ResponseExt for tonic::Status {
151    fn chain_id(&self) -> Option<Digest> {
152        self.metadata().chain_id()
153    }
154
155    fn chain(&self) -> Option<&str> {
156        self.metadata().chain()
157    }
158
159    fn epoch(&self) -> Option<u64> {
160        self.metadata().epoch()
161    }
162
163    fn checkpoint_height(&self) -> Option<u64> {
164        self.metadata().checkpoint_height()
165    }
166
167    fn timestamp_ms(&self) -> Option<u64> {
168        self.metadata().timestamp_ms()
169    }
170
171    fn timestamp(&self) -> Option<&str> {
172        self.metadata().timestamp()
173    }
174
175    fn lowest_available_checkpoint(&self) -> Option<u64> {
176        self.metadata().lowest_available_checkpoint()
177    }
178
179    fn lowest_available_checkpoint_objects(&self) -> Option<u64> {
180        self.metadata().lowest_available_checkpoint_objects()
181    }
182
183    fn server_version(&self) -> Option<&str> {
184        self.metadata().server_version()
185    }
186}
187
188mod sealed {
189    pub trait Sealed {}
190
191    impl Sealed for tonic::Status {}
192    impl<T> Sealed for tonic::Response<T> {}
193    impl Sealed for http::header::HeaderMap {}
194    impl Sealed for tonic::metadata::MetadataMap {}
195    impl<T> Sealed for crate::api::MetadataEnvelope<T> {}
196}