@devaloop/devalang 0.0.1-alpha.14 → 0.0.1-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devalang +10 -8
- package/.github/workflows/ci.yml +92 -0
- package/Cargo.toml +60 -58
- package/README.md +32 -15
- package/docs/CHANGELOG.md +93 -1
- package/docs/CONTRIBUTING.md +101 -1
- package/docs/ROADMAP.md +2 -2
- package/docs/TODO.md +1 -1
- package/examples/automation.deva +42 -0
- package/examples/bank.deva +4 -4
- package/examples/events.deva +12 -0
- package/examples/function.deva +4 -4
- package/examples/index.deva +39 -25
- package/examples/loop.deva +5 -11
- package/examples/pattern.deva +8 -0
- package/examples/plugin.deva +16 -0
- package/examples/variables.deva +1 -1
- package/out-tsc/bin/index.js +51 -7
- package/out-tsc/index.js +3 -1
- package/out-tsc/scripts/postbuild.js +9 -10
- package/out-tsc/scripts/postinstall.js +49 -0
- package/package.json +12 -4
- package/project-version.json +3 -3
- package/rust/cli/bank.rs +462 -456
- package/rust/cli/build.rs +252 -199
- package/rust/cli/check.rs +221 -180
- package/rust/cli/driver.rs +297 -292
- package/rust/cli/generator.rs +1 -0
- package/rust/cli/init.rs +87 -79
- package/rust/cli/install.rs +35 -32
- package/rust/cli/login.rs +127 -134
- package/rust/cli/mod.rs +13 -11
- package/rust/cli/play.rs +1123 -218
- package/rust/cli/telemetry.rs +19 -0
- package/rust/cli/template.rs +69 -57
- package/rust/cli/update.rs +6 -4
- package/rust/common/api.rs +5 -8
- package/rust/common/cdn.rs +3 -6
- package/rust/common/mod.rs +3 -3
- package/rust/common/sso.rs +3 -6
- package/rust/config/driver.rs +118 -94
- package/rust/config/loader.rs +165 -156
- package/rust/config/mod.rs +4 -2
- package/rust/config/settings.rs +91 -0
- package/rust/config/stats.rs +257 -0
- package/rust/core/audio/engine.rs +696 -518
- package/rust/core/audio/evaluator.rs +263 -31
- package/rust/core/audio/interpreter/arrow_call.rs +198 -161
- package/rust/core/audio/interpreter/automate.rs +18 -0
- package/rust/core/audio/interpreter/call.rs +98 -95
- package/rust/core/audio/interpreter/condition.rs +70 -71
- package/rust/core/audio/interpreter/driver.rs +487 -198
- package/rust/core/audio/interpreter/function.rs +26 -21
- package/rust/core/audio/interpreter/let_.rs +38 -19
- package/rust/core/audio/interpreter/load.rs +18 -18
- package/rust/core/audio/interpreter/loop_.rs +113 -73
- package/rust/core/audio/interpreter/mod.rs +14 -13
- package/rust/core/audio/interpreter/sleep.rs +27 -30
- package/rust/core/audio/interpreter/spawn.rs +105 -102
- package/rust/core/audio/interpreter/tempo.rs +19 -16
- package/rust/core/audio/interpreter/trigger.rs +239 -210
- package/rust/core/audio/loader/mod.rs +1 -1
- package/rust/core/audio/loader/trigger.rs +100 -97
- package/rust/core/audio/mod.rs +7 -6
- package/rust/core/audio/player.rs +64 -64
- package/rust/core/audio/renderer.rs +56 -53
- package/rust/core/audio/special/easing.rs +189 -0
- package/rust/core/audio/special/env.rs +43 -0
- package/rust/core/audio/special/math.rs +102 -0
- package/rust/core/audio/special/mod.rs +9 -0
- package/rust/core/audio/special/modulator.rs +143 -0
- package/rust/core/builder/mod.rs +80 -85
- package/rust/core/debugger/lexer.rs +27 -27
- package/rust/core/debugger/mod.rs +24 -23
- package/rust/core/debugger/module.rs +55 -47
- package/rust/core/debugger/preprocessor.rs +27 -27
- package/rust/core/debugger/store.rs +40 -39
- package/rust/core/error/mod.rs +80 -66
- package/rust/core/lexer/handler/arrow.rs +82 -31
- package/rust/core/lexer/handler/at.rs +21 -21
- package/rust/core/lexer/handler/brace.rs +41 -41
- package/rust/core/lexer/handler/colon.rs +21 -21
- package/rust/core/lexer/handler/comment.rs +30 -30
- package/rust/core/lexer/handler/dot.rs +21 -21
- package/rust/core/lexer/handler/driver.rs +337 -263
- package/rust/core/lexer/handler/identifier.rs +46 -42
- package/rust/core/lexer/handler/indent.rs +66 -66
- package/rust/core/lexer/handler/mod.rs +16 -16
- package/rust/core/lexer/handler/newline.rs +23 -23
- package/rust/core/lexer/handler/number.rs +31 -31
- package/rust/core/lexer/handler/operator.rs +46 -44
- package/rust/core/lexer/handler/parenthesis.rs +41 -41
- package/rust/core/lexer/handler/slash.rs +21 -21
- package/rust/core/lexer/handler/string.rs +63 -63
- package/rust/core/lexer/mod.rs +54 -51
- package/rust/core/lexer/token.rs +97 -91
- package/rust/core/mod.rs +11 -11
- package/rust/core/parser/driver.rs +513 -408
- package/rust/core/parser/handler/arrow_call.rs +233 -211
- package/rust/core/parser/handler/at.rs +245 -162
- package/rust/core/parser/handler/bank.rs +94 -69
- package/rust/core/parser/handler/condition.rs +80 -74
- package/rust/core/parser/handler/dot.rs +143 -135
- package/rust/core/parser/handler/identifier/automate.rs +257 -0
- package/rust/core/parser/handler/identifier/call.rs +91 -88
- package/rust/core/parser/handler/identifier/emit.rs +66 -0
- package/rust/core/parser/handler/identifier/function.rs +100 -92
- package/rust/core/parser/handler/identifier/group.rs +85 -75
- package/rust/core/parser/handler/identifier/let_.rs +158 -127
- package/rust/core/parser/handler/identifier/mod.rs +54 -52
- package/rust/core/parser/handler/identifier/on.rs +98 -0
- package/rust/core/parser/handler/identifier/print.rs +52 -0
- package/rust/core/parser/handler/identifier/sleep.rs +36 -33
- package/rust/core/parser/handler/identifier/spawn.rs +91 -88
- package/rust/core/parser/handler/identifier/synth.rs +65 -65
- package/rust/core/parser/handler/loop_.rs +170 -72
- package/rust/core/parser/handler/mod.rs +8 -8
- package/rust/core/parser/handler/tempo.rs +53 -47
- package/rust/core/parser/mod.rs +4 -4
- package/rust/core/parser/statement.rs +142 -108
- package/rust/core/plugin/loader.rs +123 -48
- package/rust/core/plugin/mod.rs +2 -1
- package/rust/core/plugin/runner.rs +296 -0
- package/rust/core/preprocessor/loader.rs +515 -326
- package/rust/core/preprocessor/mod.rs +4 -4
- package/rust/core/preprocessor/module.rs +60 -58
- package/rust/core/preprocessor/processor.rs +99 -101
- package/rust/core/preprocessor/resolver/bank.rs +51 -49
- package/rust/core/preprocessor/resolver/call.rs +100 -100
- package/rust/core/preprocessor/resolver/condition.rs +97 -97
- package/rust/core/preprocessor/resolver/driver.rs +310 -278
- package/rust/core/preprocessor/resolver/function.rs +69 -78
- package/rust/core/preprocessor/resolver/group.rs +96 -91
- package/rust/core/preprocessor/resolver/let_.rs +32 -28
- package/rust/core/preprocessor/resolver/loop_.rs +320 -91
- package/rust/core/preprocessor/resolver/mod.rs +15 -15
- package/rust/core/preprocessor/resolver/spawn.rs +76 -92
- package/rust/core/preprocessor/resolver/synth.rs +56 -50
- package/rust/core/preprocessor/resolver/tempo.rs +50 -49
- package/rust/core/preprocessor/resolver/trigger.rs +113 -116
- package/rust/core/preprocessor/resolver/value.rs +81 -87
- package/rust/core/shared/bank.rs +1 -1
- package/rust/core/shared/duration.rs +9 -9
- package/rust/core/shared/mod.rs +3 -3
- package/rust/core/shared/value.rs +35 -32
- package/rust/core/store/function.rs +34 -34
- package/rust/core/store/global.rs +55 -38
- package/rust/core/store/mod.rs +5 -5
- package/rust/core/store/variable.rs +37 -34
- package/rust/core/utils/mod.rs +2 -2
- package/rust/core/utils/path.rs +37 -31
- package/rust/core/utils/validation.rs +35 -37
- package/rust/installer/addon.rs +84 -80
- package/rust/installer/bank.rs +62 -65
- package/rust/installer/mod.rs +5 -5
- package/rust/installer/plugin.rs +54 -55
- package/rust/installer/utils.rs +56 -56
- package/rust/lib.rs +156 -164
- package/rust/main.rs +250 -145
- package/rust/utils/error.rs +200 -0
- package/rust/utils/file.rs +38 -35
- package/rust/utils/first_usage.rs +76 -0
- package/rust/utils/logger.rs +195 -139
- package/rust/utils/mod.rs +9 -50
- package/rust/utils/signature.rs +19 -17
- package/rust/utils/spinner.rs +22 -19
- package/rust/utils/telemetry.rs +292 -0
- package/rust/utils/watcher.rs +34 -33
- package/templates/minimal/README.md +97 -121
- package/templates/welcome/README.md +97 -121
- package/typescript/bin/index.ts +19 -5
- package/typescript/index.ts +3 -1
- package/typescript/scripts/postbuild.ts +10 -6
- package/typescript/scripts/postinstall.ts +56 -0
- package/typescript/scripts/version/bump.ts +0 -1
- package/typescript/scripts/version/index.ts +0 -1
- package/out-tsc/bin/devalang.exe +0 -0
package/rust/installer/bank.rs
CHANGED
|
@@ -1,65 +1,62 @@
|
|
|
1
|
-
use
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
},
|
|
8
|
-
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
pub async fn install_bank(name: &str, target_dir: &Path) -> Result<(), String> {
|
|
12
|
-
let logger = Logger::new();
|
|
13
|
-
|
|
14
|
-
let signed_url = ask_api_for_signed_url(AddonType::Bank, name).await?;
|
|
15
|
-
|
|
16
|
-
let bank_dir = target_dir.join("bank");
|
|
17
|
-
let archive_path = PathBuf::from(format!("./.deva/tmp/{}.devabank", name));
|
|
18
|
-
let extract_path = bank_dir.join(name);
|
|
19
|
-
|
|
20
|
-
download_file(&signed_url, &archive_path)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if extract_path.exists() {
|
|
25
|
-
logger.log_message(
|
|
26
|
-
LogLevel::Warning,
|
|
27
|
-
&format!(
|
|
28
|
-
"Bank '{}' already exists at '{}'. Skipping install.",
|
|
29
|
-
name,
|
|
30
|
-
extract_path.display()
|
|
31
|
-
)
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
return Ok(());
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Add the bank to the config
|
|
38
|
-
let root_dir = target_dir
|
|
39
|
-
.parent()
|
|
40
|
-
.ok_or_else(|| "Failed to determine root directory".to_string())?;
|
|
41
|
-
|
|
42
|
-
let config_path = root_dir.join(".devalang");
|
|
43
|
-
if !config_path.exists() {
|
|
44
|
-
return Err(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
Ok(())
|
|
65
|
-
}
|
|
1
|
+
use crate::{
|
|
2
|
+
config::loader::{add_bank_to_config, load_config},
|
|
3
|
+
installer::{
|
|
4
|
+
addon::{AddonType, ask_api_for_signed_url},
|
|
5
|
+
utils::{download_file, extract_archive},
|
|
6
|
+
},
|
|
7
|
+
utils::logger::{LogLevel, Logger},
|
|
8
|
+
};
|
|
9
|
+
use std::path::{Path, PathBuf};
|
|
10
|
+
|
|
11
|
+
pub async fn install_bank(name: &str, target_dir: &Path) -> Result<(), String> {
|
|
12
|
+
let logger = Logger::new();
|
|
13
|
+
|
|
14
|
+
let signed_url = ask_api_for_signed_url(AddonType::Bank, name).await?;
|
|
15
|
+
|
|
16
|
+
let bank_dir = target_dir.join("bank");
|
|
17
|
+
let archive_path = PathBuf::from(format!("./.deva/tmp/{}.devabank", name));
|
|
18
|
+
let extract_path = bank_dir.join(name);
|
|
19
|
+
|
|
20
|
+
download_file(&signed_url, &archive_path)
|
|
21
|
+
.await
|
|
22
|
+
.map_err(|e| format!("Failed to download: {}", e))?;
|
|
23
|
+
|
|
24
|
+
if extract_path.exists() {
|
|
25
|
+
logger.log_message(
|
|
26
|
+
LogLevel::Warning,
|
|
27
|
+
&format!(
|
|
28
|
+
"Bank '{}' already exists at '{}'. Skipping install.",
|
|
29
|
+
name,
|
|
30
|
+
extract_path.display()
|
|
31
|
+
),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
return Ok(());
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Add the bank to the config
|
|
38
|
+
let root_dir = target_dir
|
|
39
|
+
.parent()
|
|
40
|
+
.ok_or_else(|| "Failed to determine root directory".to_string())?;
|
|
41
|
+
|
|
42
|
+
let config_path = root_dir.join(".devalang");
|
|
43
|
+
if !config_path.exists() {
|
|
44
|
+
return Err(format!(
|
|
45
|
+
"Config file not found at '{}'. Please run 'devalang init' before adding an addon",
|
|
46
|
+
config_path.display()
|
|
47
|
+
));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let mut config = load_config(Some(&config_path))
|
|
51
|
+
.ok_or_else(|| format!("Failed to load config from '{}'", config_path.display()))?;
|
|
52
|
+
|
|
53
|
+
let dependency_path = &format!("devalang://bank/{}", name);
|
|
54
|
+
|
|
55
|
+
extract_archive(&archive_path, &extract_path)
|
|
56
|
+
.await
|
|
57
|
+
.map_err(|e| format!("Failed to extract: {}", e))?;
|
|
58
|
+
|
|
59
|
+
add_bank_to_config(&mut config, &extract_path, dependency_path);
|
|
60
|
+
|
|
61
|
+
Ok(())
|
|
62
|
+
}
|
package/rust/installer/mod.rs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
pub mod bank;
|
|
2
|
-
pub mod plugin;
|
|
3
|
-
|
|
4
|
-
pub mod
|
|
5
|
-
pub mod
|
|
1
|
+
pub mod bank;
|
|
2
|
+
pub mod plugin;
|
|
3
|
+
|
|
4
|
+
pub mod addon;
|
|
5
|
+
pub mod utils;
|
package/rust/installer/plugin.rs
CHANGED
|
@@ -1,55 +1,54 @@
|
|
|
1
|
-
use
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
pub async fn install_plugin(name: &str, target_dir: &Path) -> Result<(), String> {
|
|
9
|
-
let cdn_url = get_cdn_url();
|
|
10
|
-
let url = format!("{}/plugin/{}/download", cdn_url, name);
|
|
11
|
-
|
|
12
|
-
let plugin_dir = target_dir.join("plugin");
|
|
13
|
-
let archive_path = PathBuf::from(format!("./.deva/tmp/{}.devaplugin", name));
|
|
14
|
-
let extract_path = plugin_dir.join(name);
|
|
15
|
-
|
|
16
|
-
if extract_path.exists() {
|
|
17
|
-
println!(
|
|
18
|
-
"Plugin '{}' already exists at '{}'. Skipping install.",
|
|
19
|
-
name,
|
|
20
|
-
extract_path.display()
|
|
21
|
-
);
|
|
22
|
-
return Ok(());
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
download_file(&url, &archive_path)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
)
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
let mut config = load_config(Some(&config_path))
|
|
47
|
-
format!("Failed to load config from '{}'", config_path.display())
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
1
|
+
use crate::{
|
|
2
|
+
common::cdn::get_cdn_url,
|
|
3
|
+
config::loader::{add_plugin_to_config, load_config},
|
|
4
|
+
installer::utils::{download_file, extract_archive},
|
|
5
|
+
};
|
|
6
|
+
use std::path::{Path, PathBuf};
|
|
7
|
+
|
|
8
|
+
pub async fn install_plugin(name: &str, target_dir: &Path) -> Result<(), String> {
|
|
9
|
+
let cdn_url = get_cdn_url();
|
|
10
|
+
let url = format!("{}/plugin/{}/download", cdn_url, name);
|
|
11
|
+
|
|
12
|
+
let plugin_dir = target_dir.join("plugin");
|
|
13
|
+
let archive_path = PathBuf::from(format!("./.deva/tmp/{}.devaplugin", name));
|
|
14
|
+
let extract_path = plugin_dir.join(name);
|
|
15
|
+
|
|
16
|
+
if extract_path.exists() {
|
|
17
|
+
println!(
|
|
18
|
+
"Plugin '{}' already exists at '{}'. Skipping install.",
|
|
19
|
+
name,
|
|
20
|
+
extract_path.display()
|
|
21
|
+
);
|
|
22
|
+
return Ok(());
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
download_file(&url, &archive_path)
|
|
26
|
+
.await
|
|
27
|
+
.map_err(|e| format!("Failed to download: {}", e))?;
|
|
28
|
+
|
|
29
|
+
extract_archive(&archive_path, &extract_path)
|
|
30
|
+
.await
|
|
31
|
+
.map_err(|e| format!("Failed to extract: {}", e))?;
|
|
32
|
+
|
|
33
|
+
// Add the plugin to the config
|
|
34
|
+
let root_dir = target_dir
|
|
35
|
+
.parent()
|
|
36
|
+
.ok_or_else(|| "Failed to determine root directory".to_string())?;
|
|
37
|
+
|
|
38
|
+
let config_path = root_dir.join(".devalang");
|
|
39
|
+
if !config_path.exists() {
|
|
40
|
+
return Err(format!(
|
|
41
|
+
"Config file not found at '{}'. Please run 'devalang init' before adding an addon",
|
|
42
|
+
config_path.display()
|
|
43
|
+
));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let mut config = load_config(Some(&config_path))
|
|
47
|
+
.ok_or_else(|| format!("Failed to load config from '{}'", config_path.display()))?;
|
|
48
|
+
|
|
49
|
+
let dependency_path = &format!("devalang://plugin/{}", name);
|
|
50
|
+
|
|
51
|
+
add_plugin_to_config(&mut config, &extract_path, dependency_path);
|
|
52
|
+
|
|
53
|
+
Ok(())
|
|
54
|
+
}
|
package/rust/installer/utils.rs
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
use std::
|
|
2
|
-
use std::
|
|
3
|
-
use std::io::BufReader;
|
|
4
|
-
use std::
|
|
5
|
-
use std::
|
|
6
|
-
use zip::ZipArchive;
|
|
7
|
-
|
|
8
|
-
pub async fn download_file(url: &str, destination: &Path) -> Result<(), Box<dyn Error>> {
|
|
9
|
-
let response = reqwest::get(url).await?;
|
|
10
|
-
|
|
11
|
-
if !response.status().is_success() {
|
|
12
|
-
return Err(format!("Failed to download file: HTTP {}", response.status()).into());
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if let Some(parent) = destination.parent() {
|
|
16
|
-
std::fs::create_dir_all(parent)?;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
let bytes = response.bytes().await?;
|
|
20
|
-
let mut content = Cursor::new(bytes);
|
|
21
|
-
let mut file = File::create(destination)?;
|
|
22
|
-
copy(&mut content, &mut file)?;
|
|
23
|
-
|
|
24
|
-
Ok(())
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
pub async fn extract_archive(
|
|
28
|
-
zip_path: &Path,
|
|
29
|
-
destination: &Path
|
|
30
|
-
) -> Result<(), Box<dyn std::error::Error>> {
|
|
31
|
-
let file = File::open(zip_path)?;
|
|
32
|
-
let mut archive = ZipArchive::new(BufReader::new(file))?;
|
|
33
|
-
|
|
34
|
-
for i in 0..archive.len() {
|
|
35
|
-
let mut file = archive.by_index(i)?;
|
|
36
|
-
let outpath = destination.join(file.mangled_name());
|
|
37
|
-
|
|
38
|
-
if file.name().ends_with('/') {
|
|
39
|
-
std::fs::create_dir_all(&outpath)?;
|
|
40
|
-
} else {
|
|
41
|
-
if let Some(p) = outpath.parent() {
|
|
42
|
-
std::fs::create_dir_all(p)?;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let mut outfile = File::create(&outpath)?;
|
|
46
|
-
std::io::copy(&mut file, &mut outfile)?;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Clear the temporary folder after extraction
|
|
51
|
-
if zip_path.exists() {
|
|
52
|
-
std::fs::remove_file(zip_path)?;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
Ok(())
|
|
56
|
-
}
|
|
1
|
+
use std::error::Error;
|
|
2
|
+
use std::fs::File;
|
|
3
|
+
use std::io::BufReader;
|
|
4
|
+
use std::io::{Cursor, copy};
|
|
5
|
+
use std::path::Path;
|
|
6
|
+
use zip::ZipArchive;
|
|
7
|
+
|
|
8
|
+
pub async fn download_file(url: &str, destination: &Path) -> Result<(), Box<dyn Error>> {
|
|
9
|
+
let response = reqwest::get(url).await?;
|
|
10
|
+
|
|
11
|
+
if !response.status().is_success() {
|
|
12
|
+
return Err(format!("Failed to download file: HTTP {}", response.status()).into());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if let Some(parent) = destination.parent() {
|
|
16
|
+
std::fs::create_dir_all(parent)?;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let bytes = response.bytes().await?;
|
|
20
|
+
let mut content = Cursor::new(bytes);
|
|
21
|
+
let mut file = File::create(destination)?;
|
|
22
|
+
copy(&mut content, &mut file)?;
|
|
23
|
+
|
|
24
|
+
Ok(())
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
pub async fn extract_archive(
|
|
28
|
+
zip_path: &Path,
|
|
29
|
+
destination: &Path,
|
|
30
|
+
) -> Result<(), Box<dyn std::error::Error>> {
|
|
31
|
+
let file = File::open(zip_path)?;
|
|
32
|
+
let mut archive = ZipArchive::new(BufReader::new(file))?;
|
|
33
|
+
|
|
34
|
+
for i in 0..archive.len() {
|
|
35
|
+
let mut file = archive.by_index(i)?;
|
|
36
|
+
let outpath = destination.join(file.mangled_name());
|
|
37
|
+
|
|
38
|
+
if file.name().ends_with('/') {
|
|
39
|
+
std::fs::create_dir_all(&outpath)?;
|
|
40
|
+
} else {
|
|
41
|
+
if let Some(p) = outpath.parent() {
|
|
42
|
+
std::fs::create_dir_all(p)?;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let mut outfile = File::create(&outpath)?;
|
|
46
|
+
std::io::copy(&mut file, &mut outfile)?;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Clear the temporary folder after extraction
|
|
51
|
+
if zip_path.exists() {
|
|
52
|
+
std::fs::remove_file(zip_path)?;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
Ok(())
|
|
56
|
+
}
|