// Simplify build // - use std::fs::File; use chrono::{DateTime, Utc}; use rayon::iter::{ParallelBridge, ParallelIterator}; use tar::Archive; use crate::recipe_functions::common::{self, EnumAsValue}; pub type DefaultResult = Result<(), Box>; pub trait Peekable { fn peek(&self) -> Result, Box>; } #[derive(Debug, Clone)] /// Is expected to get information about last build pub struct LastBuildFirmware { pub path: String, pub date: DateTime, pub is_full: bool, /// Should be added later by calling implemented functions pub entries: Vec, pub tags: Option>, } impl Peekable for LastBuildFirmware { fn peek(&self) -> Result, Box> { let mut peeked = Vec::new(); let file = File::open(self.path.clone()).unwrap(); let decompressed = flate2::read::GzDecoder::new(file); let mut ar = Archive::new(decompressed); for file2 in ar.entries().unwrap() { let f = file2.unwrap(); peeked.push(f.path().unwrap().as_os_str().to_string_lossy().to_string()); } Ok(peeked) } } impl LastBuildFirmware { pub fn update_entries(&mut self) -> DefaultResult { self.entries = self.peek()?; Ok(()) } } pub fn read_last_build_firmwares() -> Result, Box> { // get config let ucfg = common::get_config(); let firmware_dir = ucfg .get("FIRMWARE_DIR") .expect("Unknown firmware directory, check .tbcfg"); // parallel walk to dir let result = walkdir::WalkDir::new(firmware_dir) .max_depth(2) .into_iter() .par_bridge() .filter(|w| { let filename = &w.as_ref().unwrap().clone(); let file_clone = filename.file_name().to_str().unwrap(); file_clone.ends_with(".tar") || file_clone.ends_with(".zip") }) .map(|entry| { let dir = entry.unwrap(); let meta = dir.clone().metadata().unwrap(); let filename = dir.clone().path().to_str().unwrap().to_string(); let mod_time: DateTime = meta.modified().unwrap().into(); let full_firmware = filename.clone().contains("full"); LastBuildFirmware { path: filename, date: mod_time, is_full: full_firmware, entries: Vec::new(), tags: None, } }) .collect(); Ok(result) } #[derive(Debug, Clone)] pub enum CountryShortCode { THA, MYS, AUS, SGP, UAE, DUBAI, HKG, GBR, CC(String), } impl EnumAsValue for CountryShortCode { fn name(&self) -> String { format!("{:?}", self.clone()).to_lowercase() } fn value(&self) -> String { self.name().to_lowercase() } fn get(val: String) -> Self { return unsafe { std::mem::transmute::<_, CountryShortCode>(val.to_uppercase()) }; } } #[allow(non_camel_case_types)] #[derive(Debug, Clone)] pub enum FirmwareTypes { FULL, PATCH, NOT_FULL, DEV, CUSTOM(String), } impl EnumAsValue for FirmwareTypes { fn name(&self) -> String { format!("{:?}", self.clone()).to_lowercase() } fn value(&self) -> String { self.name().to_lowercase() } fn get(val: String) -> Self { return unsafe { std::mem::transmute::<_, FirmwareTypes>(val.to_uppercase()) }; } } /// Filter each field in order pub struct FirmwareFilterOptions { short_country_code: Option, version: Option, fw_type: Option, additional_condition: Option bool>>, } impl Default for FirmwareFilterOptions { fn default() -> Self { Self { short_country_code: None, version: None, fw_type: None, additional_condition: None, } } } impl FirmwareFilterOptions { pub fn new() -> Self { FirmwareFilterOptions::default() } pub fn set_cc(&mut self, cc: CountryShortCode) -> &mut Self { self.short_country_code = Some(cc); self } pub fn set_version(&mut self, v: String) -> &mut Self { self.version = Some(v); self } pub fn set_fw_type(&mut self, fwt: FirmwareTypes) -> &mut Self { self.fw_type = Some(fwt); self } pub fn set_more_condition( &mut self, cond: Box bool>, ) -> &mut Self { self.additional_condition = Some(cond); self } } pub struct XBuilder { saved_firmwares: Vec, } impl XBuilder { pub fn new() -> Self { let saved_firmwares = read_last_build_firmwares().unwrap_or(Vec::new()); Self { saved_firmwares } } pub fn get_latest_by_options( &self, option: &mut FirmwareFilterOptions, ) -> Vec { let mut result = Vec::new(); if option.short_country_code.is_some() { let cc_option = option.short_country_code.as_ref().unwrap(); result = self .saved_firmwares .iter() .filter(|fw| fw.path.contains(&cc_option.name())) .map(|fw| fw.clone()) .collect(); } if option.version.is_some() { let filter_target = if result.is_empty() { self.saved_firmwares.clone() } else { result.clone() }; let version_option = option.version.as_ref().unwrap(); result = filter_target .iter() .filter(|fw| fw.path.contains(version_option)) .map(|fw| fw.clone()) .collect(); } if option.fw_type.is_some() { let filter_target = if result.is_empty() { self.saved_firmwares.clone() } else { result.clone() }; let fw_type = option.fw_type.as_ref().unwrap(); result = filter_target .iter() .filter(|fw| fw.path.contains(&fw_type.name())) .map(|fw| fw.clone()) .collect(); } if option.additional_condition.is_some() { let filter_target = if result.is_empty() { self.saved_firmwares.clone() } else { result.clone() }; let mut f_result = Vec::new(); let predicate = option.additional_condition.as_mut().unwrap(); for fw in filter_target { if predicate(fw.clone()) { f_result.push(fw.clone()); } } if !f_result.is_empty() { result = f_result; } } result } }