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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use std::fmt::Display;


/// A value that contains one of the ways to authenticate to Assemblyline
pub enum Authentication {
    /// Authenticate with a password
    Password{
        /// The name of the user account connecting
        username: String,
        /// The password of the user connecting
        password: String
    },
    /// Authenticate with an api key
    ApiKey{
        /// The name of the user account connecting
        username: String,
        /// The API key of the user connecting
        key: String
    },
    /// Authenticate with an oauth token
    OAuth{
        /// Oauth provider
        provider: String,
        /// Oauth token
        token: String
    }
}


/// Short name for serde json's basic map type
pub type JsonMap = serde_json::Map<String, serde_json::Value>;

/// Set of possible errors returned by client
#[derive(Debug)]
pub enum Error {
    /// An error produced by the client's communication with the server
    Client{
        /// A message describing the error
        message: String,
        /// HTTP status code associated
        status: u32,
        /// Server's API version if available
        api_version: Option<String>,
        /// Server's response details if available
        api_response: Option<String>
    },
    /// An error that occured during a failed communication with the server
    TransportError(String),
    /// An invalid HTTP header name or value was provided
    InvalidHeader,
    /// The server's response was truncated, corrupted, or malformed
    MalformedResponse,
    /// A string could not be converted into a sha256
    InvalidSha256,
    /// A path was provided for submission that couldn't be used
    InvalidSubmitFilePath,
    /// A url was provided for submission and a file name couldn't be parsed
    InvalidSubmitUrl,
    /// An error that has bubbled up from an IO call
    IO(std::io::Error),
    /// An error caused by failing to serialize or deserialize a message
    Serialization(serde_json::Error),
    /// An unexpected state was reached serializing submission parameters
    ParameterSerialization,
    /// A configuration value which has caused errors
    Configuration(String),
}

impl Error {
    pub (crate) fn client_error(message: String, status: u32) -> Self {
        return Error::Client { message, status, api_response: None, api_version: None }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::Client { message, status, .. } =>
                f.write_fmt(format_args!("Client error [{status}]: {message}")),
            Error::TransportError(message) =>
                f.write_fmt(format_args!("Error communicating with server: {message}")),
            Error::InvalidHeader =>
                f.write_str("An invalid HTTP header name or value was encountered"),
            Error::MalformedResponse =>
                f.write_str("A server response was malformed"),
            Error::InvalidSha256 =>
                f.write_str("An invalid SHA256 string was provided"),
            Error::InvalidSubmitFilePath =>
                f.write_str("An invalid path was given for submission"),
            Error::InvalidSubmitUrl =>
                f.write_str("An invalid URL was given for submission, try setting the file name explicitly"),
            Error::IO(error) =>
                f.write_fmt(format_args!("An IO error ocurred: {error}")),
            Error::Serialization(error) =>
                f.write_fmt(format_args!("An error occurred serializing a body: {error}")),
            Error::ParameterSerialization =>
                f.write_str("Parameter serialization yielded unexpected type."),
            Error::Configuration(message) =>
                f.write_fmt(format_args!("A configuration parameter caused an error: {message}")),
        }
    }
}

impl From<reqwest::Error> for Error {
    fn from(value: reqwest::Error) -> Self {
        if let Some(code) = value.status() {
            Error::client_error(value.to_string(), code.as_u16() as u32)
        } else {
            Error::TransportError(value.to_string())
        }
    }
}

impl From<reqwest::header::InvalidHeaderName> for Error {
    fn from(_value: reqwest::header::InvalidHeaderName) -> Self {
        Self::InvalidHeader
    }
}

impl From<reqwest::header::InvalidHeaderValue> for Error {
    fn from(_value: reqwest::header::InvalidHeaderValue) -> Self {
        Self::InvalidHeader
    }
}

impl From<serde_json::Error> for Error {
    fn from(value: serde_json::Error) -> Self {
        Self::Serialization(value)
    }
}

impl From<std::io::Error> for Error {
    fn from(value: std::io::Error) -> Self {
        Self::IO(value)
    }
}

impl From<url::ParseError> for Error {
    fn from(_value: url::ParseError) -> Self {
        Self::InvalidSubmitUrl
    }
}

impl std::error::Error for Error {

}

pub type Result<T> = std::result::Result<T, Error>;

/// A convenience trait that lets you pass true, false, or None for boolean arguments
pub trait IBool: Into<Option<bool>> + Copy {}
impl<T: Into<Option<bool>> + Copy> IBool for T {}