use crate::stats::Props;
use crate::Logger;
use serde::{Deserialize, Serialize};
use std::fs;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
const METADATA_FILE: &str = "se-metadata.json";
#[derive(Serialize, Deserialize)]
pub struct Stats {
pub browser: String,
pub browser_version: String,
pub os: String,
pub arch: String,
pub lang: String,
pub selenium_version: String,
pub stats_ttl: u64,
}
#[derive(Serialize, Deserialize)]
pub struct Browser {
pub browser_name: String,
pub major_browser_version: String,
pub browser_version: String,
pub browser_ttl: u64,
}
#[derive(Serialize, Deserialize)]
pub struct Driver {
pub major_browser_version: String,
pub driver_name: String,
pub driver_version: String,
pub driver_ttl: u64,
}
#[derive(Serialize, Deserialize)]
pub struct Metadata {
pub browsers: Vec<Browser>,
pub drivers: Vec<Driver>,
pub stats: Vec<Stats>,
}
fn get_metadata_path(cache_path: PathBuf) -> PathBuf {
cache_path.join(METADATA_FILE)
}
pub fn now_unix_timestamp() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
}
fn new_metadata(log: &Logger) -> Metadata {
log.trace("Metadata file does not exist. Creating a new one".to_string());
Metadata {
browsers: Vec::new(),
drivers: Vec::new(),
stats: Vec::new(),
}
}
pub fn get_metadata(log: &Logger, cache_path: &Option<PathBuf>) -> Metadata {
if let Some(cache) = cache_path {
let metadata_path = get_metadata_path(cache.clone());
log.trace(format!("Reading metadata from {}", metadata_path.display()));
if metadata_path.exists() {
let metadata_file = File::open(&metadata_path).unwrap();
let metadata: Metadata = match serde_json::from_reader(&metadata_file) {
Ok::<Metadata, serde_json::Error>(mut meta) => {
let now = now_unix_timestamp();
meta.browsers.retain(|b| b.browser_ttl > now);
meta.drivers.retain(|d| d.driver_ttl > now);
meta.stats.retain(|s| s.stats_ttl > now);
meta
}
Err(_e) => new_metadata(log),
};
return metadata;
}
}
new_metadata(log)
}
pub fn get_browser_version_from_metadata(
browsers_metadata: &[Browser],
browser_name: &str,
major_browser_version: &str,
) -> Option<String> {
let browser: Vec<&Browser> = browsers_metadata
.iter()
.filter(|b| {
b.browser_name.eq(browser_name) && b.major_browser_version.eq(major_browser_version)
})
.collect();
if browser.is_empty() {
None
} else {
Some(browser.first().unwrap().browser_version.to_string())
}
}
pub fn get_driver_version_from_metadata(
drivers_metadata: &[Driver],
driver_name: &str,
major_browser_version: &str,
) -> Option<String> {
let driver: Vec<&Driver> = drivers_metadata
.iter()
.filter(|d| {
d.driver_name.eq(driver_name) && d.major_browser_version.eq(major_browser_version)
})
.collect();
if driver.is_empty() {
None
} else {
Some(driver.first().unwrap().driver_version.to_string())
}
}
pub fn is_stats_in_metadata(stats_metadata: &[Stats], props: &Props) -> bool {
let props: Vec<&Stats> = stats_metadata
.iter()
.filter(|p| {
p.browser.eq(&props.browser)
&& p.browser_version.eq(&props.browser_version)
&& p.os.eq(&props.os)
&& p.arch.eq(&props.arch)
&& p.lang.eq(&props.lang)
&& p.selenium_version.eq(&props.selenium_version)
})
.collect();
!props.is_empty()
}
pub fn create_browser_metadata(
browser_name: &str,
major_browser_version: &str,
browser_version: &str,
browser_ttl: u64,
) -> Browser {
Browser {
browser_name: browser_name.to_string(),
major_browser_version: major_browser_version.to_string(),
browser_version: browser_version.to_string(),
browser_ttl: now_unix_timestamp() + browser_ttl,
}
}
pub fn create_driver_metadata(
major_browser_version: &str,
driver_name: &str,
driver_version: &str,
driver_ttl: u64,
) -> Driver {
Driver {
major_browser_version: major_browser_version.to_string(),
driver_name: driver_name.to_string(),
driver_version: driver_version.to_string(),
driver_ttl: now_unix_timestamp() + driver_ttl,
}
}
pub fn create_stats_metadata(props: &Props, stats_ttl: u64) -> Stats {
Stats {
browser: props.browser.to_string(),
browser_version: props.browser_version.to_string(),
os: props.os.to_string(),
arch: props.arch.to_string(),
lang: props.lang.to_string(),
selenium_version: props.selenium_version.to_string(),
stats_ttl: now_unix_timestamp() + stats_ttl,
}
}
pub fn write_metadata(metadata: &Metadata, log: &Logger, cache_path: Option<PathBuf>) {
if let Some(cache) = cache_path {
let metadata_path = get_metadata_path(cache.clone());
log.trace(format!("Writing metadata to {}", metadata_path.display()));
fs::write(
metadata_path,
serde_json::to_string_pretty(metadata).unwrap(),
)
.unwrap_or_else(|err| {
log.warn(format!(
"Metadata cannot be written in cache ({}): {}",
cache.display(),
err
));
});
}
}
pub fn clear_metadata(log: &Logger, path: &str) {
let cache_path = Path::new(path).to_path_buf();
let metadata_path = get_metadata_path(cache_path);
log.debug(format!(
"Deleting metadata file {}",
metadata_path.display()
));
fs::remove_file(metadata_path).unwrap_or_else(|err| {
log.warn(format!("Error deleting metadata file: {}", err));
});
}