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
11
    pub(crate) fn error(&self) -> RequestParseError {
53
        use Possibly::*;
54
        use RequestParseError as E;
55

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

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

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

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

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

            
83
1
        E::ParamType
84
11
    }
85
}