2025-09-08 10:45:36 +07:00
|
|
|
// Simplify build
|
|
|
|
|
// -
|
|
|
|
|
|
|
|
|
|
use std::fs::File;
|
|
|
|
|
|
|
|
|
|
use chrono::{DateTime, Utc};
|
|
|
|
|
use rayon::iter::{ParallelBridge, ParallelIterator};
|
|
|
|
|
use tar::Archive;
|
|
|
|
|
|
2025-09-08 13:42:18 +07:00
|
|
|
use crate::recipe_functions::common::{self, EnumAsValue};
|
|
|
|
|
|
|
|
|
|
pub type DefaultResult = Result<(), Box<dyn std::error::Error>>;
|
2025-09-08 10:45:36 +07:00
|
|
|
|
|
|
|
|
pub trait Peekable {
|
|
|
|
|
fn peek(&self) -> Result<Vec<String>, Box<dyn std::error::Error>>;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 13:42:18 +07:00
|
|
|
#[derive(Debug, Clone)]
|
2025-09-08 10:45:36 +07:00
|
|
|
/// Is expected to get information about last build
|
|
|
|
|
pub struct LastBuildFirmware {
|
|
|
|
|
pub path: String,
|
|
|
|
|
pub date: DateTime<Utc>,
|
|
|
|
|
pub is_full: bool,
|
|
|
|
|
/// Should be added later by calling implemented functions
|
|
|
|
|
pub entries: Vec<String>,
|
|
|
|
|
pub tags: Option<Vec<String>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Peekable for LastBuildFirmware {
|
|
|
|
|
fn peek(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
|
|
|
|
|
let mut peeked = Vec::new();
|
|
|
|
|
|
2025-09-08 11:07:13 +07:00
|
|
|
let file = File::open(self.path.clone()).unwrap();
|
2025-09-08 11:25:46 +07:00
|
|
|
let decompressed = flate2::read::GzDecoder::new(file);
|
|
|
|
|
let mut ar = Archive::new(decompressed);
|
2025-09-08 10:45:36 +07:00
|
|
|
|
|
|
|
|
for file2 in ar.entries().unwrap() {
|
2025-09-08 11:25:46 +07:00
|
|
|
let f = file2.unwrap();
|
2025-09-08 12:05:08 +07:00
|
|
|
peeked.push(f.path().unwrap().as_os_str().to_string_lossy().to_string());
|
2025-09-08 10:45:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(peeked)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 13:42:18 +07:00
|
|
|
impl LastBuildFirmware {
|
|
|
|
|
pub fn update_entries(&mut self) -> DefaultResult {
|
|
|
|
|
self.entries = self.peek()?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 10:45:36 +07:00
|
|
|
pub fn read_last_build_firmwares() -> Result<Vec<LastBuildFirmware>, Box<dyn std::error::Error>> {
|
|
|
|
|
// get config
|
|
|
|
|
let ucfg = common::get_config();
|
|
|
|
|
let firmware_dir = ucfg
|
|
|
|
|
.get("FIRMWARE_DIR")
|
|
|
|
|
.expect("Unknown firmware directory, check .tbcfg");
|
|
|
|
|
|
|
|
|
|
// parallel walk to dir
|
2025-09-08 11:07:13 +07:00
|
|
|
let result = walkdir::WalkDir::new(firmware_dir)
|
2025-09-08 10:45:36 +07:00
|
|
|
.max_depth(2)
|
|
|
|
|
.into_iter()
|
|
|
|
|
.par_bridge()
|
|
|
|
|
.filter(|w| {
|
2025-09-08 11:07:13 +07:00
|
|
|
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")
|
2025-09-08 10:45:36 +07:00
|
|
|
})
|
2025-09-08 11:07:13 +07:00
|
|
|
.map(|entry| {
|
|
|
|
|
let dir = entry.unwrap();
|
|
|
|
|
let meta = dir.clone().metadata().unwrap();
|
2025-09-08 10:45:36 +07:00
|
|
|
|
2025-09-08 11:10:36 +07:00
|
|
|
let filename = dir.clone().path().to_str().unwrap().to_string();
|
2025-09-08 11:07:13 +07:00
|
|
|
let mod_time: DateTime<Utc> = meta.modified().unwrap().into();
|
2025-09-08 10:45:36 +07:00
|
|
|
let full_firmware = filename.clone().contains("full");
|
|
|
|
|
|
2025-09-08 11:07:13 +07:00
|
|
|
LastBuildFirmware {
|
|
|
|
|
path: filename,
|
2025-09-08 10:45:36 +07:00
|
|
|
date: mod_time,
|
|
|
|
|
is_full: full_firmware,
|
|
|
|
|
entries: Vec::new(),
|
|
|
|
|
tags: None,
|
2025-09-08 11:07:13 +07:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
2025-09-08 10:45:36 +07:00
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
|
}
|
2025-09-08 13:42:18 +07:00
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum CountryShortCode {
|
|
|
|
|
THA,
|
|
|
|
|
MYS,
|
|
|
|
|
AUS,
|
|
|
|
|
SGP,
|
|
|
|
|
UAE,
|
|
|
|
|
DUBAI,
|
|
|
|
|
HKG,
|
|
|
|
|
GBR,
|
|
|
|
|
CC(String),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl EnumAsValue<String> 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<String> 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<CountryShortCode>,
|
|
|
|
|
version: Option<String>,
|
|
|
|
|
fw_type: Option<FirmwareTypes>,
|
|
|
|
|
additional_condition: Option<Box<dyn FnMut(LastBuildFirmware) -> 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<dyn FnMut(LastBuildFirmware) -> bool>,
|
|
|
|
|
) -> &mut Self {
|
|
|
|
|
self.additional_condition = Some(cond);
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct XBuilder {
|
|
|
|
|
saved_firmwares: Vec<LastBuildFirmware>,
|
2025-09-08 15:53:36 +07:00
|
|
|
base: Option<LastBuildFirmware>,
|
2025-09-08 13:42:18 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl XBuilder {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
let saved_firmwares = read_last_build_firmwares().unwrap_or(Vec::new());
|
|
|
|
|
|
2025-09-08 15:53:36 +07:00
|
|
|
Self {
|
|
|
|
|
saved_firmwares,
|
|
|
|
|
base: None,
|
|
|
|
|
}
|
2025-09-08 13:42:18 +07:00
|
|
|
}
|
|
|
|
|
|
2025-09-08 13:48:32 +07:00
|
|
|
pub fn get_latest_by_options(
|
|
|
|
|
&self,
|
|
|
|
|
option: &mut FirmwareFilterOptions,
|
|
|
|
|
) -> Vec<LastBuildFirmware> {
|
2025-09-08 13:42:18 +07:00
|
|
|
let mut result = Vec::new();
|
|
|
|
|
|
|
|
|
|
if option.short_country_code.is_some() {
|
2025-09-08 13:48:32 +07:00
|
|
|
let cc_option = option.short_country_code.as_ref().unwrap();
|
2025-09-08 13:42:18 +07:00
|
|
|
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()
|
|
|
|
|
};
|
2025-09-08 13:48:32 +07:00
|
|
|
let version_option = option.version.as_ref().unwrap();
|
2025-09-08 13:42:18 +07:00
|
|
|
|
|
|
|
|
result = filter_target
|
|
|
|
|
.iter()
|
2025-09-08 13:48:32 +07:00
|
|
|
.filter(|fw| fw.path.contains(version_option))
|
2025-09-08 13:42:18 +07:00
|
|
|
.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()
|
|
|
|
|
};
|
2025-09-08 13:48:32 +07:00
|
|
|
let fw_type = option.fw_type.as_ref().unwrap();
|
2025-09-08 13:42:18 +07:00
|
|
|
|
|
|
|
|
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();
|
2025-09-08 13:48:32 +07:00
|
|
|
let predicate = option.additional_condition.as_mut().unwrap();
|
2025-09-08 13:42:18 +07:00
|
|
|
|
|
|
|
|
for fw in filter_target {
|
|
|
|
|
if predicate(fw.clone()) {
|
|
|
|
|
f_result.push(fw.clone());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !f_result.is_empty() {
|
|
|
|
|
result = f_result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
}
|
2025-09-08 15:53:36 +07:00
|
|
|
|
|
|
|
|
pub fn set_base(&mut self, fwb: LastBuildFirmware) {
|
|
|
|
|
//
|
|
|
|
|
let mut base = fwb.clone();
|
|
|
|
|
if base.entries.is_empty() {
|
|
|
|
|
base.update_entries().unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.base = Some(base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// get last commit that should add to file
|
|
|
|
|
pub fn sync_file_list(&mut self) -> Vec<String> {
|
|
|
|
|
let mut result = Vec::new();
|
|
|
|
|
|
|
|
|
|
// get config
|
|
|
|
|
let ucfg = common::get_config();
|
|
|
|
|
let taobin_repo = ucfg
|
|
|
|
|
.get("TAOBIN_REPO")
|
|
|
|
|
.expect("Unknown git directory, check .tbcfg");
|
|
|
|
|
|
|
|
|
|
let git_res = git2::Repository::open(taobin_repo);
|
|
|
|
|
|
|
|
|
|
let last_build_from_base = self.base.as_ref().unwrap().date.timestamp();
|
|
|
|
|
|
|
|
|
|
match git_res {
|
|
|
|
|
Ok(repo) => {
|
|
|
|
|
let mut revwalk = repo.revwalk().expect("Failed to create revwalk");
|
|
|
|
|
for cid in revwalk {
|
|
|
|
|
let commit_id = cid.expect("Failed to get commit id");
|
|
|
|
|
let commit = repo.find_commit(commit_id).expect("Failed to find commit");
|
|
|
|
|
|
|
|
|
|
let commit_time = commit.time();
|
|
|
|
|
if commit_time.seconds() >= last_build_from_base {
|
|
|
|
|
println!(
|
|
|
|
|
"Commit {} [{}]",
|
|
|
|
|
commit.id(),
|
|
|
|
|
commit.message().unwrap_or("-")
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("Error while trying to open repo, {:?}", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
}
|
2025-09-08 13:42:18 +07:00
|
|
|
}
|