use either::Either;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use validator::Validate;
use crate::database::browser_profile::{
BrowserProfilePreliminary, Homepage, MainWebsite, Platform, ProxyType,
};
use super::super::{bookmark_dto::BookmarkFullData, proxy_dto::ProxyFullData};
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub enum Os {
#[serde(rename = "darwin")]
MacOS,
#[serde(rename = "win32")]
Windows,
#[serde(rename = "linux")]
Linux,
}
impl From<Platform> for Os {
fn from(platform: Platform) -> Self {
match platform {
Platform::Macos => Os::MacOS,
Platform::Windows => Os::Windows,
Platform::Linux => Os::Linux,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ConnectionInfo {
pub country: String,
pub region: String,
pub city: String,
pub ip: String,
pub timezone: String,
pub zip: String,
pub geo: Vec<f64>,
pub override_proxy_url: Option<String>,
}
impl ConnectionInfo {
pub fn get_mock() -> Self {
Self {
geo: vec![40.7128, -74.0060],
country: "US".to_string(),
region: "CA".to_string(),
city: "San Francisco".to_string(),
ip: "127.0.0.1".to_string(),
timezone: "PST".to_string(),
zip: "94105".to_string(),
override_proxy_url: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LoggingLevel {
Fatal,
Error,
Warning,
Info,
Debug,
Trace,
Trace2,
}
impl Serialize for LoggingLevel {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
LoggingLevel::Fatal => serializer.serialize_str("FATAL"),
LoggingLevel::Error => serializer.serialize_str("ERROR"),
LoggingLevel::Warning => serializer.serialize_str("WARNING"),
LoggingLevel::Info => serializer.serialize_str("INFO"),
LoggingLevel::Debug => serializer.serialize_str("DEBUG"),
LoggingLevel::Trace => serializer.serialize_str("TRACE"),
LoggingLevel::Trace2 => serializer.serialize_str("TRACE2"),
}
}
}
impl<'de> Deserialize<'de> for LoggingLevel {
fn deserialize<D: Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
use serde::de::Error;
let s = String::deserialize(deserializer)?.to_uppercase();
match s.as_str() {
"FATAL" => Ok(LoggingLevel::Fatal),
"ERROR" => Ok(LoggingLevel::Error),
"WARNING" => Ok(LoggingLevel::Warning),
"INFO" => Ok(LoggingLevel::Info),
"DEBUG" => Ok(LoggingLevel::Debug),
"TRACE" => Ok(LoggingLevel::Trace),
"TRACE2" => Ok(LoggingLevel::Trace2),
_ => Err(D::Error::custom(format!("Invalid logging level: {}", s))),
}
}
}
#[derive(Debug, Serialize, Deserialize, Validate, Clone)]
pub struct UserPaths {
pub app_data_dir: String,
pub user_data_dir: String,
pub user_chromium_components_dir: String,
}
impl UserPaths {
pub fn get_mock() -> Self {
Self {
app_data_dir: "/Users/user/Library/Application Support/dolphin_anty"
.to_string(),
user_data_dir:
"/Users/user/Library/Application Support/dolphin_anty/123456/data_dir"
.to_string(),
user_chromium_components_dir:
"/Users/user/Library/Application Support/dolphin_anty/chromium_components".to_string(),
}
}
}
#[derive(Debug, Serialize, Deserialize, Validate, Clone)]
pub struct Versions {
pub browser: u16,
pub local_api: String,
pub electron: String,
pub anty_extension: u16,
pub dolphin_extension: u16,
}
impl Versions {
pub fn get_mock() -> Self {
Self {
browser: 256,
local_api: "2022.1.1".to_string(),
electron: "2024.351.1".to_string(),
anty_extension: 5,
dolphin_extension: 7,
}
}
}
#[derive(Debug, Serialize, Deserialize, Validate, Clone)]
pub struct StartRequest {
pub browser_profile_id: usize,
pub os: Os,
pub versions: Versions,
pub automation: bool,
pub headless: bool,
pub imageless: bool,
pub use_action_synchronizator: bool,
pub action_synchronizator_session_name: Option<String>,
pub action_synchronizator_is_owned: Option<bool>,
pub scenum_custom_mode: bool,
pub for_scenario: bool,
pub use_mock_profile: bool,
pub user_screen_width: u16,
pub user_screen_height: u16,
pub connection_info: ConnectionInfo,
pub dolphin_integration_token: Option<String>,
pub remote_api_base_url: String,
pub paths: UserPaths,
pub logging_level: LoggingLevel,
pub browser_down_port: u16,
pub present_datadir_hash: Option<String>,
}
impl StartRequest {
pub fn get_mock() -> Self {
Self {
browser_profile_id: 0,
os: Os::Linux,
versions: Versions::get_mock(),
automation: false,
headless: false,
imageless: false,
use_action_synchronizator: false,
action_synchronizator_session_name: None,
action_synchronizator_is_owned: None,
scenum_custom_mode: false,
for_scenario: false,
use_mock_profile: false,
user_screen_width: 0,
user_screen_height: 0,
connection_info: ConnectionInfo::get_mock(),
dolphin_integration_token: None,
remote_api_base_url: "https://api.example.com".to_string(),
paths: UserPaths::get_mock(),
logging_level: LoggingLevel::Info,
browser_down_port: 255,
present_datadir_hash: None,
}
}
}
#[derive(Debug, Serialize)]
pub struct StartResponse {
pub hash: Option<String>,
pub datadir_url: String,
pub patch_url: Option<String>,
pub config: String,
pub command: Either<String, Vec<String>>,
}
#[derive(Debug, Serialize)]
pub struct DatadirResponse {
pub hash: Option<String>,
pub url: String,
}
#[derive(Debug, Serialize)]
pub struct Bookmark {
pub url: String,
pub name: String,
}
impl From<BookmarkFullData> for Bookmark {
fn from(bookmark: BookmarkFullData) -> Self {
Self {
url: bookmark.url,
name: bookmark.name,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub enum ExtensionType {
ChromeWebStore,
File,
}
impl From<String> for ExtensionType {
fn from(s: String) -> Self {
match s.as_str() {
"chromeWebStore" => ExtensionType::ChromeWebStore,
"file" => ExtensionType::File,
_ => panic!("Invalid extension type: {}", s),
}
}
}
#[derive(Debug, Serialize)]
pub struct Extension {
pub name: Option<String>,
pub extension_id: String,
pub url: String,
#[serde(rename = "type")]
pub r#type: ExtensionType,
pub hash: Option<String>,
}
impl From<crate::database::browser_profile::Extension> for Extension {
fn from(extension: crate::database::browser_profile::Extension) -> Self {
Self {
extension_id: extension.extension_id,
url: extension.url,
r#type: extension.r#type.into(),
hash: extension.hash,
name: extension.name,
}
}
}
#[derive(Debug, Serialize)]
pub struct PreliminaryResponse {
pub proxy: Option<ProxyData>,
pub name: String,
pub homepages: Vec<String>,
pub main_website: MainWebsite,
pub bookmarks: Vec<Bookmark>,
pub login: Option<String>,
pub password: Option<String>,
pub created_at: String,
pub extensions: Vec<Extension>,
}
pub struct PreliminaryData {
pub browser_profile: BrowserProfilePreliminary,
pub proxy: Option<ProxyFullData>,
pub homepages: Vec<Homepage>,
pub bookmarks: Vec<BookmarkFullData>,
pub extensions: Vec<Extension>,
}
impl From<PreliminaryData> for PreliminaryResponse {
fn from(data: PreliminaryData) -> Self {
Self {
proxy: data.proxy.map(ProxyData::from),
name: data.browser_profile.name,
homepages: data.homepages.iter().map(|h| h.url.clone()).collect(),
main_website: data.browser_profile.main_website.into(),
bookmarks: data.bookmarks.iter().map(|b| b.clone().into()).collect(),
login: data.browser_profile.login,
password: data.browser_profile.password,
created_at: data.browser_profile.created_at.to_string(),
extensions: data.extensions,
}
}
}
impl PreliminaryData {
pub fn get_mock() -> Self {
Self {
proxy: None,
homepages: vec![],
bookmarks: vec![],
extensions: vec![],
browser_profile: BrowserProfilePreliminary::get_mock(),
}
}
}
#[derive(Debug, Serialize)]
pub struct ProxyData {
pub id: u64,
pub r#type: ProxyType,
pub host: String,
pub port: String,
pub login: Option<String>,
pub password: Option<String>,
pub change_ip_url: Option<String>,
}
impl From<ProxyFullData> for ProxyData {
fn from(proxy: ProxyFullData) -> Self {
Self {
id: proxy.id,
r#type: proxy.proxy_type,
host: proxy.host,
port: proxy.port,
login: proxy.login,
password: proxy.password,
change_ip_url: proxy.change_ip_url,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_os_serialization() {
let os = Os::MacOS;
let serialized =
serde_json::to_string(&os).expect("Failed to serialize OS");
assert_eq!(serialized, "\"darwin\"");
let os = Os::Windows;
let serialized =
serde_json::to_string(&os).expect("Failed to serialize OS");
assert_eq!(serialized, "\"win32\"");
let os = Os::Linux;
let serialized =
serde_json::to_string(&os).expect("Failed to serialize OS");
assert_eq!(serialized, "\"linux\"");
}
#[test]
fn test_os_deserialization() {
let os: Os =
serde_json::from_str("\"darwin\"").expect("Failed to deserialize OS");
assert_eq!(os, Os::MacOS);
let os: Os =
serde_json::from_str("\"win32\"").expect("Failed to deserialize OS");
assert_eq!(os, Os::Windows);
let os: Os =
serde_json::from_str("\"linux\"").expect("Failed to deserialize OS");
assert_eq!(os, Os::Linux);
}
#[test]
fn test_logging_level_serialization() {
let level = LoggingLevel::Info;
let serialized =
serde_json::to_string(&level).expect("Failed to serialize logging level");
assert_eq!(serialized, "\"INFO\"");
let level = LoggingLevel::Error;
let serialized =
serde_json::to_string(&level).expect("Failed to serialize logging level");
assert_eq!(serialized, "\"ERROR\"");
}
#[test]
fn test_logging_level_deserialization() {
let level: LoggingLevel = serde_json::from_str("\"INFO\"")
.expect("Failed to deserialize logging level");
assert_eq!(level, LoggingLevel::Info);
let level: LoggingLevel = serde_json::from_str("\"info\"")
.expect("Failed to deserialize logging level");
assert_eq!(level, LoggingLevel::Info);
let result = serde_json::from_str::<LoggingLevel>("\"INVALID\"");
assert!(result.is_err());
}
#[test]
fn test_start_request_validation() {
let mut request = StartRequest::get_mock();
request.browser_profile_id = 1;
assert!(request.validate().is_ok());
let mut invalid_request = StartRequest::get_mock();
invalid_request.browser_profile_id = 0;
assert!(invalid_request.validate().is_err());
}
#[test]
fn test_connection_info_serialization() {
let connection_info = ConnectionInfo::get_mock();
let serialized = serde_json::to_string(&connection_info)
.expect("Failed to serialize connection info");
let deserialized: ConnectionInfo = serde_json::from_str(&serialized)
.expect("Failed to deserialize connection info");
assert_eq!(deserialized.country, connection_info.country);
assert_eq!(deserialized.ip, connection_info.ip);
assert_eq!(deserialized.geo, connection_info.geo);
}
#[test]
fn test_extension_type_serialization() {
let extension_type = ExtensionType::ChromeWebStore;
let serialized = serde_json::to_string(&extension_type)
.expect("Failed to serialize extension type");
assert_eq!(serialized, "\"chromeWebStore\"");
}
}