1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use anyhow::format_err;
use futures::channel::{mpsc, oneshot};
use thiserror::Error;

/// Error types that may be used by the async hanging-get server.
#[derive(Error, Debug)]
pub enum HangingGetServerError {
    /// An existing observer was already present for the client.
    #[error("Cannot have multiple concurrent observers for a single client")]
    MultipleObservers,

    /// The HangingGetBroker associated with this handle has been dropped.
    #[error("The HangingGetBroker associated with this handle has been dropped.")]
    NoBroker,

    /// This handle is sending messages faster than the broker can process them.
    #[error("This handle is sending messages faster than the broker can process them.")]
    RateLimit,

    /// Generic hanging-get server error.
    #[error("Error: {}", .0)]
    Generic(anyhow::Error),
}

impl PartialEq for HangingGetServerError {
    fn eq(&self, other: &Self) -> bool {
        use HangingGetServerError::*;
        match (self, other) {
            (MultipleObservers, MultipleObservers)
            | (NoBroker, NoBroker)
            | (RateLimit, RateLimit) => true,
            _ => false,
        }
    }
}

impl From<mpsc::SendError> for HangingGetServerError {
    fn from(error: mpsc::SendError) -> Self {
        if error.is_disconnected() {
            HangingGetServerError::NoBroker
        } else if error.is_full() {
            HangingGetServerError::RateLimit
        } else {
            HangingGetServerError::Generic(format_err!(
                "Unknown SendError error condition: {}",
                error
            ))
        }
    }
}

impl From<oneshot::Canceled> for HangingGetServerError {
    fn from(e: oneshot::Canceled) -> Self {
        HangingGetServerError::Generic(e.into())
    }
}

impl From<anyhow::Error> for HangingGetServerError {
    /// Try downcasting to more specific error types, falling back to `Generic` if that fails.
    fn from(e: anyhow::Error) -> Self {
        e.downcast::<mpsc::SendError>().map_or_else(HangingGetServerError::Generic, Self::from)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn error_partial_eq_impl() {
        use HangingGetServerError::*;
        let variants = [MultipleObservers, NoBroker, RateLimit, Generic(format_err!("err"))];
        for (i, a) in variants.iter().enumerate() {
            for (j, b) in variants.iter().enumerate() {
                // variants with the same index are equal except `Generic` errors are _never_ equal.
                if i == j && i != 3 && j != 3 {
                    assert_eq!(a, b);
                } else {
                    assert_ne!(a, b);
                }
            }
        }
    }
}