1use std::fs;
3use std::path::{Path, PathBuf};
4use std::io;
5
6pub struct DistConfig<'a> {
8 pub target_dir: &'a str,
9 pub dist_dir: &'a str,
10 pub binaries: Vec<&'a str>,
12 pub clear_dist: bool,
13 pub overwrite: bool,
14 pub dry_run: bool,
15}
16
17impl<'a> Default for DistConfig<'a> {
18 fn default() -> Self {
19 Self {
20 target_dir: "./target",
21 dist_dir: "./dist",
22 binaries: vec![],
23 clear_dist: false,
24 overwrite: true,
25 dry_run: false,
26 }
27 }
28}
29
30fn parse_os_from_triple(triple: &str) -> String {
32 let t = triple.to_lowercase();
33 if t.contains("windows") { "windows".to_string() }
34 else if t.contains("linux") { "linux".to_string() }
35 else if t.contains("darwin") || t.contains("apple") { "macos".to_string() }
36 else if t.contains("android") { "android".to_string() }
37 else if t.contains("wasm") { "wasm".to_string() }
38 else { "unknown".to_string() }
39}
40
41fn is_likely_binary(path: &Path, os_name: &str) -> bool {
43 if !path.is_file() { return false; }
44
45 let file_name = path.file_name().unwrap_or_default().to_string_lossy();
47 if file_name.starts_with('.') { return false; }
48
49 if let Some(ext) = path.extension() {
50 let ext_str = ext.to_string_lossy().to_lowercase();
51 if ["d", "rlib", "rmeta", "pdb", "lib", "dll", "so", "dylib"].contains(&ext_str.as_str()) {
53 return false;
54 }
55 if os_name == "windows" { return ext_str == "exe"; }
56 if os_name == "wasm" { return ext_str == "wasm"; }
57 } else {
58 if os_name == "windows" { return false; }
60 }
61
62 true
63}
64
65pub fn copy_dist(config: &DistConfig) -> io::Result<Vec<(PathBuf, PathBuf)>> {
68 let target_path = Path::new(config.target_dir);
69 let dist_path = Path::new(config.dist_dir);
70
71 if !target_path.exists() {
73 return Err(io::Error::new(
74 io::ErrorKind::NotFound,
75 format!("Katalog '{}' nie istnieje. Uruchom najpierw `cargo build`.", config.target_dir)
76 ));
77 }
78
79 if config.clear_dist && dist_path.exists() && !config.dry_run {
81 let _ = fs::remove_dir_all(dist_path);
83 }
84
85 let mut found_files = Vec::new(); let profiles = ["debug", "release"];
87
88 let mut scan_directory = |search_dir: &Path, os_name: &str, dest_base_dir: &Path| {
90 if config.binaries.is_empty() {
91 if let Ok(entries) = fs::read_dir(search_dir) {
93 for entry in entries.filter_map(|e| e.ok()) {
94 let path = entry.path();
95 if is_likely_binary(&path, os_name) {
96 let dest_file = dest_base_dir.join(path.file_name().unwrap());
97 found_files.push((path, dest_base_dir.to_path_buf(), dest_file));
98 }
99 }
100 }
101 } else {
102 for bin in &config.binaries {
104 let suffix = if os_name == "windows" { ".exe" } else if os_name == "wasm" { ".wasm" } else { "" };
105 let full_name = format!("{}{}", bin, suffix);
106 let path = search_dir.join(&full_name);
107 if path.exists() {
108 let dest_file = dest_base_dir.join(&full_name);
109 found_files.push((path, dest_base_dir.to_path_buf(), dest_file));
110 }
111 }
112 }
113 };
114
115 let host_os = std::env::consts::OS;
119 for profile in &profiles {
120 let search_dir = target_path.join(profile);
121 let dest_base = dist_path.join(host_os).join(profile);
122 if search_dir.exists() {
123 scan_directory(&search_dir, host_os, &dest_base);
124 }
125 }
126
127 if let Ok(entries) = fs::read_dir(target_path) {
131 for entry in entries.filter_map(|e| e.ok()) {
132 let path = entry.path();
133 if path.is_dir() {
134 let dir_name = path.file_name().unwrap_or_default().to_string_lossy();
135 if dir_name.contains('-') {
136 let os_name = parse_os_from_triple(&dir_name);
137 for profile in &profiles {
138 let search_dir = path.join(profile);
139 let dest_base = dist_path.join(&os_name).join(profile);
140 if search_dir.exists() {
141 scan_directory(&search_dir, &os_name, &dest_base);
142 }
143 }
144 }
145 }
146 }
147 }
148
149 let mut processed_files = Vec::new();
153
154 for (src, dest_dir, dest_file) in found_files {
155 if dest_file.exists() && !config.overwrite {
157 continue; }
159
160 if !config.dry_run {
161 fs::create_dir_all(&dest_dir)?;
162 fs::copy(&src, &dest_file)?;
163 }
164
165 processed_files.push((src, dest_file));
166 }
167
168 Ok(processed_files)
169}