mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-30 16:09:12 +00:00
Merge https://github.com/DioxusLabs/cli into hot_reload
This commit is contained in:
commit
11d045deeb
11 changed files with 361 additions and 18 deletions
|
@ -2,7 +2,7 @@
|
|||
name = "dioxus-cli"
|
||||
version = "0.1.4"
|
||||
authors = ["Jonathan Kelley"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
description = "CLI tool for developing, testing, and publishing Dioxus apps"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
|
@ -39,11 +39,15 @@ hyper = "0.14.17"
|
|||
axum = { version = "0.5.1", features = ["ws", "headers"] }
|
||||
tower-http = { version = "0.2.2", features = ["fs", "trace"] }
|
||||
headers = "0.3.7"
|
||||
|
||||
walkdir = "2"
|
||||
|
||||
# tools download
|
||||
dirs = "4.0.0"
|
||||
reqwest = { version = "0.11", features = ["rustls-tls", "stream", "trust-dns"] }
|
||||
flate2 = "1.0.22"
|
||||
tar = "0.4.38"
|
||||
zip = "0.6.2"
|
||||
tower = "0.4.12"
|
||||
|
||||
syn = { version = "1.0" }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
version = "Two"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
imports_granularity = "Crate"
|
||||
#use_small_heuristics = "Max"
|
||||
|
|
|
@ -43,3 +43,18 @@ script = []
|
|||
# use binaryen.wasm-opt for output Wasm file
|
||||
# binaryen just will trigger in `web` platform
|
||||
binaryen = { wasm_opt = true }
|
||||
|
||||
# use sass auto will auto check sass file and build it.
|
||||
[application.tools.sass]
|
||||
|
||||
# auto will check the assets dirs, and auto to transform all scss file to css file.
|
||||
input = "*"
|
||||
|
||||
# or you can specify some scss file -> css file
|
||||
# input = [
|
||||
# # some sass file path
|
||||
# # this file will translate to `/css/test.css`
|
||||
# "/css/test.scss"
|
||||
# ]
|
||||
|
||||
source_map = true
|
194
src/builder.rs
194
src/builder.rs
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
config::{CrateConfig, ExecutableType},
|
||||
error::{Error, Result},
|
||||
tools::Tool,
|
||||
DioxusConfig,
|
||||
};
|
||||
use std::{
|
||||
|
@ -29,6 +30,9 @@ pub fn build(config: &CrateConfig) -> Result<()> {
|
|||
..
|
||||
} = config;
|
||||
|
||||
// start to build the assets
|
||||
let ignore_files = build_assets(config)?;
|
||||
|
||||
let t_start = std::time::Instant::now();
|
||||
|
||||
// [1] Build the .wasm module
|
||||
|
@ -44,6 +48,21 @@ pub fn build(config: &CrateConfig) -> Result<()> {
|
|||
if config.release {
|
||||
cmd.arg("--release");
|
||||
}
|
||||
if config.verbose {
|
||||
cmd.arg("--verbose");
|
||||
}
|
||||
|
||||
if config.custom_profile.is_some() {
|
||||
let custom_profile = config.custom_profile.as_ref().unwrap();
|
||||
cmd.arg("--profile");
|
||||
cmd.arg(custom_profile);
|
||||
}
|
||||
|
||||
if config.features.is_some() {
|
||||
let features_str = config.features.as_ref().unwrap().join(" ");
|
||||
cmd.arg("--features");
|
||||
cmd.arg(features_str);
|
||||
}
|
||||
|
||||
match executable {
|
||||
ExecutableType::Binary(name) => cmd.arg("--bin").arg(name),
|
||||
|
@ -154,6 +173,13 @@ pub fn build(config: &CrateConfig) -> Result<()> {
|
|||
log::warn!("Error copying dir: {}", _e);
|
||||
}
|
||||
}
|
||||
for ignore in &ignore_files {
|
||||
let ignore = ignore.strip_prefix(&config.asset_dir).unwrap();
|
||||
let ignore = config.out_dir.join(ignore);
|
||||
if ignore.is_file() {
|
||||
std::fs::remove_file(ignore)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -166,6 +192,8 @@ pub fn build(config: &CrateConfig) -> Result<()> {
|
|||
pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> {
|
||||
log::info!("🚅 Running build [Desktop] command...");
|
||||
|
||||
let ignore_files = build_assets(config)?;
|
||||
|
||||
let mut cmd = Command::new("cargo");
|
||||
cmd.current_dir(&config.crate_dir)
|
||||
.arg("build")
|
||||
|
@ -175,6 +203,21 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> {
|
|||
if config.release {
|
||||
cmd.arg("--release");
|
||||
}
|
||||
if config.verbose {
|
||||
cmd.arg("--verbose");
|
||||
}
|
||||
|
||||
if config.custom_profile.is_some() {
|
||||
let custom_profile = config.custom_profile.as_ref().unwrap();
|
||||
cmd.arg("--profile");
|
||||
cmd.arg(custom_profile);
|
||||
}
|
||||
|
||||
if config.features.is_some() {
|
||||
let features_str = config.features.as_ref().unwrap().join(" ");
|
||||
cmd.arg("--features");
|
||||
cmd.arg(features_str);
|
||||
}
|
||||
|
||||
match &config.executable {
|
||||
crate::ExecutableType::Binary(name) => cmd.arg("--bin").arg(name),
|
||||
|
@ -250,6 +293,13 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> {
|
|||
log::warn!("Error copying dir: {}", e);
|
||||
}
|
||||
}
|
||||
for ignore in &ignore_files {
|
||||
let ignore = ignore.strip_prefix(&config.asset_dir).unwrap();
|
||||
let ignore = config.out_dir.join(ignore);
|
||||
if ignore.is_file() {
|
||||
std::fs::remove_file(ignore)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -339,6 +389,150 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
|
|||
html.replace("{app_title}", &title)
|
||||
}
|
||||
|
||||
// this function will build some assets file
|
||||
// like sass tool resources
|
||||
// this function will return a array which file don't need copy to out_dir.
|
||||
fn build_assets(config: &CrateConfig) -> Result<Vec<PathBuf>> {
|
||||
let mut result = vec![];
|
||||
|
||||
let dioxus_config = &config.dioxus_config;
|
||||
let dioxus_tools = dioxus_config.application.tools.clone().unwrap_or_default();
|
||||
|
||||
// check sass tool state
|
||||
let sass = Tool::Sass;
|
||||
if sass.is_installed() && dioxus_tools.contains_key("sass") {
|
||||
let sass_conf = dioxus_tools.get("sass").unwrap();
|
||||
if let Some(tab) = sass_conf.as_table() {
|
||||
let source_map = tab.contains_key("source_map");
|
||||
let source_map = if source_map && tab.get("source_map").unwrap().is_bool() {
|
||||
if tab.get("source_map").unwrap().as_bool().unwrap_or_default() {
|
||||
"--source-map"
|
||||
} else {
|
||||
"--no-source-map"
|
||||
}
|
||||
} else {
|
||||
"--source-map"
|
||||
};
|
||||
|
||||
if tab.contains_key("input") {
|
||||
if tab.get("input").unwrap().is_str() {
|
||||
let file = tab.get("input").unwrap().as_str().unwrap().trim();
|
||||
|
||||
if file == "*" {
|
||||
// if the sass open auto, we need auto-check the assets dir.
|
||||
let asset_dir = config.asset_dir.clone();
|
||||
if asset_dir.is_dir() {
|
||||
for entry in walkdir::WalkDir::new(&asset_dir)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
{
|
||||
let temp = entry.path();
|
||||
if temp.is_file() {
|
||||
let suffix = temp.extension();
|
||||
if suffix.is_none() { continue; }
|
||||
let suffix = suffix.unwrap().to_str().unwrap();
|
||||
if suffix == "scss" || suffix == "sass" {
|
||||
// if file suffix is `scss` / `sass` we need transform it.
|
||||
let out_file = format!(
|
||||
"{}.css",
|
||||
temp.file_stem().unwrap().to_str().unwrap()
|
||||
);
|
||||
let target_path = config
|
||||
.out_dir
|
||||
.join(
|
||||
temp.strip_prefix(&asset_dir)
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap(),
|
||||
)
|
||||
.join(out_file);
|
||||
let res = sass.call(
|
||||
"sass",
|
||||
vec![
|
||||
temp.to_str().unwrap(),
|
||||
target_path.to_str().unwrap(),
|
||||
source_map,
|
||||
],
|
||||
);
|
||||
if res.is_ok() {
|
||||
result.push(temp.to_path_buf());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// just transform one file.
|
||||
let relative_path = if &file[0..1] == "/" {
|
||||
&file[1..file.len()]
|
||||
} else {
|
||||
file
|
||||
};
|
||||
let path = config.asset_dir.join(relative_path);
|
||||
let out_file =
|
||||
format!("{}.css", path.file_stem().unwrap().to_str().unwrap());
|
||||
let target_path = config
|
||||
.out_dir
|
||||
.join(PathBuf::from(relative_path).parent().unwrap())
|
||||
.join(out_file);
|
||||
if path.is_file() {
|
||||
let res = sass.call(
|
||||
"sass",
|
||||
vec![
|
||||
path.to_str().unwrap(),
|
||||
target_path.to_str().unwrap(),
|
||||
source_map,
|
||||
],
|
||||
);
|
||||
if res.is_ok() {
|
||||
result.push(path);
|
||||
} else {
|
||||
log::error!("{:?}", res);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if tab.get("input").unwrap().is_array() {
|
||||
// check files list.
|
||||
let list = tab.get("input").unwrap().as_array().unwrap();
|
||||
for i in list {
|
||||
if i.is_str() {
|
||||
let path = i.as_str().unwrap();
|
||||
let relative_path = if &path[0..1] == "/" {
|
||||
&path[1..path.len()]
|
||||
} else {
|
||||
path
|
||||
};
|
||||
let path = config.asset_dir.join(relative_path);
|
||||
let out_file =
|
||||
format!("{}.css", path.file_stem().unwrap().to_str().unwrap());
|
||||
let target_path = config
|
||||
.out_dir
|
||||
.join(PathBuf::from(relative_path).parent().unwrap())
|
||||
.join(out_file);
|
||||
if path.is_file() {
|
||||
let res = sass.call(
|
||||
"sass",
|
||||
vec![
|
||||
path.to_str().unwrap(),
|
||||
target_path.to_str().unwrap(),
|
||||
source_map,
|
||||
],
|
||||
);
|
||||
if res.is_ok() {
|
||||
result.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// SASS END
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// use binary_install::{Cache, Download};
|
||||
|
||||
// /// Attempts to find `wasm-opt` in `PATH` locally, or failing that downloads a
|
||||
|
|
|
@ -14,11 +14,16 @@ impl Build {
|
|||
|
||||
// change the release state.
|
||||
crate_config.with_release(self.build.release);
|
||||
crate_config.with_verbose(self.build.verbose);
|
||||
|
||||
if self.build.example.is_some() {
|
||||
crate_config.as_example(self.build.example.unwrap());
|
||||
}
|
||||
|
||||
if self.build.profile.is_some() {
|
||||
crate_config.set_profile(self.build.profile.unwrap());
|
||||
}
|
||||
|
||||
let platform = self.build.platform.unwrap_or_else(|| {
|
||||
crate_config
|
||||
.dioxus_config
|
||||
|
|
|
@ -12,13 +12,26 @@ pub struct ConfigOptsBuild {
|
|||
#[serde(default)]
|
||||
pub release: bool,
|
||||
|
||||
// Use verbose output [default: false]
|
||||
#[clap(long)]
|
||||
#[serde(default)]
|
||||
pub verbose: bool,
|
||||
|
||||
/// Build a example [default: ""]
|
||||
#[clap(long)]
|
||||
pub example: Option<String>,
|
||||
|
||||
/// Build with custom profile
|
||||
#[clap(long)]
|
||||
pub profile: Option<String>,
|
||||
|
||||
/// Build platform: support Web & Desktop [default: "default_platform"]
|
||||
#[clap(long)]
|
||||
pub platform: Option<String>,
|
||||
|
||||
/// Space separated list of features to activate
|
||||
#[clap(long)]
|
||||
pub features: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Parser)]
|
||||
|
@ -36,6 +49,15 @@ pub struct ConfigOptsServe {
|
|||
#[serde(default)]
|
||||
pub release: bool,
|
||||
|
||||
// Use verbose output [default: false]
|
||||
#[clap(long)]
|
||||
#[serde(default)]
|
||||
pub verbose: bool,
|
||||
|
||||
/// Build with custom profile
|
||||
#[clap(long)]
|
||||
pub profile: Option<String>,
|
||||
|
||||
/// Build platform: support Web & Desktop [default: "default_platform"]
|
||||
#[clap(long)]
|
||||
pub platform: Option<String>,
|
||||
|
@ -44,6 +66,10 @@ pub struct ConfigOptsServe {
|
|||
#[clap(long)]
|
||||
#[serde(default)]
|
||||
pub hot_reload: bool,
|
||||
|
||||
/// Space separated list of features to activate
|
||||
#[clap(long)]
|
||||
pub features: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// Ensure the given value for `--public-url` is formatted correctly.
|
||||
|
|
|
@ -18,14 +18,18 @@ impl Serve {
|
|||
let mut crate_config = crate::CrateConfig::new()?;
|
||||
|
||||
// change the relase state.
|
||||
crate_config
|
||||
.with_release(self.serve.release)
|
||||
.with_hot_reload(self.serve.hot_reload);
|
||||
crate_config.with_hot_reload(self.serve.hot_reload);
|
||||
crate_config.with_release(self.serve.release);
|
||||
crate_config.with_verbose(self.serve.verbose);
|
||||
|
||||
if self.serve.example.is_some() {
|
||||
crate_config.as_example(self.serve.example.unwrap());
|
||||
}
|
||||
|
||||
if self.serve.profile.is_some() {
|
||||
crate_config.set_profile(self.serve.profile.unwrap());
|
||||
}
|
||||
|
||||
let platform = self.serve.platform.unwrap_or_else(|| {
|
||||
crate_config
|
||||
.dioxus_config
|
||||
|
|
|
@ -20,14 +20,18 @@ impl Tool {
|
|||
Tool::List {} => {
|
||||
for item in tools::tool_list() {
|
||||
if tools::Tool::from_str(item).unwrap().is_installed() {
|
||||
println!("{item} [installed]");
|
||||
println!("- {item} [installed]");
|
||||
} else {
|
||||
println!("{item}");
|
||||
println!("- {item}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Tool::AppPath {} => {
|
||||
println!("{}", tools::tools_path().to_str().unwrap());
|
||||
if let Some(v) = tools::tools_path().to_str() {
|
||||
println!("{}", v);
|
||||
} else {
|
||||
log::error!("Tools path get failed.");
|
||||
}
|
||||
}
|
||||
Tool::Add { name } => {
|
||||
let tool_list = tools::tool_list();
|
||||
|
@ -58,7 +62,6 @@ impl Tool {
|
|||
log::info!("Tool {name} install successfully!");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,6 +115,9 @@ pub struct CrateConfig {
|
|||
pub dioxus_config: DioxusConfig,
|
||||
pub release: bool,
|
||||
pub hot_reload: bool,
|
||||
pub verbose: bool,
|
||||
pub custom_profile: Option<String>,
|
||||
pub features: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -164,6 +167,9 @@ impl CrateConfig {
|
|||
|
||||
let release = false;
|
||||
let hot_reload = false;
|
||||
let verbose = false;
|
||||
let custom_profile = None;
|
||||
let features = None;
|
||||
|
||||
Ok(Self {
|
||||
out_dir,
|
||||
|
@ -176,6 +182,9 @@ impl CrateConfig {
|
|||
release,
|
||||
dioxus_config,
|
||||
hot_reload,
|
||||
custom_profile,
|
||||
features,
|
||||
verbose,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -194,6 +203,21 @@ impl CrateConfig {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_verbose(&mut self, verbose: bool) -> &mut Self {
|
||||
self.verbose = verbose;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_profile(&mut self, profile: String) -> &mut Self {
|
||||
self.custom_profile = Some(profile);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_features(&mut self, features: Vec<String>) -> &mut Self {
|
||||
self.features = Some(features);
|
||||
self
|
||||
}
|
||||
|
||||
// pub fn with_build_options(&mut self, options: &BuildOptions) {
|
||||
// if let Some(name) = &options.example {
|
||||
// self.as_example(name.clone());
|
||||
|
|
|
@ -189,7 +189,7 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> {
|
|||
let file_service_config = config.clone();
|
||||
let file_service = ServiceBuilder::new()
|
||||
.and_then(
|
||||
|response: Response<ServeFileSystemResponseBody>| async move {
|
||||
move |response: Response<ServeFileSystemResponseBody>| async move {
|
||||
let response = if file_service_config
|
||||
.dioxus_config
|
||||
.web
|
||||
|
@ -300,7 +300,7 @@ pub async fn startup_default(config: CrateConfig) -> Result<()> {
|
|||
let file_service_config = config.clone();
|
||||
let file_service = ServiceBuilder::new()
|
||||
.and_then(
|
||||
|response: Response<ServeFileSystemResponseBody>| async move {
|
||||
move |response: Response<ServeFileSystemResponseBody>| async move {
|
||||
let response = if file_service_config
|
||||
.dioxus_config
|
||||
.web
|
||||
|
|
78
src/tools.rs
78
src/tools.rs
|
@ -1,6 +1,6 @@
|
|||
use std::{
|
||||
fs::{create_dir_all, File},
|
||||
path::PathBuf,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
|
@ -13,10 +13,11 @@ use tokio::io::AsyncWriteExt;
|
|||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Tool {
|
||||
Binaryen,
|
||||
Sass,
|
||||
}
|
||||
|
||||
pub fn tool_list() -> Vec<&'static str> {
|
||||
vec!["binaryen"]
|
||||
vec!["binaryen", "sass"]
|
||||
}
|
||||
|
||||
pub fn app_path() -> PathBuf {
|
||||
|
@ -52,6 +53,7 @@ impl Tool {
|
|||
pub fn from_str(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
"binaryen" => Some(Self::Binaryen),
|
||||
"sass" => Some(Self::Sass),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +62,7 @@ impl Tool {
|
|||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
Self::Binaryen => "binaryen",
|
||||
Self::Sass => "sass",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,6 +70,7 @@ impl Tool {
|
|||
pub fn bin_path(&self) -> &str {
|
||||
match self {
|
||||
Self::Binaryen => "bin",
|
||||
Self::Sass => ".",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,6 +88,17 @@ impl Tool {
|
|||
panic!("unsupported platformm");
|
||||
}
|
||||
}
|
||||
Self::Sass => {
|
||||
if cfg!(target_os = "windows") {
|
||||
"windows"
|
||||
} else if cfg!(target_os = "macos") {
|
||||
"macos"
|
||||
} else if cfg!(target_os = "linux") {
|
||||
"linux"
|
||||
} else {
|
||||
panic!("unsupported platformm");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,6 +111,13 @@ impl Tool {
|
|||
target = self.target_platform()
|
||||
)
|
||||
}
|
||||
Self::Sass => {
|
||||
format!(
|
||||
"https://github.com/sass/dart-sass/releases/download/1.51.0/dart-sass-1.51.0-{target}-x64.{extension}",
|
||||
target = self.target_platform(),
|
||||
extension = self.extension()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,6 +125,13 @@ impl Tool {
|
|||
pub fn extension(&self) -> &str {
|
||||
match self {
|
||||
Self::Binaryen => "tar.gz",
|
||||
Self::Sass => {
|
||||
if cfg!(target_os = "windows") {
|
||||
"zip"
|
||||
} else {
|
||||
"tar.gz"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,7 +160,7 @@ impl Tool {
|
|||
let chunk = chunk_res.context("error reading chunk from download")?;
|
||||
let _ = file.write(chunk.as_ref()).await;
|
||||
}
|
||||
|
||||
// log::info!("temp file path: {:?}", temp_out);
|
||||
Ok(temp_out)
|
||||
}
|
||||
|
||||
|
@ -143,7 +172,7 @@ impl Tool {
|
|||
let dir_name = if self == &Tool::Binaryen {
|
||||
"binaryen-version_105"
|
||||
} else {
|
||||
""
|
||||
"dart-sass"
|
||||
};
|
||||
|
||||
if self.extension() == "tar.gz" {
|
||||
|
@ -151,7 +180,10 @@ impl Tool {
|
|||
let tar = GzDecoder::new(tar_gz);
|
||||
let mut archive = Archive::new(tar);
|
||||
archive.unpack(&tool_path)?;
|
||||
// println!("{:?} -> {:?}", tool_path.join(dir_name), tool_path.join(self.name()));
|
||||
std::fs::rename(tool_path.join(dir_name), tool_path.join(self.name()))?;
|
||||
} else if self.extension() == "zip" {
|
||||
// decompress the `zip` file
|
||||
extract_zip(&temp_path, &tool_path)?;
|
||||
std::fs::rename(tool_path.join(dir_name), tool_path.join(self.name()))?;
|
||||
}
|
||||
|
||||
|
@ -169,6 +201,13 @@ impl Tool {
|
|||
command.to_string()
|
||||
}
|
||||
}
|
||||
Tool::Sass => {
|
||||
if cfg!(target_os = "windows") {
|
||||
format!("{}.bat", command)
|
||||
} else {
|
||||
command.to_string()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if !bin_path.join(&command_file).is_file() {
|
||||
|
@ -185,3 +224,32 @@ impl Tool {
|
|||
Ok(output.stdout)
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> {
|
||||
let zip_file = std::fs::File::open(&file)?;
|
||||
let mut zip = zip::ZipArchive::new(zip_file)?;
|
||||
|
||||
if !target.exists() {
|
||||
let _ = std::fs::create_dir_all(target)?;
|
||||
}
|
||||
|
||||
for i in 0..zip.len() {
|
||||
let mut file = zip.by_index(i)?;
|
||||
if file.is_dir() {
|
||||
// dir
|
||||
let target = target.join(Path::new(&file.name().replace('\\', "")));
|
||||
let _ = std::fs::create_dir_all(target)?;
|
||||
} else {
|
||||
// file
|
||||
let file_path = target.join(Path::new(file.name()));
|
||||
let mut target_file = if !file_path.exists() {
|
||||
std::fs::File::create(file_path)?
|
||||
} else {
|
||||
std::fs::File::open(file_path)?
|
||||
};
|
||||
let _num = std::io::copy(&mut file, &mut target_file)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue