kwui_cli/
lib.rs

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            // println!("add [{}]", dir);
108            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        // need to trim the string to remove newlines at the end.
173        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}