1
//! Wrapper types for subsets of ChanMsg and RelayMsg types.
2
//!
3
//! These wrappers define types that are valid in response to particular
4
//! request, or when received in particular circumstances.  They're used
5
//! so that Rust's typesafety can help enforce protocol properties.
6

            
7
use derive_deftly::{Deftly, define_derive_deftly};
8
use std::fmt::{self, Display};
9
use tor_cell::chancell::msg::{self as chanmsg};
10

            
11
define_derive_deftly! {
12
    /// Derives a `TryFrom<AnyChanMsg>` implementation for enums
13
    /// that represent restricted subsets of ChanMsgs
14
    ///
15
    /// # Limitations
16
    ///
17
    /// The variants of the enum this is derived for *must* be a
18
    /// subset of the variants of [`AnyChanMsg`].
19
    RestrictedChanMsgSet:
20

            
21
    impl TryFrom<tor_cell::chancell::msg::AnyChanMsg> for $ttype {
22
        type Error = $crate::Error;
23

            
24
562
        fn try_from(m: tor_cell::chancell::msg::AnyChanMsg) -> $crate::Result<$ttype> {
25
            match m {
26
                $( tor_cell::chancell::msg::AnyChanMsg::$vname(m) => Ok($ttype::$vname(m)), )
27
                _ => Err($crate::Error::ChanProto(format!(
28
                    "Got a {} {}",
29
                    <tor_cell::chancell::msg::AnyChanMsg as tor_cell::chancell::ChanMsg>::cmd(&m),
30
                    ${tmeta(usage) as str},
31
                ))),
32
            }
33
        }
34
    }
35
}
36

            
37
pub(crate) use derive_deftly_template_RestrictedChanMsgSet;
38

            
39
/// A subclass of ChanMsg that can arrive in response to a CREATE* cell
40
/// that we send.
41
#[cfg_attr(docsrs, doc(cfg(feature = "testing")))]
42
#[derive(Debug, Deftly)]
43
#[allow(unreachable_pub)] // Only `pub` with feature `testing`; otherwise, visible in crate
44
#[allow(clippy::exhaustive_enums)]
45
#[derive_deftly(RestrictedChanMsgSet)]
46
#[deftly(usage = "in response to circuit creation")]
47
pub enum CreateResponse {
48
    /// Destroy cell: the CREATE failed.
49
    Destroy(chanmsg::Destroy),
50
    /// CreatedFast: good response to a CREATE cell.
51
    CreatedFast(chanmsg::CreatedFast),
52
    /// Created2: good response to a CREATE2 cell.
53
    Created2(chanmsg::Created2),
54
}
55

            
56
impl Display for CreateResponse {
57
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58
        use CreateResponse as CR;
59
        match self {
60
            CR::Destroy(destroy) => write!(f, "DESTROY({})", destroy.reason()),
61
            CR::CreatedFast(_) => Display::fmt("CREATED_FAST", f),
62
            CR::Created2(_) => Display::fmt("CREATED2", f),
63
        }
64
    }
65
}
66

            
67
#[cfg(test)]
68
mod test {
69
    // @@ begin test lint list maintained by maint/add_warning @@
70
    #![allow(clippy::bool_assert_comparison)]
71
    #![allow(clippy::clone_on_copy)]
72
    #![allow(clippy::dbg_macro)]
73
    #![allow(clippy::mixed_attributes_style)]
74
    #![allow(clippy::print_stderr)]
75
    #![allow(clippy::print_stdout)]
76
    #![allow(clippy::single_char_pattern)]
77
    #![allow(clippy::unwrap_used)]
78
    #![allow(clippy::unchecked_time_subtraction)]
79
    #![allow(clippy::useless_vec)]
80
    #![allow(clippy::needless_pass_by_value)]
81
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
82

            
83
    use super::*;
84

            
85
    #[test]
86
    fn create_response() {
87
        use tor_cell::chancell::msg::{self, AnyChanMsg};
88
        fn good(m: AnyChanMsg) {
89
            assert!(CreateResponse::try_from(m).is_ok());
90
        }
91
        fn bad(m: AnyChanMsg) {
92
            assert!(CreateResponse::try_from(m).is_err());
93
        }
94

            
95
        good(msg::Destroy::new(2.into()).into());
96
        good(msg::CreatedFast::new(&b"this offer is unrepeatable"[..]).into());
97
        good(msg::Created2::new(&b"guaranteed guaranteed"[..]).into());
98
        bad(msg::CreateFast::new(&b"for a lifetime or more"[..]).into());
99
        bad(msg::Versions::new([1, 2, 3]).unwrap().into());
100
    }
101
}