From 86629d4e85c94324c5049cdbd75c09194b918c76 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Sat, 18 Oct 2025 18:23:24 +0800 Subject: feat: add build_helper crate for publishing - Add Cargo.toml and main.rs for build helper - Support custom cargo pub command for binary distribution --- crates/build_helper/src/bin/exporter.rs | 145 ++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 crates/build_helper/src/bin/exporter.rs (limited to 'crates/build_helper/src') diff --git a/crates/build_helper/src/bin/exporter.rs b/crates/build_helper/src/bin/exporter.rs new file mode 100644 index 0000000..edc7e9c --- /dev/null +++ b/crates/build_helper/src/bin/exporter.rs @@ -0,0 +1,145 @@ +use std::{collections::VecDeque, env::current_dir}; + +use colored::Colorize; + +fn main() -> Result<(), Box> { + println!(" {} `.cargo/cargo.toml`", "Reading".green().bold()); + + let start_time = std::time::Instant::now(); + let mut copied_files = 0; + + let target_dir = current_target_dir().expect("Failed to get target directory"); + let publish_dir = current_publish_dir().expect("Failed to get publish directory"); + let publish_binaries = publish_binaries().expect("Failed to get publish binaries"); + + if publish_dir.exists() { + std::fs::remove_dir_all(&publish_dir)?; + } + std::fs::create_dir_all(&publish_dir)?; + + let mut queue = VecDeque::new(); + queue.push_back(target_dir); + + while let Some(current_dir) = queue.pop_front() { + let entries = match std::fs::read_dir(¤t_dir) { + Ok(entries) => entries, + Err(_) => continue, + }; + + for entry in entries.flatten() { + let path = entry.path(); + let metadata = match entry.metadata() { + Ok(metadata) => metadata, + Err(_) => continue, + }; + + if metadata.is_dir() { + queue.push_back(path); + continue; + } + + if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) + && publish_binaries.contains(&file_name.to_string()) { + let parent_dir_name = path + .parent() + .and_then(|p| p.file_name()) + .and_then(|n| n.to_str()) + .unwrap_or(""); + + let dest_path = publish_dir.join(parent_dir_name).join(file_name); + + if let Some(parent) = dest_path.parent() { + std::fs::create_dir_all(parent)?; + } + + println!( + " {} `{}/{}` ({})", + "Copy".green().bold(), + parent_dir_name, + file_name, + path.display() + ); + std::fs::copy(&path, &dest_path)?; + copied_files += 1; + } + } + } + + let duration = start_time.elapsed(); + println!(); + println!( + "Done (in {:.1}s) Publish {} {}", + duration.as_secs_f32(), + copied_files, + if copied_files == 1 { "file" } else { "files" } + ); + + Ok(()) +} + +/// Get a target directory from the cargo config +/// Returns the complete path relative to the current directory +fn get_target_dir(section: &str) -> Result { + let current = current_dir()?; + let config_file = current.join(".cargo").join("config.toml"); + let config_content = std::fs::read_to_string(&config_file)?; + let config: toml::Value = toml::from_str(&config_content).map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("Failed to parse config.toml: {}", e), + ) + })?; + let target_dir_str = config[section]["target-dir"].as_str().ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + "target-dir not found or not a string", + ) + })?; + + Ok(current.join(target_dir_str)) +} + +/// Get the binaries list from the cargo config +/// Returns a vector of binary names +fn get_array(section: &str, array_name: &str) -> Result, std::io::Error> { + let current = current_dir()?; + let config_file = current.join(".cargo").join("config.toml"); + let config_content = std::fs::read_to_string(&config_file)?; + let config: toml::Value = toml::from_str(&config_content).map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("Failed to parse config.toml: {}", e), + ) + })?; + + if let Some(array) = config[section][array_name].as_array() { + let arr: Vec = array + .iter() + .filter_map(|v| v.as_str().map(String::from)) + .collect(); + Ok(arr) + } else { + Ok(Vec::new()) + } +} + +/// Get the target directory of the current project +/// By reading the `build.target-dir` configuration item in the `.cargo/config.toml` file +/// Returns the complete path relative to the current directory +fn current_target_dir() -> Result { + get_target_dir("build") +} + +/// Get the publish directory of the current project +/// By reading the `publish.target-dir` configuration item in the `.cargo/config.toml` file +/// Returns the complete path relative to the current directory +fn current_publish_dir() -> Result { + get_target_dir("publish") +} + +/// Get the binaries list for publishing +// By reading the `publish.binaries` configuration item in the `.cargo/config.toml` file +// Returns a vector of binary names +fn publish_binaries() -> Result, std::io::Error> { + get_array("publish", "binaries") +} -- cgit