#!/usr/bin/env runrs //! ```cargo //! [dependencies] //! serde = { version = "1.0", features = ["derive"] } //! serde_json = "1.0" //! sha256 = "1.5" //! rust_util = { version = "0.6" } //! ``` use rust_util::{ debugging, failure_and_exit, iff, opt_result, opt_value_result, success, warning, XResult, }; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::fs; use std::option::Option; use std::path::PathBuf; const SCRIPT_META_FILE: &str = "script-meta-v2.json"; const SCRIPT_CONFIG_FILE: &str = "script-config.json"; const SINGLE_SCRIPTS_DIR: &str = "single-scripts"; #[derive(Debug, Deserialize)] struct ScriptConfig { file_ext: String, simple_script_url: String, project_script_url: String, script_sub_dir: Option, skip_dirs: Vec, } impl ScriptConfig { fn skip_dir(&self, dir: &str) -> bool { for skip_dir in &self.skip_dirs { if skip_dir == dir { return true; } } dir == "update-meta-rs" || dir == SINGLE_SCRIPTS_DIR || dir.starts_with(".") } } #[derive(Serialize)] struct ScriptMeta { script_name: String, script_length: u64, script_sha256: String, script_full_url: String, #[serde(skip_serializing_if = "Option::is_none")] single_script_file: Option, } fn main() -> XResult<()> { if fs::metadata(SCRIPT_CONFIG_FILE).is_err() { failure_and_exit!("Script config file {} not found.", SCRIPT_CONFIG_FILE); } if fs::metadata(SCRIPT_META_FILE).is_err() { failure_and_exit!("Script meta file {} not found.", SCRIPT_META_FILE); } let script_config_content = opt_result!( fs::read_to_string(SCRIPT_CONFIG_FILE), "Read {}, failed: {}", SCRIPT_CONFIG_FILE ); let script_config: ScriptConfig = opt_result!( serde_json::from_str(&script_config_content), "Parse {}, failed: {}", SCRIPT_CONFIG_FILE ); debugging!("Script config: {:#?}", script_config); let mut script_meta_map = BTreeMap::new(); let current_read_dir = opt_result!(fs::read_dir("."), "Read dir '.' failed: {}"); for dir in current_read_dir { let dir_entry = opt_result!(dir, "Get dir failed: {}"); let dir_file_type = opt_result!(dir_entry.file_type(), "Get dir type failed: {}"); let file_name_os_string = dir_entry.file_name(); let script_dir = opt_value_result!(file_name_os_string.to_str(), "Cannot get file name."); if !dir_file_type.is_dir() { debugging!("Skip none dir: {}", script_dir); continue; } if script_config.skip_dir(&script_dir) { debugging!("Skip update skip dirs: {}", script_dir); continue; } let abs_dir_entry = std::path::absolute(&dir_entry.path())?; let mut main_script = abs_dir_entry; if let Some(sub_dir) = &script_config.script_sub_dir { main_script = main_script.join(sub_dir); } main_script = main_script.join(format!("main.{}", script_config.file_ext)); if let Some(script_file_name) = translate_script_dir_to_script_name(script_dir, &script_config) { script_meta_map.insert( script_file_name.clone(), read_script_meta( script_dir, script_file_name.clone(), &main_script, false, &script_config, )?, ); } } if let Ok(single_script_meta) = fs::metadata(SINGLE_SCRIPTS_DIR) { if single_script_meta.is_dir() { let single_scripts_read_dir = opt_result!(fs::read_dir(SINGLE_SCRIPTS_DIR), "Read dir '.' failed: {}"); for file in single_scripts_read_dir { let file_entry = opt_result!(file, "Get dir failed: {}"); let dir_file_type = opt_result!(file_entry.file_type(), "Get dir type failed: {}"); let file_name_os_string = file_entry.file_name(); let script_file = opt_value_result!(file_name_os_string.to_str(), "Cannot get file name."); if !dir_file_type.is_file() { debugging!("Skip none file: {}", script_file); continue; } let abs_file_entry = std::path::absolute(&file_entry.path())?; let script_file_ext = format!(".{}", script_config.file_ext); if !script_file.ends_with(&script_file_ext) { continue; } let script_file_name = script_file.to_string(); if script_meta_map.contains_key(&script_file_name) { warning!("Script: {script_file_name} exists."); continue; } script_meta_map.insert( script_file_name.clone(), read_script_meta( "", script_file_name.clone(), &abs_file_entry, true, &script_config, )?, ); } } } let script_meta_json = serde_json::to_string_pretty(&script_meta_map)?; fs::write(SCRIPT_META_FILE, script_meta_json.as_bytes())?; success!("Update file: {} succeed.", SCRIPT_META_FILE); Ok(()) } // translate filename-ext to filename.ext fn translate_script_dir_to_script_name( script_dir: &str, script_config: &ScriptConfig, ) -> Option { let script_dir_ext = format!("-{}", script_config.file_ext); if script_dir.ends_with(&script_dir_ext) { let remove_ext_dir_name = script_dir .chars() .take(script_dir.len() - script_dir_ext.len()) .collect::(); Some(format!("{remove_ext_dir_name}.{}", script_config.file_ext)) } else { None } } fn read_script_meta( script_dir: &str, script_name: String, script_path: &PathBuf, is_simple_script: bool, script_config: &ScriptConfig, ) -> XResult { let script_meta = opt_result!( script_path.metadata(), "Read file: {:?} meta failed: {}", script_path ); let script_path_content = fs::read(script_path)?; let script_sha256 = sha256::digest(&script_path_content); let script_full_url = if is_simple_script { script_config .simple_script_url .replace("$NAME", &script_name) } else { script_config .project_script_url .replace("$NAME", script_dir) }; let single_script_file = iff!(is_simple_script, Some(true), None); Ok(ScriptMeta { script_name: script_name.clone(), script_length: script_meta.len(), script_sha256, script_full_url, single_script_file, }) }