Skip to main content

zingo_netutils/
error.rs

1//! Error types for the [`Indexer`](super::Indexer) and
2//! `TransparentIndexer` traits.
3//!
4//! Each trait method has a dedicated error enum so callers can
5//! distinguish connection failures ([`GetClientError`]) from
6//! server-side gRPC errors ([`tonic::Status`]).
7
8/// Connection-level error returned by [`GrpcIndexer::new`](super::GrpcIndexer::new)
9/// and the connection phase of every trait method.
10///
11/// Callers can depend on:
12/// - `InvalidScheme` and `InvalidAuthority` are deterministic — retrying
13///   with the same URI will always fail.
14/// - `Transport` wraps a [`tonic::transport::Error`] and may be transient
15///   (e.g. DNS resolution, TCP connect timeout). Retrying may succeed.
16///
17/// ```
18/// use zingo_netutils::GetClientError;
19///
20/// let e = GetClientError::InvalidScheme;
21/// assert_eq!(e.to_string(), "bad uri: invalid scheme");
22///
23/// let e = GetClientError::InvalidAuthority;
24/// assert_eq!(e.to_string(), "bad uri: invalid authority");
25///
26/// // Transport variant accepts From<tonic::transport::Error>
27/// let _: fn(tonic::transport::Error) -> GetClientError = GetClientError::from;
28/// ```
29#[derive(Debug, thiserror::Error)]
30pub enum GetClientError {
31    #[error("bad uri: invalid scheme")]
32    InvalidScheme,
33
34    #[error("bad uri: invalid authority")]
35    InvalidAuthority,
36
37    #[error(transparent)]
38    Transport(#[from] tonic::transport::Error),
39}
40
41#[cfg(test)]
42mod get_client_error_tests {
43    use super::*;
44
45    #[test]
46    fn invalid_scheme() {
47        let e = GetClientError::InvalidScheme;
48        assert!(matches!(e, GetClientError::InvalidScheme));
49        assert_eq!(e.to_string(), "bad uri: invalid scheme");
50    }
51
52    #[test]
53    fn invalid_authority() {
54        let e = GetClientError::InvalidAuthority;
55        assert!(matches!(e, GetClientError::InvalidAuthority));
56        assert_eq!(e.to_string(), "bad uri: invalid authority");
57    }
58
59    #[test]
60    fn transport_from_conversion() {
61        // Verify the From impl exists at compile time.
62        let _: fn(tonic::transport::Error) -> GetClientError = GetClientError::from;
63    }
64}
65
66/// Error from the `get_info` (`GetLightdInfo`) RPC.
67///
68/// Callers can depend on:
69/// - `GetClientError` means the connection was never established.
70/// - `GetLightdInfoError` means the server received the request but
71///   returned a gRPC status (e.g. `Unavailable`, `Internal`).
72///
73/// ```
74/// use zingo_netutils::{GetClientError, GetInfoError};
75///
76/// let e = GetInfoError::from(GetClientError::InvalidScheme);
77/// assert!(matches!(e, GetInfoError::GetClientError(_)));
78///
79/// let e = GetInfoError::from(tonic::Status::internal("oops"));
80/// assert!(matches!(e, GetInfoError::GetLightdInfoError(_)));
81/// assert!(e.to_string().contains("oops"));
82/// ```
83#[derive(Debug, thiserror::Error)]
84pub enum GetInfoError {
85    #[error(transparent)]
86    GetClientError(#[from] GetClientError),
87
88    #[error("gRPC error: {0}")]
89    GetLightdInfoError(#[from] tonic::Status),
90}
91
92/// Error from the `get_latest_block` (`GetLatestBlock`) RPC.
93///
94/// Callers can depend on:
95/// - `GetClientError` means the connection was never established.
96/// - `GetLatestBlockError` means the server returned a gRPC status.
97///
98/// ```
99/// use zingo_netutils::{GetClientError, GetLatestBlockError};
100///
101/// let e = GetLatestBlockError::from(GetClientError::InvalidScheme);
102/// assert!(matches!(e, GetLatestBlockError::GetClientError(_)));
103///
104/// let e = GetLatestBlockError::from(tonic::Status::internal("oops"));
105/// assert!(matches!(e, GetLatestBlockError::GetLatestBlockError(_)));
106/// ```
107#[derive(Debug, thiserror::Error)]
108pub enum GetLatestBlockError {
109    #[error(transparent)]
110    GetClientError(#[from] GetClientError),
111
112    #[error("gRPC error: {0}")]
113    GetLatestBlockError(#[from] tonic::Status),
114}
115
116/// Error from the `send_transaction` (`SendTransaction`) RPC.
117///
118/// Callers can depend on:
119/// - `GetClientError` means the connection was never established.
120/// - `SendTransactionError` means the server returned a gRPC status
121///   before evaluating the transaction.
122/// - `SendRejected` means the server received and evaluated the
123///   transaction but rejected it (e.g. duplicate, invalid). The string
124///   contains the server's rejection reason. This is **not** retryable
125///   with the same transaction bytes.
126///
127/// ```
128/// use zingo_netutils::{GetClientError, SendTransactionError};
129///
130/// let e = SendTransactionError::from(GetClientError::InvalidScheme);
131/// assert!(matches!(e, SendTransactionError::GetClientError(_)));
132///
133/// let e = SendTransactionError::from(tonic::Status::internal("oops"));
134/// assert!(matches!(e, SendTransactionError::SendTransactionError(_)));
135///
136/// let e = SendTransactionError::SendRejected("duplicate".into());
137/// assert!(matches!(e, SendTransactionError::SendRejected(_)));
138/// assert_eq!(e.to_string(), "send rejected: duplicate");
139/// ```
140#[derive(Debug, thiserror::Error)]
141pub enum SendTransactionError {
142    #[error(transparent)]
143    GetClientError(#[from] GetClientError),
144
145    #[error("gRPC error: {0}")]
146    SendTransactionError(#[from] tonic::Status),
147
148    #[error("send rejected: {0}")]
149    SendRejected(String),
150}
151
152/// Error from the `get_tree_state` (`GetTreeState`) RPC.
153///
154/// Callers can depend on:
155/// - `GetClientError` means the connection was never established.
156/// - `GetTreeStateError` means the server returned a gRPC status
157///   (e.g. the requested block does not exist).
158///
159/// ```
160/// use zingo_netutils::{GetClientError, GetTreeStateError};
161///
162/// let e = GetTreeStateError::from(GetClientError::InvalidScheme);
163/// assert!(matches!(e, GetTreeStateError::GetClientError(_)));
164///
165/// let e = GetTreeStateError::from(tonic::Status::not_found("no such block"));
166/// assert!(matches!(e, GetTreeStateError::GetTreeStateError(_)));
167/// ```
168#[derive(Debug, thiserror::Error)]
169pub enum GetTreeStateError {
170    #[error(transparent)]
171    GetClientError(#[from] GetClientError),
172
173    #[error("gRPC error: {0}")]
174    GetTreeStateError(#[from] tonic::Status),
175}
176
177/// Error from the `get_block` (`GetBlock`) RPC.
178///
179/// Callers can depend on:
180/// - `GetClientError` means the connection was never established.
181/// - `GetBlockError` means the server returned a gRPC status
182///   (e.g. the requested block does not exist).
183///
184/// ```
185/// use zingo_netutils::{GetClientError, GetBlockError};
186///
187/// let e = GetBlockError::from(GetClientError::InvalidScheme);
188/// assert!(matches!(e, GetBlockError::GetClientError(_)));
189///
190/// let e = GetBlockError::from(tonic::Status::not_found("no such block"));
191/// assert!(matches!(e, GetBlockError::GetBlockError(_)));
192/// ```
193#[derive(Debug, thiserror::Error)]
194pub enum GetBlockError {
195    #[error(transparent)]
196    GetClientError(#[from] GetClientError),
197
198    #[error("gRPC error: {0}")]
199    GetBlockError(#[from] tonic::Status),
200}
201
202/// Error from the deprecated `get_block_nullifiers` (`GetBlockNullifiers`) RPC.
203///
204/// Callers can depend on:
205/// - `GetClientError` means the connection was never established.
206/// - `GetBlockNullifiersError` means the server returned a gRPC status.
207///
208/// ```
209/// use zingo_netutils::{GetClientError, GetBlockNullifiersError};
210///
211/// let e = GetBlockNullifiersError::from(GetClientError::InvalidScheme);
212/// assert!(matches!(e, GetBlockNullifiersError::GetClientError(_)));
213///
214/// let e = GetBlockNullifiersError::from(tonic::Status::internal("oops"));
215/// assert!(matches!(e, GetBlockNullifiersError::GetBlockNullifiersError(_)));
216/// ```
217#[derive(Debug, thiserror::Error)]
218pub enum GetBlockNullifiersError {
219    #[error(transparent)]
220    GetClientError(#[from] GetClientError),
221
222    #[error("gRPC error: {0}")]
223    GetBlockNullifiersError(#[from] tonic::Status),
224}
225
226/// Error from the `get_block_range` (`GetBlockRange`) RPC.
227///
228/// Callers can depend on:
229/// - `GetClientError` means the connection was never established.
230/// - `GetBlockRangeError` means the server returned a gRPC status
231///   before or instead of streaming blocks.
232///
233/// ```
234/// use zingo_netutils::{GetClientError, GetBlockRangeError};
235///
236/// let e = GetBlockRangeError::from(GetClientError::InvalidScheme);
237/// assert!(matches!(e, GetBlockRangeError::GetClientError(_)));
238///
239/// let e = GetBlockRangeError::from(tonic::Status::internal("oops"));
240/// assert!(matches!(e, GetBlockRangeError::GetBlockRangeError(_)));
241/// ```
242#[derive(Debug, thiserror::Error)]
243pub enum GetBlockRangeError {
244    #[error(transparent)]
245    GetClientError(#[from] GetClientError),
246
247    #[error("gRPC error: {0}")]
248    GetBlockRangeError(#[from] tonic::Status),
249}
250
251/// Error from the deprecated `get_block_range_nullifiers` (`GetBlockRangeNullifiers`) RPC.
252///
253/// Callers can depend on:
254/// - `GetClientError` means the connection was never established.
255/// - `GetBlockRangeNullifiersError` means the server returned a gRPC status.
256///
257/// ```
258/// use zingo_netutils::{GetClientError, GetBlockRangeNullifiersError};
259///
260/// let e = GetBlockRangeNullifiersError::from(GetClientError::InvalidScheme);
261/// assert!(matches!(e, GetBlockRangeNullifiersError::GetClientError(_)));
262///
263/// let e = GetBlockRangeNullifiersError::from(tonic::Status::internal("oops"));
264/// assert!(matches!(e, GetBlockRangeNullifiersError::GetBlockRangeNullifiersError(_)));
265/// ```
266#[derive(Debug, thiserror::Error)]
267pub enum GetBlockRangeNullifiersError {
268    #[error(transparent)]
269    GetClientError(#[from] GetClientError),
270
271    #[error("gRPC error: {0}")]
272    GetBlockRangeNullifiersError(#[from] tonic::Status),
273}
274
275/// Error from the `get_transaction` (`GetTransaction`) RPC.
276///
277/// Callers can depend on:
278/// - `GetClientError` means the connection was never established.
279/// - `GetTransactionError` means the server returned a gRPC status
280///   (e.g. the transaction was not found).
281///
282/// ```
283/// use zingo_netutils::{GetClientError, GetTransactionError};
284///
285/// let e = GetTransactionError::from(GetClientError::InvalidScheme);
286/// assert!(matches!(e, GetTransactionError::GetClientError(_)));
287///
288/// let e = GetTransactionError::from(tonic::Status::not_found("no such tx"));
289/// assert!(matches!(e, GetTransactionError::GetTransactionError(_)));
290/// ```
291#[derive(Debug, thiserror::Error)]
292pub enum GetTransactionError {
293    #[error(transparent)]
294    GetClientError(#[from] GetClientError),
295
296    #[error("gRPC error: {0}")]
297    GetTransactionError(#[from] tonic::Status),
298}
299
300/// Error from the `get_mempool_tx` (`GetMempoolTx`) RPC.
301///
302/// Callers can depend on:
303/// - `GetClientError` means the connection was never established.
304/// - `GetMempoolTxError` means the server returned a gRPC status
305///   before or instead of streaming mempool transactions.
306///
307/// ```
308/// use zingo_netutils::{GetClientError, GetMempoolTxError};
309///
310/// let e = GetMempoolTxError::from(GetClientError::InvalidScheme);
311/// assert!(matches!(e, GetMempoolTxError::GetClientError(_)));
312///
313/// let e = GetMempoolTxError::from(tonic::Status::internal("oops"));
314/// assert!(matches!(e, GetMempoolTxError::GetMempoolTxError(_)));
315/// ```
316#[derive(Debug, thiserror::Error)]
317pub enum GetMempoolTxError {
318    #[error(transparent)]
319    GetClientError(#[from] GetClientError),
320
321    #[error("gRPC error: {0}")]
322    GetMempoolTxError(#[from] tonic::Status),
323}
324
325/// Error from the `get_mempool_stream` (`GetMempoolStream`) RPC.
326///
327/// Callers can depend on:
328/// - `GetClientError` means the connection was never established.
329/// - `GetMempoolStreamError` means the server returned a gRPC status
330///   before or instead of opening the stream.
331///
332/// ```
333/// use zingo_netutils::{GetClientError, GetMempoolStreamError};
334///
335/// let e = GetMempoolStreamError::from(GetClientError::InvalidScheme);
336/// assert!(matches!(e, GetMempoolStreamError::GetClientError(_)));
337///
338/// let e = GetMempoolStreamError::from(tonic::Status::internal("oops"));
339/// assert!(matches!(e, GetMempoolStreamError::GetMempoolStreamError(_)));
340/// ```
341#[derive(Debug, thiserror::Error)]
342pub enum GetMempoolStreamError {
343    #[error(transparent)]
344    GetClientError(#[from] GetClientError),
345
346    #[error("gRPC error: {0}")]
347    GetMempoolStreamError(#[from] tonic::Status),
348}
349
350/// Error from the `get_latest_tree_state` (`GetLatestTreeState`) RPC.
351///
352/// Callers can depend on:
353/// - `GetClientError` means the connection was never established.
354/// - `GetLatestTreeStateError` means the server returned a gRPC status.
355///
356/// ```
357/// use zingo_netutils::{GetClientError, GetLatestTreeStateError};
358///
359/// let e = GetLatestTreeStateError::from(GetClientError::InvalidScheme);
360/// assert!(matches!(e, GetLatestTreeStateError::GetClientError(_)));
361///
362/// let e = GetLatestTreeStateError::from(tonic::Status::internal("oops"));
363/// assert!(matches!(e, GetLatestTreeStateError::GetLatestTreeStateError(_)));
364/// ```
365#[derive(Debug, thiserror::Error)]
366pub enum GetLatestTreeStateError {
367    #[error(transparent)]
368    GetClientError(#[from] GetClientError),
369
370    #[error("gRPC error: {0}")]
371    GetLatestTreeStateError(#[from] tonic::Status),
372}
373
374/// Error from the `get_subtree_roots` (`GetSubtreeRoots`) RPC.
375///
376/// Callers can depend on:
377/// - `GetClientError` means the connection was never established.
378/// - `GetSubtreeRootsError` means the server returned a gRPC status
379///   before or instead of streaming subtree roots.
380///
381/// ```
382/// use zingo_netutils::{GetClientError, GetSubtreeRootsError};
383///
384/// let e = GetSubtreeRootsError::from(GetClientError::InvalidScheme);
385/// assert!(matches!(e, GetSubtreeRootsError::GetClientError(_)));
386///
387/// let e = GetSubtreeRootsError::from(tonic::Status::internal("oops"));
388/// assert!(matches!(e, GetSubtreeRootsError::GetSubtreeRootsError(_)));
389/// ```
390#[derive(Debug, thiserror::Error)]
391pub enum GetSubtreeRootsError {
392    #[error(transparent)]
393    GetClientError(#[from] GetClientError),
394
395    #[error("gRPC error: {0}")]
396    GetSubtreeRootsError(#[from] tonic::Status),
397}
398
399/// Error from the `ping` (`Ping`) RPC.
400///
401/// Callers can depend on:
402/// - `GetClientError` means the connection was never established.
403/// - `PingError` means the server returned a gRPC status (e.g. the
404///   server was not started with `--ping-very-insecure`).
405///
406/// ```
407/// # #[cfg(feature = "ping-very-insecure")]
408/// # {
409/// use zingo_netutils::{GetClientError, PingError};
410///
411/// let e = PingError::from(GetClientError::InvalidScheme);
412/// assert!(matches!(e, PingError::GetClientError(_)));
413///
414/// let e = PingError::from(tonic::Status::permission_denied("not enabled"));
415/// assert!(matches!(e, PingError::PingError(_)));
416/// # }
417/// ```
418#[cfg(feature = "ping-very-insecure")]
419#[derive(Debug, thiserror::Error)]
420pub enum PingError {
421    #[error(transparent)]
422    GetClientError(#[from] GetClientError),
423
424    #[error("gRPC error: {0}")]
425    PingError(#[from] tonic::Status),
426}
427
428/// Helper to construct a `tonic::Status` for testing.
429#[cfg(test)]
430fn test_status() -> tonic::Status {
431    tonic::Status::internal("test error")
432}
433
434/// Helper to construct a `GetClientError` for testing.
435#[cfg(test)]
436fn test_client_error() -> GetClientError {
437    GetClientError::InvalidScheme
438}
439
440/// Macro that generates a test module for a standard 2-variant error enum
441/// (GetClientError + tonic::Status).
442#[cfg(test)]
443macro_rules! two_variant_error_tests {
444    ($mod_name:ident, $error_type:ident, $status_variant:ident) => {
445        mod $mod_name {
446            use super::*;
447
448            #[test]
449            fn from_get_client_error() {
450                let inner = test_client_error();
451                let e = $error_type::from(inner);
452                assert!(matches!(e, $error_type::GetClientError(_)));
453                assert!(e.to_string().contains("invalid scheme"));
454            }
455
456            #[test]
457            fn from_status() {
458                let status = test_status();
459                let e = $error_type::from(status);
460                assert!(matches!(e, $error_type::$status_variant(_)));
461                assert!(e.to_string().contains("test error"));
462            }
463        }
464    };
465}
466
467#[cfg(test)]
468mod tests {
469    use super::*;
470
471    two_variant_error_tests!(get_info, GetInfoError, GetLightdInfoError);
472    two_variant_error_tests!(get_latest_block, GetLatestBlockError, GetLatestBlockError);
473    two_variant_error_tests!(get_tree_state, GetTreeStateError, GetTreeStateError);
474    two_variant_error_tests!(get_block, GetBlockError, GetBlockError);
475    two_variant_error_tests!(
476        get_block_nullifiers,
477        GetBlockNullifiersError,
478        GetBlockNullifiersError
479    );
480    two_variant_error_tests!(get_block_range, GetBlockRangeError, GetBlockRangeError);
481    two_variant_error_tests!(
482        get_block_range_nullifiers,
483        GetBlockRangeNullifiersError,
484        GetBlockRangeNullifiersError
485    );
486    two_variant_error_tests!(get_transaction, GetTransactionError, GetTransactionError);
487    two_variant_error_tests!(get_mempool_tx, GetMempoolTxError, GetMempoolTxError);
488    two_variant_error_tests!(
489        get_mempool_stream,
490        GetMempoolStreamError,
491        GetMempoolStreamError
492    );
493    two_variant_error_tests!(
494        get_latest_tree_state,
495        GetLatestTreeStateError,
496        GetLatestTreeStateError
497    );
498    two_variant_error_tests!(
499        get_subtree_roots,
500        GetSubtreeRootsError,
501        GetSubtreeRootsError
502    );
503
504    mod send_transaction {
505        use super::*;
506
507        #[test]
508        fn from_get_client_error() {
509            let inner = test_client_error();
510            let e = SendTransactionError::from(inner);
511            assert!(matches!(e, SendTransactionError::GetClientError(_)));
512        }
513
514        #[test]
515        fn from_status() {
516            let status = test_status();
517            let e = SendTransactionError::from(status);
518            assert!(matches!(e, SendTransactionError::SendTransactionError(_)));
519        }
520
521        #[test]
522        fn send_rejected() {
523            let e = SendTransactionError::SendRejected("bad tx".into());
524            assert!(matches!(e, SendTransactionError::SendRejected(_)));
525            assert_eq!(e.to_string(), "send rejected: bad tx");
526        }
527    }
528
529    #[cfg(feature = "ping-very-insecure")]
530    two_variant_error_tests!(ping, PingError, PingError);
531}
532
533// ── TransparentIndexer errors ───────────────────────────────────────
534
535#[cfg(feature = "globally-public-transparent")]
536pub mod transparent {
537    use super::GetClientError;
538
539    /// Error from the deprecated `get_taddress_txids` (`GetTaddressTxids`) RPC.
540    ///
541    /// Callers can depend on:
542    /// - `GetClientError` means the connection was never established.
543    /// - `GetTaddressTxidsError` means the server returned a gRPC status.
544    ///
545    /// ```
546    /// # #[cfg(feature = "globally-public-transparent")]
547    /// # {
548    /// use zingo_netutils::error::transparent::GetTaddressTxidsError;
549    /// use zingo_netutils::GetClientError;
550    ///
551    /// let e = GetTaddressTxidsError::from(GetClientError::InvalidScheme);
552    /// assert!(matches!(e, GetTaddressTxidsError::GetClientError(_)));
553    ///
554    /// let e = GetTaddressTxidsError::from(tonic::Status::internal("oops"));
555    /// assert!(matches!(e, GetTaddressTxidsError::GetTaddressTxidsError(_)));
556    /// # }
557    /// ```
558    #[derive(Debug, thiserror::Error)]
559    pub enum GetTaddressTxidsError {
560        #[error(transparent)]
561        GetClientError(#[from] GetClientError),
562
563        #[error("gRPC error: {0}")]
564        GetTaddressTxidsError(#[from] tonic::Status),
565    }
566
567    /// Error from the `get_taddress_transactions` (`GetTaddressTransactions`) RPC.
568    ///
569    /// Callers can depend on:
570    /// - `GetClientError` means the connection was never established.
571    /// - `GetTaddressTransactionsError` means the server returned a gRPC status.
572    ///
573    /// ```
574    /// # #[cfg(feature = "globally-public-transparent")]
575    /// # {
576    /// use zingo_netutils::error::transparent::GetTaddressTransactionsError;
577    /// use zingo_netutils::GetClientError;
578    ///
579    /// let e = GetTaddressTransactionsError::from(GetClientError::InvalidScheme);
580    /// assert!(matches!(e, GetTaddressTransactionsError::GetClientError(_)));
581    ///
582    /// let e = GetTaddressTransactionsError::from(tonic::Status::internal("oops"));
583    /// assert!(matches!(e, GetTaddressTransactionsError::GetTaddressTransactionsError(_)));
584    /// # }
585    /// ```
586    #[derive(Debug, thiserror::Error)]
587    pub enum GetTaddressTransactionsError {
588        #[error(transparent)]
589        GetClientError(#[from] GetClientError),
590
591        #[error("gRPC error: {0}")]
592        GetTaddressTransactionsError(#[from] tonic::Status),
593    }
594
595    /// Error from the `get_taddress_balance` (`GetTaddressBalance`) RPC.
596    ///
597    /// Callers can depend on:
598    /// - `GetClientError` means the connection was never established.
599    /// - `GetTaddressBalanceError` means the server returned a gRPC status.
600    ///
601    /// ```
602    /// # #[cfg(feature = "globally-public-transparent")]
603    /// # {
604    /// use zingo_netutils::error::transparent::GetTaddressBalanceError;
605    /// use zingo_netutils::GetClientError;
606    ///
607    /// let e = GetTaddressBalanceError::from(GetClientError::InvalidScheme);
608    /// assert!(matches!(e, GetTaddressBalanceError::GetClientError(_)));
609    ///
610    /// let e = GetTaddressBalanceError::from(tonic::Status::internal("oops"));
611    /// assert!(matches!(e, GetTaddressBalanceError::GetTaddressBalanceError(_)));
612    /// # }
613    /// ```
614    #[derive(Debug, thiserror::Error)]
615    pub enum GetTaddressBalanceError {
616        #[error(transparent)]
617        GetClientError(#[from] GetClientError),
618
619        #[error("gRPC error: {0}")]
620        GetTaddressBalanceError(#[from] tonic::Status),
621    }
622
623    /// Error from the `get_taddress_balance_stream` (`GetTaddressBalanceStream`) RPC.
624    ///
625    /// Callers can depend on:
626    /// - `GetClientError` means the connection was never established.
627    /// - `GetTaddressBalanceStreamError` means the server returned a gRPC status.
628    ///
629    /// ```
630    /// # #[cfg(feature = "globally-public-transparent")]
631    /// # {
632    /// use zingo_netutils::error::transparent::GetTaddressBalanceStreamError;
633    /// use zingo_netutils::GetClientError;
634    ///
635    /// let e = GetTaddressBalanceStreamError::from(GetClientError::InvalidScheme);
636    /// assert!(matches!(e, GetTaddressBalanceStreamError::GetClientError(_)));
637    ///
638    /// let e = GetTaddressBalanceStreamError::from(tonic::Status::internal("oops"));
639    /// assert!(matches!(e, GetTaddressBalanceStreamError::GetTaddressBalanceStreamError(_)));
640    /// # }
641    /// ```
642    #[derive(Debug, thiserror::Error)]
643    pub enum GetTaddressBalanceStreamError {
644        #[error(transparent)]
645        GetClientError(#[from] GetClientError),
646
647        #[error("gRPC error: {0}")]
648        GetTaddressBalanceStreamError(#[from] tonic::Status),
649    }
650
651    /// Error from the `get_address_utxos` (`GetAddressUtxos`) RPC.
652    ///
653    /// Callers can depend on:
654    /// - `GetClientError` means the connection was never established.
655    /// - `GetAddressUtxosError` means the server returned a gRPC status.
656    ///
657    /// ```
658    /// # #[cfg(feature = "globally-public-transparent")]
659    /// # {
660    /// use zingo_netutils::error::transparent::GetAddressUtxosError;
661    /// use zingo_netutils::GetClientError;
662    ///
663    /// let e = GetAddressUtxosError::from(GetClientError::InvalidScheme);
664    /// assert!(matches!(e, GetAddressUtxosError::GetClientError(_)));
665    ///
666    /// let e = GetAddressUtxosError::from(tonic::Status::internal("oops"));
667    /// assert!(matches!(e, GetAddressUtxosError::GetAddressUtxosError(_)));
668    /// # }
669    /// ```
670    #[derive(Debug, thiserror::Error)]
671    pub enum GetAddressUtxosError {
672        #[error(transparent)]
673        GetClientError(#[from] GetClientError),
674
675        #[error("gRPC error: {0}")]
676        GetAddressUtxosError(#[from] tonic::Status),
677    }
678
679    /// Error from the `get_address_utxos_stream` (`GetAddressUtxosStream`) RPC.
680    ///
681    /// Callers can depend on:
682    /// - `GetClientError` means the connection was never established.
683    /// - `GetAddressUtxosStreamError` means the server returned a gRPC status
684    ///   before or instead of streaming UTXOs.
685    ///
686    /// ```
687    /// # #[cfg(feature = "globally-public-transparent")]
688    /// # {
689    /// use zingo_netutils::error::transparent::GetAddressUtxosStreamError;
690    /// use zingo_netutils::GetClientError;
691    ///
692    /// let e = GetAddressUtxosStreamError::from(GetClientError::InvalidScheme);
693    /// assert!(matches!(e, GetAddressUtxosStreamError::GetClientError(_)));
694    ///
695    /// let e = GetAddressUtxosStreamError::from(tonic::Status::internal("oops"));
696    /// assert!(matches!(e, GetAddressUtxosStreamError::GetAddressUtxosStreamError(_)));
697    /// # }
698    /// ```
699    #[derive(Debug, thiserror::Error)]
700    pub enum GetAddressUtxosStreamError {
701        #[error(transparent)]
702        GetClientError(#[from] GetClientError),
703
704        #[error("gRPC error: {0}")]
705        GetAddressUtxosStreamError(#[from] tonic::Status),
706    }
707
708    #[cfg(test)]
709    mod tests {
710        use super::*;
711
712        fn test_status() -> tonic::Status {
713            tonic::Status::internal("test error")
714        }
715
716        fn test_client_error() -> GetClientError {
717            GetClientError::InvalidScheme
718        }
719
720        macro_rules! two_variant_error_tests {
721            ($mod_name:ident, $error_type:ident, $status_variant:ident) => {
722                mod $mod_name {
723                    use super::*;
724
725                    #[test]
726                    fn from_get_client_error() {
727                        let inner = test_client_error();
728                        let e = $error_type::from(inner);
729                        assert!(matches!(e, $error_type::GetClientError(_)));
730                        assert!(e.to_string().contains("invalid scheme"));
731                    }
732
733                    #[test]
734                    fn from_status() {
735                        let status = test_status();
736                        let e = $error_type::from(status);
737                        assert!(matches!(e, $error_type::$status_variant(_)));
738                        assert!(e.to_string().contains("test error"));
739                    }
740                }
741            };
742        }
743
744        two_variant_error_tests!(
745            get_taddress_txids,
746            GetTaddressTxidsError,
747            GetTaddressTxidsError
748        );
749        two_variant_error_tests!(
750            get_taddress_transactions,
751            GetTaddressTransactionsError,
752            GetTaddressTransactionsError
753        );
754        two_variant_error_tests!(
755            get_taddress_balance,
756            GetTaddressBalanceError,
757            GetTaddressBalanceError
758        );
759        two_variant_error_tests!(
760            get_taddress_balance_stream,
761            GetTaddressBalanceStreamError,
762            GetTaddressBalanceStreamError
763        );
764        two_variant_error_tests!(
765            get_address_utxos,
766            GetAddressUtxosError,
767            GetAddressUtxosError
768        );
769        two_variant_error_tests!(
770            get_address_utxos_stream,
771            GetAddressUtxosStreamError,
772            GetAddressUtxosStreamError
773        );
774    }
775}