pub mod certs;
use crate::VirusTotalError;
use std::collections::HashMap;
use std::fmt::Display;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ReportResponseHeader<A> {
#[serde(default)]
pub links: HashMap<String, String>,
#[serde(rename = "type")]
pub record_type: RecordType,
pub id: String,
pub attributes: A,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum RecordType {
#[serde(alias = "domain", alias = "domains")]
Domain,
#[serde(alias = "ip_address")]
IPAddress,
#[serde(alias = "files", alias = "file")]
File,
}
impl Display for RecordType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RecordType::Domain => write!(f, "domains"),
RecordType::IPAddress => write!(f, "ip_addresses"),
RecordType::File => write!(f, "files"),
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ReportRequestResponse<R> {
#[serde(rename = "data")]
Data(R),
#[serde(rename = "error")]
Error(VirusTotalError),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RescanRequestData {
#[serde(rename = "type")]
pub rescan_type: String,
pub id: String,
pub links: HashMap<String, String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AnalysisResult {
pub category: String,
pub engine_name: String,
pub engine_version: Option<String>,
pub result: Option<String>,
pub method: String,
pub engine_update: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct LastAnalysisStats {
pub harmless: u32,
#[serde(rename = "type-unsupported", default)]
pub type_unsupported: Option<u32>,
pub suspicious: u32,
#[serde(rename = "confirmed-timeout", default)]
pub confirmed_timeout: Option<u32>,
pub timeout: u32,
#[serde(default)]
pub failure: Option<u32>,
pub malicious: u32,
pub undetected: u32,
}
impl LastAnalysisStats {
#[must_use]
pub fn av_count(&self) -> u32 {
self.harmless + self.suspicious + self.malicious + self.undetected
}
#[must_use]
pub fn safe_count(&self) -> u32 {
self.harmless + self.undetected
}
#[must_use]
pub fn error_count(&self) -> u32 {
self.type_unsupported.unwrap_or_default()
+ self.confirmed_timeout.unwrap_or_default()
+ self.timeout
+ self.failure.unwrap_or_default()
}
#[must_use]
pub fn is_benign(&self) -> bool {
self.malicious == 0 && self.suspicious == 0
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Votes {
pub harmless: u32,
pub malicious: u32,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize_valid_response() {
const RESPONSE: &str = include_str!("../../testdata/rescan.json");
let rescan: ReportRequestResponse<RescanRequestData> =
serde_json::from_str(RESPONSE).expect("failed to deserialize VT rescan");
if let ReportRequestResponse::Data(data) = rescan {
assert_eq!(data.rescan_type, "analysis");
} else {
panic!("Rescan report shouldn't be an error type");
}
}
}