1#![allow(unused, dead_code)]
2
3pub mod file_format;
4pub mod packager;
5pub mod binary_release;
6pub mod template_release;
7pub mod new;
8pub mod build;
9pub mod run;
10
11use itertools::Itertools;
12use path_clean;
13use std::path::{Path, PathBuf};
14use std::process::{Command, Stdio};
15use cargo_toml::Manifest;
16
17pub use packager::{list, unpack, PackItem};
18
19pub enum PackInput {
20 SourceFile { src: String },
21 SourceDir { src: String },
22 FileMapping { src: String, dst: String },
23 DirMapping { src: String, dst: String },
24}
25
26pub fn pack(output_file: &str, input_list: &[PackInput]) -> std::io::Result<()> {
27 let mut file_items = Vec::new();
28 let mut dir_items = Vec::new();
29 for input in input_list.into_iter() {
30 match input {
31 PackInput::SourceFile { src } => {
32 let src = path_clean::clean(&src);
33 let src_file_name = src
34 .file_name()
35 .ok_or_else(|| {
36 std::io::Error::other(format!("invalid source filename {}", src.to_string_lossy()))
37 })?
38 .to_string_lossy()
39 .to_string();
40 let dst = String::from("/") + src_file_name.as_str();
41 file_items.push(PackItem {
42 src: src.to_string_lossy().into(),
43 dst,
44 });
45 }
46 PackInput::SourceDir { src } => {
47 let src = path_clean::clean(&src);
48 let src_file_name = src
49 .file_name()
50 .ok_or_else(|| {
51 std::io::Error::other(format!("invalid source directory {}", src.to_string_lossy()))
52 })?
53 .to_string_lossy()
54 .to_string();
55 let dst = String::from("/") + src_file_name.as_str();
56
57 if let Ok((sub_file_items, sub_dir_items)) = scan_dir(src.as_ref(), &dst) {
58 for item in sub_file_items.into_iter() {
59 file_items.push(item);
60 }
61 for item in sub_dir_items.into_iter() {
62 dir_items.push(item);
63 }
64 }
65 }
66 PackInput::FileMapping { src, dst } => {
67 let src = path_clean::clean(&src);
68 let src_file_name = src
69 .file_name()
70 .ok_or_else(|| {
71 std::io::Error::other(format!("invalid source filename {}", src.to_string_lossy()))
72 })?
73 .to_string_lossy()
74 .to_string();
75 let dst = if dst.ends_with("/") {
76 dst.to_string() + src_file_name.as_str()
77 } else {
78 dst.to_string()
79 };
80 file_items.push(PackItem {
81 src: src.to_string_lossy().into(),
82 dst,
83 });
84 }
85 PackInput::DirMapping { src, dst } => {
86 let src = path_clean::clean(&src);
87 let dst = if dst.ends_with("/") {
88 dst[..(dst.len() - 1)].to_string()
89 } else {
90 dst.to_string()
91 };
92
93 if let Ok((sub_file_items, sub_dir_items)) = scan_dir(src.as_ref(), &dst) {
94 for item in sub_file_items.into_iter() {
95 file_items.push(item);
96 }
97 for item in sub_dir_items.into_iter() {
98 dir_items.push(item);
99 }
100 }
101 }
102 }
103 }
104 for f_item in file_items.iter() {
105 if let Some(idx) = f_item.dst.rfind('/') {
106 let dir = &f_item.dst[..(idx + 1)];
107 dir_items.push(dir.to_string());
109 }
110 }
111 dir_items.push("/".to_string());
112 dir_items.sort_by_key(|x| x.to_lowercase());
113 dir_items = dir_items.into_iter().dedup().collect();
114 crate::packager::pack(output_file, file_items, dir_items).map_err(|e| std::io::Error::other(e))
115}
116
117fn scan_dir(dir: &Path, dst: &str) -> anyhow::Result<(Vec<PackItem>, Vec<String>)> {
118 let mut file_items = Vec::new();
119 let mut dir_items = Vec::new();
120 for entry in walkdir::WalkDir::new(dir)
121 .follow_links(true)
122 .into_iter()
123 .filter_map(|e| e.ok())
124 {
125 let full_path = entry.path().to_string_lossy();
126 let f_components = entry
127 .path()
128 .components()
129 .map(|x| x.as_os_str().to_string_lossy().to_string())
130 .collect::<Vec<_>>();
131 let f_components = &f_components[(f_components.len() - entry.depth())..];
132 let dst = format!("{}/{}", dst, f_components.join("/"));
133
134 let meta = entry.metadata()?;
135 if meta.is_dir() {
136 let full_path = full_path + "/";
137 let dst = if dst.ends_with('/') { dst } else { dst + "/" };
138 println!("scan_dir, add dir [{}]:[{}]", full_path, dst);
139 dir_items.push(dst);
140 } else if meta.is_file() {
141 println!("scan_dir, add file [{}]:[{}]", full_path, dst);
142 file_items.push(PackItem {
143 src: full_path.to_string(),
144 dst,
145 });
146 }
147 }
148 Ok((file_items, dir_items))
149}
150
151pub fn check_source_dir(source_dir: &PathBuf) -> anyhow::Result<()> {
152 let manifest = Manifest::from_path(source_dir.join("Cargo.toml"))
153 .map_err(|e| {
154 eprintln!("Load Cargo.toml error: {}", e);
155 e
156 })?;
157 if let Some(ws) = manifest.workspace {
158 if ws.members.contains(&String::from("kwui-sys")) {
159 return Ok(());
160 }
161 }
162 anyhow::bail!("Invalid source_dir {}", source_dir.display())
163}
164
165pub fn git_half_hash(source_dir: &PathBuf) -> Option<String> {
166 let mut cmd = Command::new("git");
167 cmd.current_dir(source_dir).arg("rev-parse").arg("--short=20");
168 let output = cmd.arg("HEAD").stderr(Stdio::inherit()).output().ok()?;
169 if output.status.code() != Some(0) {
170 None
171 } else {
172 Some(String::from_utf8(output.stdout).unwrap().trim().to_string())
174 }
175}
176
177pub fn cargo_package_name(source_dir: &PathBuf) -> anyhow::Result<String> {
178 let manifest = Manifest::from_path(source_dir.join("Cargo.toml"))
179 .map_err(|e| {
180 eprintln!("Load Cargo.toml error: {}", e);
181 e
182 })?;
183 if let Some(pkg) = manifest.package {
184 Ok(pkg.name.clone())
185 } else {
186 anyhow::bail!("Invalid source_dir {}", source_dir.display())
187 }
188}