@devaloop/devalang 0.0.1-beta.2 → 0.0.1-beta.3
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/Cargo.toml +84 -81
- package/README.md +3 -2
- package/docs/CHANGELOG.md +41 -0
- package/docs/ROADMAP.md +3 -3
- package/examples/chain.deva +19 -0
- package/examples/plugin.deva +10 -10
- package/examples/routing.deva +23 -0
- package/out-tsc/bin/project-version.json +6 -0
- package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +8 -8
- package/out-tsc/scripts/version/copy-to-binary.d.ts +1 -0
- package/out-tsc/scripts/version/copy-to-binary.js +79 -0
- package/package.json +23 -10
- package/project-version.json +3 -3
- package/rust/bindings/Cargo.toml +9 -0
- package/rust/bindings/src/lib.rs +86 -0
- package/rust/cli/addon/commands.rs +35 -0
- package/rust/cli/addon/download.rs +234 -0
- package/rust/cli/addon/install.rs +33 -0
- package/rust/cli/addon/list.rs +224 -0
- package/rust/cli/addon/metadata.rs +124 -0
- package/rust/cli/addon/mod.rs +8 -0
- package/rust/cli/addon/remove.rs +271 -0
- package/rust/cli/addon/update.rs +305 -0
- package/rust/cli/{install/addon.rs → addon/utils.rs} +109 -118
- package/rust/cli/build/commands.rs +153 -153
- package/rust/cli/build/process.rs +165 -165
- package/rust/cli/check/mod.rs +208 -208
- package/rust/cli/discover/commands.rs +275 -253
- package/rust/cli/discover/config.rs +109 -111
- package/rust/cli/discover/fs.rs +19 -19
- package/rust/cli/discover/install.rs +214 -103
- package/rust/cli/discover/metadata.rs +48 -48
- package/rust/cli/discover/mod.rs +5 -5
- package/rust/cli/me/commands.rs +52 -0
- package/rust/cli/me/mod.rs +1 -0
- package/rust/cli/mod.rs +12 -12
- package/rust/cli/parser.rs +30 -69
- package/rust/cli/play/commands.rs +375 -375
- package/rust/cli/play/process.rs +159 -159
- package/rust/core/audio/engine/driver.rs +19 -2
- package/rust/core/audio/engine/export.rs +169 -169
- package/rust/core/audio/engine/mod.rs +56 -56
- package/rust/core/audio/engine/notes/dsp.rs +88 -85
- package/rust/core/audio/engine/notes/mod.rs +53 -44
- package/rust/core/audio/engine/notes/params.rs +294 -294
- package/rust/core/audio/engine/sample/insert.rs +148 -47
- package/rust/core/audio/engine/sample/mod.rs +40 -40
- package/rust/core/audio/engine/sample/padding.rs +170 -170
- package/rust/core/audio/evaluator/condition.rs +61 -61
- package/rust/core/audio/evaluator/numeric.rs +152 -152
- package/rust/core/audio/evaluator/rhs.rs +16 -16
- package/rust/core/audio/evaluator/string_expr.rs +94 -94
- package/rust/core/audio/interpreter/driver.rs +574 -574
- package/rust/core/audio/interpreter/mod.rs +2 -2
- package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +9 -5
- package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +398 -384
- package/rust/core/audio/interpreter/statements/arrow_call/methods/effects.rs +323 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +1 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +66 -11
- package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -3
- package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -192
- package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -24
- package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -116
- package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -97
- package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -100
- package/rust/core/audio/interpreter/statements/automate.rs +16 -16
- package/rust/core/audio/interpreter/statements/call.rs +31 -1
- package/rust/core/audio/interpreter/statements/condition.rs +72 -72
- package/rust/core/audio/interpreter/statements/function.rs +24 -24
- package/rust/core/audio/interpreter/statements/let_.rs +36 -36
- package/rust/core/audio/interpreter/statements/load.rs +17 -17
- package/rust/core/audio/interpreter/statements/loop_.rs +115 -115
- package/rust/core/audio/interpreter/statements/spawn.rs +51 -2
- package/rust/core/audio/interpreter/statements/trigger.rs +242 -239
- package/rust/core/audio/loader/trigger.rs +98 -98
- package/rust/core/audio/player.rs +70 -70
- package/rust/core/audio/special/mod.rs +9 -9
- package/rust/core/builder/mod.rs +129 -129
- package/rust/core/debugger/lexer.rs +27 -27
- package/rust/core/debugger/logs.rs +52 -52
- package/rust/core/debugger/preprocessor.rs +27 -27
- package/rust/core/debugger/store.rs +38 -38
- package/rust/core/lexer/driver.rs +59 -59
- package/rust/core/lexer/handler/arrow.rs +82 -82
- 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 -337
- package/rust/core/lexer/handler/identifier.rs +47 -47
- package/rust/core/lexer/handler/indent.rs +66 -66
- package/rust/core/lexer/handler/mod.rs +15 -15
- 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 -46
- 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 +3 -3
- package/rust/core/mod.rs +9 -9
- package/rust/core/parser/driver/block.rs +111 -111
- package/rust/core/parser/driver/cursor.rs +82 -82
- package/rust/core/parser/driver/driver_impl.rs +21 -1
- package/rust/core/parser/driver/mod.rs +6 -6
- package/rust/core/parser/driver/parse_array.rs +120 -120
- package/rust/core/parser/driver/parse_map.rs +247 -223
- package/rust/core/parser/driver/parser.rs +160 -160
- package/rust/core/parser/handler/arrow_call.rs +65 -14
- package/rust/core/parser/handler/identifier/synth.rs +171 -135
- package/rust/core/parser/handler/mod.rs +9 -9
- package/rust/core/parser/handler/pattern.rs +24 -1
- package/rust/core/plugin/loader.rs +137 -137
- package/rust/core/plugin/mod.rs +2 -2
- package/rust/core/plugin/runner/non_wasm.rs +481 -297
- package/rust/core/plugin/runner/wasm32.rs +1 -0
- package/rust/core/preprocessor/loader/inject.rs +313 -278
- package/rust/core/preprocessor/loader/loader_helpers.rs +110 -110
- package/rust/core/preprocessor/loader/mod.rs +235 -235
- package/rust/core/preprocessor/module.rs +55 -55
- package/rust/core/preprocessor/processor/handlers.rs +107 -107
- package/rust/core/preprocessor/resolver/bank.rs +49 -49
- package/rust/core/preprocessor/resolver/call.rs +124 -124
- package/rust/core/preprocessor/resolver/condition.rs +95 -95
- package/rust/core/preprocessor/resolver/driver.rs +324 -324
- package/rust/core/preprocessor/resolver/function.rs +69 -69
- package/rust/core/preprocessor/resolver/group.rs +122 -122
- package/rust/core/preprocessor/resolver/let_.rs +32 -32
- package/rust/core/preprocessor/resolver/loop_.rs +318 -318
- package/rust/core/preprocessor/resolver/mod.rs +16 -16
- package/rust/core/preprocessor/resolver/pattern.rs +95 -83
- package/rust/core/preprocessor/resolver/spawn.rs +99 -99
- package/rust/core/preprocessor/resolver/synth.rs +54 -54
- package/rust/core/preprocessor/resolver/tempo.rs +48 -48
- package/rust/core/preprocessor/resolver/trigger.rs +116 -116
- package/rust/core/preprocessor/resolver/value.rs +176 -176
- package/rust/core/store/global.rs +57 -57
- package/rust/lib.rs +323 -323
- package/rust/macros/Cargo.toml +14 -0
- package/rust/macros/src/lib.rs +52 -0
- package/rust/main.rs +311 -142
- package/rust/types/Cargo.toml +1 -1
- package/rust/types/src/addons.rs +3 -1
- package/rust/types/src/config.rs +1 -3
- package/rust/utils/Cargo.toml +5 -2
- package/rust/utils/src/file.rs +397 -14
- package/rust/utils/src/path.rs +31 -2
- package/rust/utils/src/version.rs +38 -7
- package/rust/web/auth.rs +5 -0
- package/rust/web/forge.rs +5 -0
- package/rust/web/mod.rs +5 -3
- package/typescript/scripts/version/copy-to-binary.ts +82 -0
- package/rust/cli/bank/api.rs +0 -122
- package/rust/cli/bank/commands.rs +0 -306
- package/rust/cli/bank/mod.rs +0 -29
- package/rust/cli/install/bank.rs +0 -72
- package/rust/cli/install/commands.rs +0 -35
- package/rust/cli/install/mod.rs +0 -4
- package/rust/cli/install/plugin.rs +0 -80
package/rust/types/src/config.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use serde::{Deserialize, Serialize};
|
|
1
|
+
use serde::{ Deserialize, Serialize };
|
|
2
2
|
use toml::Value as TomlValue;
|
|
3
3
|
|
|
4
4
|
use crate::TelemetrySettings;
|
|
@@ -57,13 +57,11 @@ pub struct ProjectConfigDefaults {
|
|
|
57
57
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
|
58
58
|
pub struct ProjectConfigBankEntry {
|
|
59
59
|
pub path: String,
|
|
60
|
-
pub version: Option<String>,
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
|
64
63
|
pub struct ProjectConfigPluginEntry {
|
|
65
64
|
pub path: String,
|
|
66
|
-
pub version: Option<String>,
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
package/rust/utils/Cargo.toml
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "devalang_utils"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.3"
|
|
4
4
|
description = "Utility functions and types for Devalang"
|
|
5
5
|
license = "MIT"
|
|
6
6
|
authors = ["Devaloop <contact@devaloop.com>"]
|
|
7
7
|
edition = "2024"
|
|
8
8
|
|
|
9
9
|
[dependencies]
|
|
10
|
-
devalang_types = { path = "../types", version = "0.0.
|
|
10
|
+
devalang_types = { path = "../types", version = "0.0.4" }
|
|
11
11
|
serde = { version = "1.0", features = ["derive"] }
|
|
12
12
|
serde_json = "1.0"
|
|
13
13
|
include_dir = "0.7"
|
|
14
14
|
notify = "6.1"
|
|
15
15
|
zip = "4.3.0"
|
|
16
|
+
flate2 = "1.0"
|
|
17
|
+
tar = "0.4"
|
|
18
|
+
tempfile = "3"
|
|
16
19
|
|
|
17
20
|
[dependencies.crossterm]
|
|
18
21
|
version = "0.27"
|
package/rust/utils/src/file.rs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
use include_dir::{Dir, DirEntry};
|
|
2
|
-
use std::{fs, path::Path};
|
|
1
|
+
use include_dir::{ Dir, DirEntry };
|
|
2
|
+
use std::{ fs, path::Path, io::{ Read, Seek, SeekFrom } };
|
|
3
3
|
|
|
4
4
|
pub fn copy_dir_recursive(dir: &Dir, target_root: &Path, base_path: &Path) {
|
|
5
5
|
for entry in dir.entries() {
|
|
@@ -56,39 +56,422 @@ pub fn format_file_size(bytes: u64) -> String {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
pub fn extract_zip_safely(archive_path: &Path, dest: &Path) -> Result<(), String> {
|
|
59
|
-
|
|
59
|
+
// Open file and peek magic bytes to determine archive format
|
|
60
|
+
let mut file = std::fs::File
|
|
61
|
+
::open(archive_path)
|
|
60
62
|
.map_err(|e| format!("Failed to open archive {}: {}", archive_path.display(), e))?;
|
|
61
|
-
let mut archive = zip::ZipArchive::new(file)
|
|
62
|
-
.map_err(|e| format!("Failed to read archive {}: {}", archive_path.display(), e))?;
|
|
63
63
|
|
|
64
|
+
let mut magic = [0u8; 4];
|
|
65
|
+
let n = file
|
|
66
|
+
.read(&mut magic)
|
|
67
|
+
.map_err(|e| format!("Failed to read archive header {}: {}", archive_path.display(), e))?;
|
|
68
|
+
file
|
|
69
|
+
.seek(SeekFrom::Start(0))
|
|
70
|
+
.map_err(|e| format!("Failed to seek archive {}: {}", archive_path.display(), e))?;
|
|
71
|
+
|
|
72
|
+
let is_gzip = n >= 2 && magic[0] == 0x1f && magic[1] == 0x8b;
|
|
73
|
+
let is_zip = n >= 2 && magic[0] == 0x50 && magic[1] == 0x4b;
|
|
74
|
+
|
|
75
|
+
if is_gzip {
|
|
76
|
+
// The gzip may contain a tar archive OR a gzipped zip (PK inside).
|
|
77
|
+
// Decompress to a temporary file, inspect its magic bytes, then choose extractor.
|
|
78
|
+
let mut gz = flate2::read::GzDecoder::new(file);
|
|
79
|
+
|
|
80
|
+
let mut named = tempfile::NamedTempFile
|
|
81
|
+
::new()
|
|
82
|
+
.map_err(|e| format!("Failed to create temp file: {}", e))?;
|
|
83
|
+
|
|
84
|
+
std::io
|
|
85
|
+
::copy(&mut gz, &mut named)
|
|
86
|
+
.map_err(|e| format!("Failed to decompress gzip to temp: {}", e))?;
|
|
87
|
+
named
|
|
88
|
+
.as_file_mut()
|
|
89
|
+
.seek(SeekFrom::Start(0))
|
|
90
|
+
.map_err(|e| format!("Failed to seek temp file: {}", e))?;
|
|
91
|
+
|
|
92
|
+
// Read magic of decompressed content
|
|
93
|
+
let mut head = [0u8; 4];
|
|
94
|
+
let n = named
|
|
95
|
+
.as_file_mut()
|
|
96
|
+
.read(&mut head)
|
|
97
|
+
.map_err(|e| format!("Failed to read temp archive header: {}", e))?;
|
|
98
|
+
named
|
|
99
|
+
.as_file_mut()
|
|
100
|
+
.seek(SeekFrom::Start(0))
|
|
101
|
+
.map_err(|e| format!("Failed to rewind temp file: {}", e))?;
|
|
102
|
+
|
|
103
|
+
let inner_is_zip = n >= 2 && head[0] == 0x50 && head[1] == 0x4b;
|
|
104
|
+
|
|
105
|
+
if inner_is_zip {
|
|
106
|
+
let tmp_file = named
|
|
107
|
+
.reopen()
|
|
108
|
+
.map_err(|e| format!("Failed to reopen temp file for zip: {}", e))?;
|
|
109
|
+
|
|
110
|
+
let mut archive = zip::ZipArchive
|
|
111
|
+
::new(tmp_file)
|
|
112
|
+
.map_err(|e| format!("Failed to read zip archive inside gzip: {}", e))?;
|
|
113
|
+
|
|
114
|
+
for i in 0..archive.len() {
|
|
115
|
+
let mut file = archive
|
|
116
|
+
.by_index(i)
|
|
117
|
+
.map_err(|e| format!("Failed to access zip archive entry {}: {}", i, e))?;
|
|
118
|
+
let enclosed = match file.enclosed_name() {
|
|
119
|
+
Some(p) => p.to_owned(),
|
|
120
|
+
None => {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
if
|
|
125
|
+
enclosed.is_absolute() ||
|
|
126
|
+
enclosed.components().any(|c| matches!(c, std::path::Component::ParentDir))
|
|
127
|
+
{
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
let outpath = dest.join(enclosed);
|
|
131
|
+
if file.name().ends_with('/') || file.is_dir() {
|
|
132
|
+
std::fs
|
|
133
|
+
::create_dir_all(&outpath)
|
|
134
|
+
.map_err(|e| format!("Failed to create dir {}: {}", outpath.display(), e))?;
|
|
135
|
+
} else {
|
|
136
|
+
if let Some(p) = outpath.parent() {
|
|
137
|
+
std::fs
|
|
138
|
+
::create_dir_all(p)
|
|
139
|
+
.map_err(|e|
|
|
140
|
+
format!("Failed to create parent {}: {}", p.display(), e)
|
|
141
|
+
)?;
|
|
142
|
+
}
|
|
143
|
+
let mut outfile = std::fs::File
|
|
144
|
+
::create(&outpath)
|
|
145
|
+
.map_err(|e|
|
|
146
|
+
format!("Failed to create file {}: {}", outpath.display(), e)
|
|
147
|
+
)?;
|
|
148
|
+
std::io
|
|
149
|
+
::copy(&mut file, &mut outfile)
|
|
150
|
+
.map_err(|e| format!("Failed to write file {}: {}", outpath.display(), e))?;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// NamedTempFile will be removed on drop
|
|
155
|
+
return Ok(());
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// otherwise treat as tar
|
|
159
|
+
let tmp_file = named
|
|
160
|
+
.reopen()
|
|
161
|
+
.map_err(|e| format!("Failed to reopen temp file for tar: {}", e))?;
|
|
162
|
+
let mut archive = tar::Archive::new(tmp_file);
|
|
163
|
+
|
|
164
|
+
for entry in archive
|
|
165
|
+
.entries()
|
|
166
|
+
.map_err(|e| format!("Failed to read tar entries (from gzip): {}", e))? {
|
|
167
|
+
let mut entry = entry.map_err(|e|
|
|
168
|
+
format!("Failed to read tar archive entry (from gzip): {}", e)
|
|
169
|
+
)?;
|
|
170
|
+
let path = match entry.path() {
|
|
171
|
+
Ok(p) => p.into_owned(),
|
|
172
|
+
Err(_) => {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
if
|
|
177
|
+
path.is_absolute() ||
|
|
178
|
+
path.components().any(|c| matches!(c, std::path::Component::ParentDir))
|
|
179
|
+
{
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
let outpath = dest.join(&path);
|
|
183
|
+
if let Some(parent) = outpath.parent() {
|
|
184
|
+
std::fs
|
|
185
|
+
::create_dir_all(parent)
|
|
186
|
+
.map_err(|e| format!("Failed to create parent {}: {}", parent.display(), e))?;
|
|
187
|
+
}
|
|
188
|
+
entry
|
|
189
|
+
.unpack(&outpath)
|
|
190
|
+
.map_err(|e|
|
|
191
|
+
format!("Failed to unpack tar entry to {}: {}", outpath.display(), e)
|
|
192
|
+
)?;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return Ok(());
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if is_zip {
|
|
199
|
+
// Re-open file for zip
|
|
200
|
+
let file = std::fs::File
|
|
201
|
+
::open(archive_path)
|
|
202
|
+
.map_err(|e|
|
|
203
|
+
format!("Failed to open archive for zip {}: {}", archive_path.display(), e)
|
|
204
|
+
)?;
|
|
205
|
+
|
|
206
|
+
let mut archive = zip::ZipArchive
|
|
207
|
+
::new(file)
|
|
208
|
+
.map_err(|e| format!("Failed to read zip archive {}: {}", archive_path.display(), e))?;
|
|
209
|
+
|
|
210
|
+
for i in 0..archive.len() {
|
|
211
|
+
let mut file = archive
|
|
212
|
+
.by_index(i)
|
|
213
|
+
.map_err(|e| format!("Failed to access zip archive entry {}: {}", i, e))?;
|
|
214
|
+
|
|
215
|
+
let enclosed = match file.enclosed_name() {
|
|
216
|
+
Some(p) => p.to_owned(),
|
|
217
|
+
None => {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
if
|
|
223
|
+
enclosed.is_absolute() ||
|
|
224
|
+
enclosed.components().any(|c| matches!(c, std::path::Component::ParentDir))
|
|
225
|
+
{
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let outpath = dest.join(enclosed);
|
|
230
|
+
if file.name().ends_with('/') || file.is_dir() {
|
|
231
|
+
std::fs
|
|
232
|
+
::create_dir_all(&outpath)
|
|
233
|
+
.map_err(|e| format!("Failed to create dir {}: {}", outpath.display(), e))?;
|
|
234
|
+
} else {
|
|
235
|
+
if let Some(p) = outpath.parent() {
|
|
236
|
+
std::fs
|
|
237
|
+
::create_dir_all(p)
|
|
238
|
+
.map_err(|e| format!("Failed to create parent {}: {}", p.display(), e))?;
|
|
239
|
+
}
|
|
240
|
+
let mut outfile = std::fs::File
|
|
241
|
+
::create(&outpath)
|
|
242
|
+
.map_err(|e| format!("Failed to create file {}: {}", outpath.display(), e))?;
|
|
243
|
+
std::io
|
|
244
|
+
::copy(&mut file, &mut outfile)
|
|
245
|
+
.map_err(|e| format!("Failed to write file {}: {}", outpath.display(), e))?;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return Ok(());
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Unknown magic: try tar.gz first, then zip as fallback
|
|
253
|
+
// Try tar.gz
|
|
254
|
+
let file = std::fs::File
|
|
255
|
+
::open(archive_path)
|
|
256
|
+
.map_err(|e| format!("Failed to open archive {}: {}", archive_path.display(), e))?;
|
|
257
|
+
if
|
|
258
|
+
let Ok(()) = (|| -> Result<(), String> {
|
|
259
|
+
let gz = flate2::read::GzDecoder::new(&file);
|
|
260
|
+
let mut archive = tar::Archive::new(gz);
|
|
261
|
+
for entry in archive
|
|
262
|
+
.entries()
|
|
263
|
+
.map_err(|e| format!("Failed to read tar entries: {}", e))? {
|
|
264
|
+
let mut entry = entry.map_err(|e|
|
|
265
|
+
format!("Failed to read tar archive entry: {}", e)
|
|
266
|
+
)?;
|
|
267
|
+
let path = match entry.path() {
|
|
268
|
+
Ok(p) => p.into_owned(),
|
|
269
|
+
Err(_) => {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
if
|
|
274
|
+
path.is_absolute() ||
|
|
275
|
+
path.components().any(|c| matches!(c, std::path::Component::ParentDir))
|
|
276
|
+
{
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
let outpath = dest.join(&path);
|
|
280
|
+
if let Some(parent) = outpath.parent() {
|
|
281
|
+
std::fs
|
|
282
|
+
::create_dir_all(parent)
|
|
283
|
+
.map_err(|e|
|
|
284
|
+
format!("Failed to create parent {}: {}", parent.display(), e)
|
|
285
|
+
)?;
|
|
286
|
+
}
|
|
287
|
+
entry
|
|
288
|
+
.unpack(&outpath)
|
|
289
|
+
.map_err(|e|
|
|
290
|
+
format!("Failed to unpack tar entry to {}: {}", outpath.display(), e)
|
|
291
|
+
)?;
|
|
292
|
+
}
|
|
293
|
+
Ok(())
|
|
294
|
+
})()
|
|
295
|
+
{
|
|
296
|
+
return Ok(());
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Fallback to zip
|
|
300
|
+
let file = std::fs::File
|
|
301
|
+
::open(archive_path)
|
|
302
|
+
.map_err(|e| format!("Failed to open archive for zip {}: {}", archive_path.display(), e))?;
|
|
303
|
+
let mut archive = zip::ZipArchive
|
|
304
|
+
::new(file)
|
|
305
|
+
.map_err(|e| format!("Failed to read zip archive {}: {}", archive_path.display(), e))?;
|
|
64
306
|
for i in 0..archive.len() {
|
|
65
307
|
let mut file = archive
|
|
66
308
|
.by_index(i)
|
|
67
|
-
.map_err(|e| format!("Failed to access archive entry {}: {}", i, e))?;
|
|
68
|
-
|
|
309
|
+
.map_err(|e| format!("Failed to access zip archive entry {}: {}", i, e))?;
|
|
69
310
|
let enclosed = match file.enclosed_name() {
|
|
70
|
-
Some(
|
|
311
|
+
Some(p) => p.to_owned(),
|
|
71
312
|
None => {
|
|
72
313
|
continue;
|
|
73
314
|
}
|
|
74
315
|
};
|
|
75
|
-
|
|
316
|
+
if
|
|
317
|
+
enclosed.is_absolute() ||
|
|
318
|
+
enclosed.components().any(|c| matches!(c, std::path::Component::ParentDir))
|
|
319
|
+
{
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
76
322
|
let outpath = dest.join(enclosed);
|
|
77
|
-
|
|
78
323
|
if file.name().ends_with('/') || file.is_dir() {
|
|
79
|
-
std::fs
|
|
324
|
+
std::fs
|
|
325
|
+
::create_dir_all(&outpath)
|
|
80
326
|
.map_err(|e| format!("Failed to create dir {}: {}", outpath.display(), e))?;
|
|
81
327
|
} else {
|
|
82
328
|
if let Some(p) = outpath.parent() {
|
|
83
|
-
std::fs
|
|
329
|
+
std::fs
|
|
330
|
+
::create_dir_all(p)
|
|
84
331
|
.map_err(|e| format!("Failed to create parent {}: {}", p.display(), e))?;
|
|
85
332
|
}
|
|
86
|
-
let mut outfile = std::fs::File
|
|
333
|
+
let mut outfile = std::fs::File
|
|
334
|
+
::create(&outpath)
|
|
87
335
|
.map_err(|e| format!("Failed to create file {}: {}", outpath.display(), e))?;
|
|
88
|
-
std::io
|
|
336
|
+
std::io
|
|
337
|
+
::copy(&mut file, &mut outfile)
|
|
89
338
|
.map_err(|e| format!("Failed to write file {}: {}", outpath.display(), e))?;
|
|
90
339
|
}
|
|
91
340
|
}
|
|
92
341
|
|
|
93
342
|
Ok(())
|
|
94
343
|
}
|
|
344
|
+
|
|
345
|
+
/// Detects addon type by inspecting archive contents without extracting fully.
|
|
346
|
+
/// Returns one of: "bank", "plugin", "preset", "template", or "unknown".
|
|
347
|
+
pub fn detect_addon_type_in_archive(archive_path: &Path) -> Result<String, String> {
|
|
348
|
+
use std::fs::File;
|
|
349
|
+
|
|
350
|
+
let mut file = File::open(archive_path)
|
|
351
|
+
.map_err(|e| format!("Failed to open archive {}: {}", archive_path.display(), e))?;
|
|
352
|
+
|
|
353
|
+
let mut magic = [0u8; 4];
|
|
354
|
+
let n = file
|
|
355
|
+
.read(&mut magic)
|
|
356
|
+
.map_err(|e| format!("Failed to read archive header {}: {}", archive_path.display(), e))?;
|
|
357
|
+
file
|
|
358
|
+
.seek(SeekFrom::Start(0))
|
|
359
|
+
.map_err(|e| format!("Failed to seek archive {}: {}", archive_path.display(), e))?;
|
|
360
|
+
|
|
361
|
+
let is_gzip = n >= 2 && magic[0] == 0x1f && magic[1] == 0x8b;
|
|
362
|
+
let is_zip = n >= 2 && magic[0] == 0x50 && magic[1] == 0x4b;
|
|
363
|
+
|
|
364
|
+
let check_name = |name: &str| {
|
|
365
|
+
let lname = name.to_ascii_lowercase();
|
|
366
|
+
if lname.ends_with("bank.toml") {
|
|
367
|
+
return Some("bank".to_string());
|
|
368
|
+
}
|
|
369
|
+
if lname.ends_with("plugin.toml") {
|
|
370
|
+
return Some("plugin".to_string());
|
|
371
|
+
}
|
|
372
|
+
if lname.ends_with("preset.toml") {
|
|
373
|
+
return Some("preset".to_string());
|
|
374
|
+
}
|
|
375
|
+
if lname.ends_with("template.toml") {
|
|
376
|
+
return Some("template".to_string());
|
|
377
|
+
}
|
|
378
|
+
None
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
if is_gzip {
|
|
382
|
+
// Decompress to temp and inspect inner archive
|
|
383
|
+
let mut gz = flate2::read::GzDecoder::new(file);
|
|
384
|
+
let mut named = tempfile::NamedTempFile::new()
|
|
385
|
+
.map_err(|e| format!("Failed to create temp file: {}", e))?;
|
|
386
|
+
std::io::copy(&mut gz, &mut named)
|
|
387
|
+
.map_err(|e| format!("Failed to decompress gzip to temp: {}", e))?;
|
|
388
|
+
named.as_file_mut().seek(SeekFrom::Start(0)).map_err(|e| format!("Failed to seek temp file: {}", e))?;
|
|
389
|
+
|
|
390
|
+
// Inspect inner magic
|
|
391
|
+
let mut head = [0u8; 4];
|
|
392
|
+
let m = named.as_file_mut().read(&mut head).map_err(|e| format!("Failed to read temp archive header: {}", e))?;
|
|
393
|
+
named.as_file_mut().seek(SeekFrom::Start(0)).map_err(|e| format!("Failed to rewind temp file: {}", e))?;
|
|
394
|
+
|
|
395
|
+
let inner_is_zip = m >= 2 && head[0] == 0x50 && head[1] == 0x4b;
|
|
396
|
+
if inner_is_zip {
|
|
397
|
+
let tmp_file = named.reopen().map_err(|e| format!("Failed to reopen temp file for zip: {}", e))?;
|
|
398
|
+
let mut archive = zip::ZipArchive::new(tmp_file).map_err(|e| format!("Failed to read zip archive inside gzip: {}", e))?;
|
|
399
|
+
for i in 0..archive.len() {
|
|
400
|
+
if let Ok(file) = archive.by_index(i) {
|
|
401
|
+
if let Some(enclosed) = file.enclosed_name() {
|
|
402
|
+
let s = enclosed.to_string_lossy().to_string();
|
|
403
|
+
if let Some(t) = check_name(&s) {
|
|
404
|
+
return Ok(t);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return Ok("unknown".to_string());
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// treat as tar
|
|
413
|
+
let tmp_file = named.reopen().map_err(|e| format!("Failed to reopen temp file for tar: {}", e))?;
|
|
414
|
+
let mut archive = tar::Archive::new(tmp_file);
|
|
415
|
+
if let Ok(entries) = archive.entries() {
|
|
416
|
+
for entry in entries.filter_map(|e| e.ok()) {
|
|
417
|
+
if let Ok(path) = entry.path() {
|
|
418
|
+
let s = path.to_string_lossy().to_string();
|
|
419
|
+
if let Some(t) = check_name(&s) {
|
|
420
|
+
return Ok(t);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return Ok("unknown".to_string());
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if is_zip {
|
|
429
|
+
let file = std::fs::File::open(archive_path).map_err(|e| format!("Failed to open archive for zip {}: {}", archive_path.display(), e))?;
|
|
430
|
+
let mut archive = zip::ZipArchive::new(file).map_err(|e| format!("Failed to read zip archive {}: {}", archive_path.display(), e))?;
|
|
431
|
+
for i in 0..archive.len() {
|
|
432
|
+
if let Ok(file) = archive.by_index(i) {
|
|
433
|
+
if let Some(enclosed) = file.enclosed_name() {
|
|
434
|
+
let s = enclosed.to_string_lossy().to_string();
|
|
435
|
+
if let Some(t) = check_name(&s) {
|
|
436
|
+
return Ok(t);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return Ok("unknown".to_string());
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// fallback: try tar.gz read attempt
|
|
445
|
+
if let Ok(f) = std::fs::File::open(archive_path) {
|
|
446
|
+
let gz = flate2::read::GzDecoder::new(f);
|
|
447
|
+
let mut archive = tar::Archive::new(gz);
|
|
448
|
+
if let Ok(entries) = archive.entries() {
|
|
449
|
+
for entry in entries.filter_map(|e| e.ok()) {
|
|
450
|
+
if let Ok(path) = entry.path() {
|
|
451
|
+
let s = path.to_string_lossy().to_string();
|
|
452
|
+
if let Some(t) = check_name(&s) {
|
|
453
|
+
return Ok(t);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
Ok("unknown".to_string())
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/// Removes the temporary folder located at `.deva/tmp` inside the project root if it exists.
|
|
464
|
+
/// Returns Ok(()) even if the folder did not exist. Returns Err(String) on filesystem errors.
|
|
465
|
+
pub fn clear_tmp_folder() -> Result<(), String> {
|
|
466
|
+
match crate::path::ensure_deva_dir() {
|
|
467
|
+
Ok(deva_dir) => {
|
|
468
|
+
let tmp_dir = deva_dir.join("tmp");
|
|
469
|
+
if tmp_dir.exists() {
|
|
470
|
+
std::fs::remove_dir_all(&tmp_dir)
|
|
471
|
+
.map_err(|e| format!("Failed to remove tmp dir '{}': {}", tmp_dir.display(), e))?;
|
|
472
|
+
}
|
|
473
|
+
Ok(())
|
|
474
|
+
}
|
|
475
|
+
Err(e) => Err(e),
|
|
476
|
+
}
|
|
477
|
+
}
|
package/rust/utils/src/path.rs
CHANGED
|
@@ -43,8 +43,37 @@ pub fn find_project_root() -> Option<PathBuf> {
|
|
|
43
43
|
|
|
44
44
|
/// Finds the package root using the `CARGO_MANIFEST_DIR` env var set by Cargo.
|
|
45
45
|
pub fn get_package_root() -> Option<PathBuf> {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
// Prefer Cargo-provided manifest dir when available (build-time / cargo run)
|
|
47
|
+
if let Ok(cargo_dir) = env::var("CARGO_MANIFEST_DIR") {
|
|
48
|
+
let p = PathBuf::from(cargo_dir);
|
|
49
|
+
if p.exists() {
|
|
50
|
+
return Some(p);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// At runtime (packaged binary) try to infer the package root from the
|
|
55
|
+
// binary location: walk upward from the running executable and look for
|
|
56
|
+
// common markers (package.json, project-version.json, Cargo.toml).
|
|
57
|
+
if let Ok(exe_path) = env::current_exe() {
|
|
58
|
+
if let Some(mut dir) = exe_path.parent().map(|p| p.to_path_buf()) {
|
|
59
|
+
loop {
|
|
60
|
+
if dir.join("package.json").exists()
|
|
61
|
+
|| dir.join("project-version.json").exists()
|
|
62
|
+
|| dir.join("Cargo.toml").exists()
|
|
63
|
+
{
|
|
64
|
+
return Some(dir);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if let Some(parent) = dir.parent() {
|
|
68
|
+
dir = parent.to_path_buf();
|
|
69
|
+
} else {
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
None
|
|
48
77
|
}
|
|
49
78
|
|
|
50
79
|
/// Gets the project root or returns a descriptive error if not found.
|
|
@@ -1,20 +1,51 @@
|
|
|
1
1
|
use crate::{ path::get_package_root, signature::get_signature };
|
|
2
2
|
|
|
3
|
+
/// Returns the CLI version with a runtime-first strategy:
|
|
4
|
+
/// 1. DEVALANG_CLI_VERSION env var
|
|
5
|
+
/// 2. project-version.json located next to the running binary
|
|
6
|
+
/// 3. package.json located in the package root
|
|
7
|
+
/// 4. compile-time env!("CARGO_PKG_VERSION") as a last resort
|
|
3
8
|
pub fn get_version() -> String {
|
|
9
|
+
// 1) env override
|
|
10
|
+
if let Ok(v) = std::env::var("DEVALANG_CLI_VERSION") {
|
|
11
|
+
if !v.trim().is_empty() {
|
|
12
|
+
return v;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 2) project-version.json next to binary
|
|
17
|
+
if let Ok(exe) = std::env::current_exe() {
|
|
18
|
+
if let Some(bin_dir) = exe.parent() {
|
|
19
|
+
let pv = bin_dir.join("project-version.json");
|
|
20
|
+
if pv.exists() {
|
|
21
|
+
if let Ok(contents) = std::fs::read_to_string(pv) {
|
|
22
|
+
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(&contents) {
|
|
23
|
+
if let Some(v) = parsed.get("version").and_then(|s| s.as_str()) {
|
|
24
|
+
return v.to_string();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 3) package.json via package root
|
|
4
33
|
if let Some(root) = get_package_root() {
|
|
5
|
-
let
|
|
6
|
-
if
|
|
7
|
-
if let Ok(
|
|
8
|
-
if let
|
|
9
|
-
if let Some(
|
|
10
|
-
return
|
|
34
|
+
let pkg = root.join("package.json");
|
|
35
|
+
if pkg.exists() {
|
|
36
|
+
if let Ok(contents) = std::fs::read_to_string(pkg) {
|
|
37
|
+
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(&contents) {
|
|
38
|
+
if let Some(v) = parsed.get("version").and_then(|s| s.as_str()) {
|
|
39
|
+
return v.to_string();
|
|
11
40
|
}
|
|
12
41
|
}
|
|
13
42
|
}
|
|
14
43
|
}
|
|
15
44
|
}
|
|
16
45
|
|
|
17
|
-
|
|
46
|
+
// 4) compile-time fallback
|
|
47
|
+
let compile_time = option_env!("CARGO_PKG_VERSION").unwrap_or("0.0.0");
|
|
48
|
+
compile_time.to_string()
|
|
18
49
|
}
|
|
19
50
|
|
|
20
51
|
pub fn get_version_with_signature() -> String {
|
package/rust/web/auth.rs
ADDED
package/rust/web/mod.rs
CHANGED