1
//! Variations on our message types used to give better diagnostics for
2
//! unparsable requests.
3

            
4
use tor_rpcbase as rpc;
5

            
6
use super::{ReqMeta, RequestId};
7
use crate::err::RequestParseError;
8

            
9
/// An invalid approximation of a request.
10
///
11
/// If we can't deserialize a [`Request`](super::Request) properly,
12
/// we try to deserialize it into
13
/// _this_ structure so we can explain what was wrong with it.
14
#[derive(Debug, serde::Deserialize)]
15
pub(crate) struct InvalidRequest {
16
    /// Possibly, an ID field.
17
    ///
18
    /// If we can't parse this, then there's no hope of giving back a cogent
19
    /// response.
20
    id: Option<Possibly<RequestId>>,
21
    /// The object that was passed in, if any.
22
    obj: Option<Possibly<rpc::ObjectId>>,
23
    /// The metadata that was passed in, if any.
24
    meta: Option<Possibly<ReqMeta>>,
25
    /// The method that was passed in, if any.
26
    method: Option<Possibly<String>>,
27
    /// The params that were passed in, if any.
28
    params: Option<serde_json::Value>,
29
}
30

            
31
/// Either a "good" value that we could deserialize as a `T`, or some "Bad" value that we couldn't.
32
#[derive(Debug, serde::Deserialize)]
33
#[serde(untagged)]
34
enum Possibly<T> {
35
    /// The value was deserialized as expected.
36
    Good(T),
37
    /// The value could not be deserialized as expected.
38
    #[allow(dead_code)] // deserialize the Value; we may use it for error reporting some day
39
    Bad(serde_json::Value),
40
}
41

            
42
impl InvalidRequest {
43
    /// Return the ID for this request, if it has one.
44
    pub(crate) fn id(&self) -> Option<&RequestId> {
45
        match &self.id {
46
            Some(Possibly::Good(id)) => Some(id),
47
            _ => None,
48
        }
49
    }
50

            
51
    /// Return an error explaining why this wasn't a valid request.
52
22
    pub(crate) fn error(&self) -> RequestParseError {
53
        use Possibly::*;
54
        use RequestParseError as E;
55

            
56
20
        match self.id {
57
2
            None => return E::IdMissing,
58
2
            Some(Bad(_)) => return E::IdType,
59
18
            _ => {}
60
        }
61

            
62
16
        match self.obj {
63
2
            None => return E::ObjMissing,
64
2
            Some(Bad(_)) => return E::ObjType,
65
14
            _ => {}
66
        }
67

            
68
10
        match &self.method {
69
2
            None => return E::MethodMissing,
70
2
            Some(Bad(_)) => return E::MethodType,
71
10
            Some(Good(name)) if !rpc::is_method_name(name) => return E::NoSuchMethod,
72
8
            _ => {}
73
        }
74

            
75
4
        if matches!(self.meta, Some(Bad(_))) {
76
4
            return E::MetaType;
77
4
        }
78
4

            
79
4
        if self.params.is_none() {
80
2
            return E::MissingParams;
81
2
        }
82
2

            
83
2
        E::ParamType
84
22
    }
85
}