@devaloop/devalang 0.0.1-beta.1 → 0.0.1-beta.2
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 +9 -10
- package/Cargo.toml +5 -4
- package/README.md +7 -5
- package/docs/CHANGELOG.md +42 -0
- package/docs/ROADMAP.md +5 -1
- package/docs/TODO.md +3 -14
- package/examples/bus.deva +10 -0
- package/examples/effect.deva +2 -0
- package/examples/filter.deva +11 -0
- package/examples/lfo.deva +9 -0
- package/examples/synth.deva +11 -1
- package/examples/synth_types.deva +17 -0
- package/out-tsc/core/functions/index.d.ts +5 -0
- package/out-tsc/core/functions/index.js +11 -0
- package/out-tsc/pkg/devalang_core.d.ts +2 -0
- package/out-tsc/pkg/devalang_core.js +17 -2
- package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +8 -7
- package/package.json +1 -1
- package/project-version.json +3 -3
- package/rust/cli/bank/api.rs +122 -122
- package/rust/cli/bank/commands.rs +33 -2
- package/rust/cli/bank/mod.rs +29 -29
- package/rust/cli/build/commands.rs +53 -3
- package/rust/cli/build/mod.rs +2 -2
- package/rust/cli/build/process.rs +26 -7
- package/rust/cli/check/mod.rs +2 -2
- package/rust/cli/discover/commands.rs +253 -253
- package/rust/cli/discover/config.rs +111 -111
- package/rust/cli/discover/fs.rs +19 -19
- package/rust/cli/discover/install.rs +103 -103
- package/rust/cli/discover/metadata.rs +48 -48
- package/rust/cli/discover/mod.rs +5 -5
- package/rust/cli/install/addon.rs +118 -118
- package/rust/cli/install/bank.rs +22 -3
- package/rust/cli/install/commands.rs +35 -35
- package/rust/cli/install/mod.rs +4 -4
- package/rust/cli/install/plugin.rs +80 -61
- package/rust/cli/login/commands.rs +124 -124
- package/rust/cli/mod.rs +12 -12
- package/rust/cli/parser.rs +46 -1
- package/rust/cli/play/commands.rs +71 -20
- package/rust/cli/play/mod.rs +5 -5
- package/rust/cli/play/process.rs +14 -5
- package/rust/cli/play/realtime.rs +91 -91
- package/rust/cli/telemetry/commands.rs +22 -22
- package/rust/cli/telemetry/event_creator.rs +80 -80
- package/rust/cli/telemetry/mod.rs +3 -3
- package/rust/cli/telemetry/send.rs +51 -51
- package/rust/cli/template/commands.rs +69 -69
- package/rust/config/driver.rs +112 -103
- package/rust/config/mod.rs +3 -3
- package/rust/config/ops.rs +26 -26
- package/rust/config/settings.rs +101 -101
- package/rust/core/audio/engine/driver.rs +220 -0
- package/rust/core/audio/engine/export.rs +169 -0
- package/rust/core/audio/engine/helpers.rs +178 -170
- package/rust/core/audio/engine/mod.rs +51 -2
- package/rust/core/audio/engine/notes/dsp.rs +85 -0
- package/rust/core/audio/engine/notes/mod.rs +44 -0
- package/rust/core/audio/engine/notes/params.rs +294 -0
- package/rust/core/audio/engine/sample/insert.rs +199 -0
- package/rust/core/audio/engine/sample/mod.rs +40 -0
- package/rust/core/audio/engine/sample/padding.rs +170 -0
- package/rust/core/audio/evaluator/condition.rs +61 -0
- package/rust/core/audio/evaluator/mod.rs +9 -0
- package/rust/core/audio/{evaluator.rs → evaluator/numeric.rs} +1 -159
- package/rust/core/audio/evaluator/rhs.rs +16 -0
- package/rust/core/audio/evaluator/string_expr.rs +94 -0
- package/rust/core/audio/interpreter/driver.rs +55 -23
- package/rust/core/audio/interpreter/mod.rs +1 -13
- package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +175 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +384 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +2 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +316 -0
- package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -0
- package/rust/core/audio/interpreter/{automate.rs → statements/automate.rs} +16 -18
- package/rust/core/audio/interpreter/{call.rs → statements/call.rs} +5 -4
- package/rust/core/audio/interpreter/{condition.rs → statements/condition.rs} +2 -1
- package/rust/core/audio/interpreter/{function.rs → statements/function.rs} +2 -4
- package/rust/core/audio/interpreter/{let_.rs → statements/let_.rs} +2 -4
- package/rust/core/audio/interpreter/{load.rs → statements/load.rs} +2 -4
- package/rust/core/audio/interpreter/{loop_.rs → statements/loop_.rs} +2 -1
- package/rust/core/audio/interpreter/statements/mod.rs +12 -0
- package/rust/core/audio/interpreter/{sleep.rs → statements/sleep.rs} +28 -28
- package/rust/core/audio/interpreter/{spawn.rs → statements/spawn.rs} +3 -2
- package/rust/core/audio/interpreter/{tempo.rs → statements/tempo.rs} +40 -40
- package/rust/core/audio/interpreter/{trigger.rs → statements/trigger.rs} +1 -1
- package/rust/core/audio/loader/trigger.rs +2 -1
- package/rust/core/audio/mod.rs +6 -7
- package/rust/core/audio/player.rs +70 -70
- package/rust/core/audio/special/easing.rs +189 -189
- package/rust/core/audio/special/env.rs +45 -45
- package/rust/core/audio/special/math.rs +134 -134
- package/rust/core/audio/special/mod.rs +9 -9
- package/rust/core/audio/special/modulator.rs +143 -143
- package/rust/core/builder/mod.rs +45 -2
- package/rust/core/debugger/lexer.rs +27 -27
- package/rust/core/debugger/{module.rs → logs.rs} +3 -6
- package/rust/core/debugger/mod.rs +30 -30
- package/rust/core/debugger/preprocessor.rs +27 -27
- package/rust/core/debugger/store.rs +2 -4
- package/rust/core/error/mod.rs +269 -269
- package/rust/core/lexer/driver.rs +59 -61
- 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 +0 -1
- package/rust/core/parser/driver/block.rs +111 -0
- package/rust/core/parser/driver/cursor.rs +82 -0
- package/rust/core/parser/driver/driver_impl.rs +139 -0
- package/rust/core/parser/driver/mod.rs +6 -0
- package/rust/core/parser/driver/parse_array.rs +120 -0
- package/rust/core/parser/driver/parse_map.rs +223 -0
- package/rust/core/parser/driver/parser.rs +160 -0
- package/rust/core/parser/handler/arrow_call.rs +28 -4
- package/rust/core/parser/handler/at.rs +279 -279
- package/rust/core/parser/handler/bank.rs +104 -104
- package/rust/core/parser/handler/condition.rs +83 -83
- package/rust/core/parser/handler/dot.rs +148 -148
- package/rust/core/parser/handler/identifier/automate.rs +254 -254
- package/rust/core/parser/handler/identifier/call.rs +91 -91
- package/rust/core/parser/handler/identifier/emit.rs +70 -70
- package/rust/core/parser/handler/identifier/function.rs +113 -113
- package/rust/core/parser/handler/identifier/group.rs +89 -89
- package/rust/core/parser/handler/identifier/let_.rs +173 -173
- package/rust/core/parser/handler/identifier/mod.rs +55 -55
- package/rust/core/parser/handler/identifier/on.rs +107 -107
- package/rust/core/parser/handler/identifier/print.rs +49 -49
- package/rust/core/parser/handler/identifier/sleep.rs +96 -43
- package/rust/core/parser/handler/identifier/spawn.rs +91 -91
- package/rust/core/parser/handler/identifier/synth.rs +135 -135
- package/rust/core/parser/handler/loop_.rs +194 -194
- package/rust/core/parser/handler/mod.rs +9 -9
- package/rust/core/parser/handler/pattern.rs +1 -1
- package/rust/core/parser/handler/tempo.rs +105 -57
- package/rust/core/parser/statement.rs +10 -11
- package/rust/core/plugin/loader.rs +1 -1
- package/rust/core/plugin/mod.rs +2 -2
- package/rust/core/plugin/runner/mod.rs +11 -0
- package/rust/core/plugin/{runner.rs → runner/non_wasm.rs} +297 -347
- package/rust/core/plugin/runner/wasm32.rs +43 -0
- package/rust/core/preprocessor/loader/inject.rs +278 -0
- package/rust/core/preprocessor/loader/loader_helpers.rs +110 -0
- package/rust/core/preprocessor/loader/mod.rs +235 -0
- package/rust/core/preprocessor/module.rs +2 -7
- package/rust/core/preprocessor/{processor.rs → processor/handlers.rs} +6 -13
- package/rust/core/preprocessor/processor/mod.rs +1 -0
- 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 +2 -2
- package/rust/core/preprocessor/resolver/group.rs +46 -18
- 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 +83 -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 +2 -6
- package/rust/core/store/mod.rs +1 -5
- package/rust/lib.rs +18 -3
- package/rust/main.rs +27 -3
- package/rust/types/Cargo.toml +1 -1
- package/rust/types/src/addons.rs +55 -55
- package/rust/types/src/config.rs +84 -74
- package/rust/types/src/lib.rs +15 -12
- package/rust/types/src/plugin.rs +20 -0
- package/rust/types/src/store.rs +139 -0
- package/rust/types/src/telemetry.rs +85 -85
- package/rust/utils/Cargo.toml +2 -2
- package/rust/utils/src/file.rs +94 -94
- package/rust/utils/src/first_usage.rs +97 -97
- package/rust/utils/src/lib.rs +9 -9
- package/rust/utils/src/logger.rs +200 -200
- package/rust/utils/src/path.rs +129 -88
- package/rust/utils/src/signature.rs +41 -41
- package/rust/utils/src/spinner.rs +20 -20
- package/rust/utils/src/version.rs +27 -27
- package/rust/utils/src/watcher.rs +46 -46
- package/rust/web/api.rs +5 -5
- package/rust/web/cdn.rs +34 -34
- package/rust/web/mod.rs +3 -3
- package/tests/integration.rs +21 -21
- package/typescript/core/functions/index.ts +11 -0
- package/typescript/pkg/devalang_core.ts +20 -4
- package/rust/core/audio/engine/sample.rs +0 -366
- package/rust/core/audio/engine/synth.rs +0 -325
- package/rust/core/audio/interpreter/arrow_call.rs +0 -311
- package/rust/core/audio/renderer.rs +0 -54
- package/rust/core/parser/driver.rs +0 -584
- package/rust/core/preprocessor/loader.rs +0 -637
- package/rust/core/store/export.rs +0 -28
- package/rust/core/store/function.rs +0 -40
- package/rust/core/store/import.rs +0 -28
- package/rust/core/store/variable.rs +0 -51
- package/rust/core/utils/mod.rs +0 -1
- package/rust/core/utils/path.rs +0 -37
|
@@ -1,253 +1,253 @@
|
|
|
1
|
-
use crate::cli::discover::config::add_addons_to_config;
|
|
2
|
-
use crate::cli::discover::install::install_selected_addons;
|
|
3
|
-
use devalang_types::DiscoveredAddon;
|
|
4
|
-
use devalang_utils::{
|
|
5
|
-
logger::{LogLevel, Logger},
|
|
6
|
-
path as path_utils,
|
|
7
|
-
spinner::start_spinner,
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
pub async fn handle_discover_command() -> Result<(), String> {
|
|
11
|
-
let deva_dir = path_utils::ensure_deva_dir()?;
|
|
12
|
-
|
|
13
|
-
// Search for addons (banks, plugins, presets, templates) in the .deva directory
|
|
14
|
-
let valid_addons_extensions = ["devabank", "devaplugin", "devapreset", "devatemplate"];
|
|
15
|
-
|
|
16
|
-
let mut addons_found = Vec::new();
|
|
17
|
-
|
|
18
|
-
// Recursively walk the .deva directory and collect addon files matching
|
|
19
|
-
// the known addon extensions. This allows discovery in nested folders.
|
|
20
|
-
fn walk_dir_collect(base: &std::path::Path, exts: &[&str], out: &mut Vec<DiscoveredAddon>) {
|
|
21
|
-
if let Ok(entries) = std::fs::read_dir(base) {
|
|
22
|
-
for entry in entries.filter_map(|e| e.ok()) {
|
|
23
|
-
let p = entry.path();
|
|
24
|
-
if p.is_dir() {
|
|
25
|
-
walk_dir_collect(&p, exts, out);
|
|
26
|
-
} else if p.is_file() {
|
|
27
|
-
if let Some(ext) = p.extension().and_then(|s| s.to_str()) {
|
|
28
|
-
if exts.contains(&ext) {
|
|
29
|
-
let name = p
|
|
30
|
-
.file_stem()
|
|
31
|
-
.map(|s| s.to_string_lossy().to_string())
|
|
32
|
-
.unwrap_or_default();
|
|
33
|
-
let addon_type = match ext {
|
|
34
|
-
"devabank" => "bank",
|
|
35
|
-
"devaplugin" => "plugin",
|
|
36
|
-
"devapreset" => "preset",
|
|
37
|
-
"devatemplate" => "template",
|
|
38
|
-
_ => "unknown",
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
out.push(DiscoveredAddon {
|
|
42
|
-
path: p.clone(),
|
|
43
|
-
name,
|
|
44
|
-
extension: ext.to_string(),
|
|
45
|
-
addon_type: addon_type.to_string(),
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
walk_dir_collect(&deva_dir, &valid_addons_extensions, &mut addons_found);
|
|
55
|
-
|
|
56
|
-
let logger = Logger::new();
|
|
57
|
-
|
|
58
|
-
let banks_found = addons_found
|
|
59
|
-
.iter()
|
|
60
|
-
.filter(|addon| addon.addon_type == "bank")
|
|
61
|
-
.cloned()
|
|
62
|
-
.collect::<Vec<_>>();
|
|
63
|
-
|
|
64
|
-
let plugins_found = addons_found
|
|
65
|
-
.iter()
|
|
66
|
-
.filter(|addon| addon.addon_type == "plugin")
|
|
67
|
-
.cloned()
|
|
68
|
-
.collect::<Vec<_>>();
|
|
69
|
-
|
|
70
|
-
let presets_found = addons_found
|
|
71
|
-
.iter()
|
|
72
|
-
.filter(|addon| addon.addon_type == "preset")
|
|
73
|
-
.cloned()
|
|
74
|
-
.collect::<Vec<_>>();
|
|
75
|
-
|
|
76
|
-
let templates_found = addons_found
|
|
77
|
-
.iter()
|
|
78
|
-
.filter(|addon| addon.addon_type == "template")
|
|
79
|
-
.cloned()
|
|
80
|
-
.collect::<Vec<_>>();
|
|
81
|
-
|
|
82
|
-
let mut all_addons = Vec::with_capacity(
|
|
83
|
-
banks_found.len() + plugins_found.len() + presets_found.len() + templates_found.len(),
|
|
84
|
-
);
|
|
85
|
-
all_addons.extend(banks_found.iter().cloned());
|
|
86
|
-
all_addons.extend(plugins_found.iter().cloned());
|
|
87
|
-
all_addons.extend(presets_found.iter().cloned());
|
|
88
|
-
all_addons.extend(templates_found.iter().cloned());
|
|
89
|
-
|
|
90
|
-
println!();
|
|
91
|
-
|
|
92
|
-
if all_addons.is_empty() {
|
|
93
|
-
logger.log_message(LogLevel::Error, "No addons found in the '.deva' folder");
|
|
94
|
-
return Ok(());
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if !banks_found.is_empty() {
|
|
98
|
-
let mut bank_traces: Vec<String> = Vec::new();
|
|
99
|
-
|
|
100
|
-
for addon in &banks_found {
|
|
101
|
-
bank_traces.push(addon.name.to_string());
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
let trace_refs: Vec<&str> = bank_traces.iter().map(|s| s.as_str()).collect();
|
|
105
|
-
|
|
106
|
-
logger.log_message_with_trace(
|
|
107
|
-
LogLevel::Info,
|
|
108
|
-
format!("Found {} compiled banks in workspace", banks_found.len()).as_str(),
|
|
109
|
-
trace_refs,
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if !plugins_found.is_empty() {
|
|
114
|
-
let mut plugin_traces: Vec<String> = Vec::new();
|
|
115
|
-
|
|
116
|
-
for addon in &plugins_found {
|
|
117
|
-
plugin_traces.push(addon.name.to_string());
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
let trace_refs: Vec<&str> = plugin_traces.iter().map(|s| s.as_str()).collect();
|
|
121
|
-
|
|
122
|
-
logger.log_message_with_trace(
|
|
123
|
-
LogLevel::Info,
|
|
124
|
-
format!(
|
|
125
|
-
"Found {} compiled plugins in workspace",
|
|
126
|
-
plugins_found.len()
|
|
127
|
-
)
|
|
128
|
-
.as_str(),
|
|
129
|
-
trace_refs,
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if !presets_found.is_empty() {
|
|
134
|
-
let mut preset_traces: Vec<String> = Vec::new();
|
|
135
|
-
|
|
136
|
-
for addon in &presets_found {
|
|
137
|
-
preset_traces.push(addon.name.to_string());
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
let trace_refs: Vec<&str> = preset_traces.iter().map(|s| s.as_str()).collect();
|
|
141
|
-
|
|
142
|
-
logger.log_message_with_trace(
|
|
143
|
-
LogLevel::Info,
|
|
144
|
-
format!(
|
|
145
|
-
"Found {} compiled presets in workspace",
|
|
146
|
-
presets_found.len()
|
|
147
|
-
)
|
|
148
|
-
.as_str(),
|
|
149
|
-
trace_refs,
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if !templates_found.is_empty() {
|
|
154
|
-
let mut template_traces: Vec<String> = Vec::new();
|
|
155
|
-
|
|
156
|
-
for addon in &templates_found {
|
|
157
|
-
template_traces.push(addon.name.to_string());
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let trace_refs: Vec<&str> = template_traces.iter().map(|s| s.as_str()).collect();
|
|
161
|
-
|
|
162
|
-
logger.log_message_with_trace(
|
|
163
|
-
LogLevel::Info,
|
|
164
|
-
format!(
|
|
165
|
-
"Found {} compiled templates in workspace",
|
|
166
|
-
templates_found.len()
|
|
167
|
-
)
|
|
168
|
-
.as_str(),
|
|
169
|
-
trace_refs,
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
println!();
|
|
174
|
-
|
|
175
|
-
// Build user-friendly, unique labels tied to each addon by including the path
|
|
176
|
-
let choice_labels: Vec<String> = all_addons
|
|
177
|
-
.iter()
|
|
178
|
-
.map(|addon| {
|
|
179
|
-
format!(
|
|
180
|
-
"{}: {} ({})",
|
|
181
|
-
addon.addon_type,
|
|
182
|
-
addon.name,
|
|
183
|
-
addon.path.display()
|
|
184
|
-
)
|
|
185
|
-
})
|
|
186
|
-
.collect();
|
|
187
|
-
|
|
188
|
-
let selected_addons = match inquire::MultiSelect::new(
|
|
189
|
-
"Select addons to install:",
|
|
190
|
-
choice_labels.clone(),
|
|
191
|
-
)
|
|
192
|
-
.prompt()
|
|
193
|
-
{
|
|
194
|
-
Ok(selected_addons) => selected_addons,
|
|
195
|
-
Err(err) => {
|
|
196
|
-
logger.log_message(
|
|
197
|
-
LogLevel::Error,
|
|
198
|
-
format!("Error selecting addons: {}", err).as_str(),
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
return Err(format!("Error selecting addons: {}", err));
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
let spinner = start_spinner("Installing addons...");
|
|
206
|
-
|
|
207
|
-
let addons_to_install = selected_addons
|
|
208
|
-
.iter()
|
|
209
|
-
.filter_map(|label| {
|
|
210
|
-
all_addons.iter().find(|addon| {
|
|
211
|
-
let candidate = format!(
|
|
212
|
-
"{}: {} ({})",
|
|
213
|
-
addon.addon_type,
|
|
214
|
-
addon.name,
|
|
215
|
-
addon.path.display()
|
|
216
|
-
);
|
|
217
|
-
&candidate == label
|
|
218
|
-
})
|
|
219
|
-
})
|
|
220
|
-
.cloned()
|
|
221
|
-
.collect::<Vec<_>>();
|
|
222
|
-
|
|
223
|
-
let install_selected_addons_result = install_selected_addons(addons_to_install).await;
|
|
224
|
-
match install_selected_addons_result {
|
|
225
|
-
Ok(addons_enriched) => {
|
|
226
|
-
if let Err(e) = add_addons_to_config(addons_enriched).await {
|
|
227
|
-
spinner.finish_and_clear();
|
|
228
|
-
logger.log_message(
|
|
229
|
-
LogLevel::Error,
|
|
230
|
-
format!("Failed to add addons to config: {}", e).as_str(),
|
|
231
|
-
);
|
|
232
|
-
return Err(format!("Failed to add addons to config: {}", e));
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
spinner.finish_and_clear();
|
|
236
|
-
println!();
|
|
237
|
-
logger.log_message(
|
|
238
|
-
LogLevel::Success,
|
|
239
|
-
"Successfully installed addons !".to_string().as_str(),
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
Err(e) => {
|
|
243
|
-
spinner.finish_and_clear();
|
|
244
|
-
logger.log_message(
|
|
245
|
-
LogLevel::Error,
|
|
246
|
-
format!("Failed to install addons: {}", e).as_str(),
|
|
247
|
-
);
|
|
248
|
-
return Err(format!("Failed to install addons: {}", e));
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
Ok(())
|
|
253
|
-
}
|
|
1
|
+
use crate::cli::discover::config::add_addons_to_config;
|
|
2
|
+
use crate::cli::discover::install::install_selected_addons;
|
|
3
|
+
use devalang_types::DiscoveredAddon;
|
|
4
|
+
use devalang_utils::{
|
|
5
|
+
logger::{LogLevel, Logger},
|
|
6
|
+
path as path_utils,
|
|
7
|
+
spinner::start_spinner,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
pub async fn handle_discover_command() -> Result<(), String> {
|
|
11
|
+
let deva_dir = path_utils::ensure_deva_dir()?;
|
|
12
|
+
|
|
13
|
+
// Search for addons (banks, plugins, presets, templates) in the .deva directory
|
|
14
|
+
let valid_addons_extensions = ["devabank", "devaplugin", "devapreset", "devatemplate"];
|
|
15
|
+
|
|
16
|
+
let mut addons_found = Vec::new();
|
|
17
|
+
|
|
18
|
+
// Recursively walk the .deva directory and collect addon files matching
|
|
19
|
+
// the known addon extensions. This allows discovery in nested folders.
|
|
20
|
+
fn walk_dir_collect(base: &std::path::Path, exts: &[&str], out: &mut Vec<DiscoveredAddon>) {
|
|
21
|
+
if let Ok(entries) = std::fs::read_dir(base) {
|
|
22
|
+
for entry in entries.filter_map(|e| e.ok()) {
|
|
23
|
+
let p = entry.path();
|
|
24
|
+
if p.is_dir() {
|
|
25
|
+
walk_dir_collect(&p, exts, out);
|
|
26
|
+
} else if p.is_file() {
|
|
27
|
+
if let Some(ext) = p.extension().and_then(|s| s.to_str()) {
|
|
28
|
+
if exts.contains(&ext) {
|
|
29
|
+
let name = p
|
|
30
|
+
.file_stem()
|
|
31
|
+
.map(|s| s.to_string_lossy().to_string())
|
|
32
|
+
.unwrap_or_default();
|
|
33
|
+
let addon_type = match ext {
|
|
34
|
+
"devabank" => "bank",
|
|
35
|
+
"devaplugin" => "plugin",
|
|
36
|
+
"devapreset" => "preset",
|
|
37
|
+
"devatemplate" => "template",
|
|
38
|
+
_ => "unknown",
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
out.push(DiscoveredAddon {
|
|
42
|
+
path: p.clone(),
|
|
43
|
+
name,
|
|
44
|
+
extension: ext.to_string(),
|
|
45
|
+
addon_type: addon_type.to_string(),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
walk_dir_collect(&deva_dir, &valid_addons_extensions, &mut addons_found);
|
|
55
|
+
|
|
56
|
+
let logger = Logger::new();
|
|
57
|
+
|
|
58
|
+
let banks_found = addons_found
|
|
59
|
+
.iter()
|
|
60
|
+
.filter(|addon| addon.addon_type == "bank")
|
|
61
|
+
.cloned()
|
|
62
|
+
.collect::<Vec<_>>();
|
|
63
|
+
|
|
64
|
+
let plugins_found = addons_found
|
|
65
|
+
.iter()
|
|
66
|
+
.filter(|addon| addon.addon_type == "plugin")
|
|
67
|
+
.cloned()
|
|
68
|
+
.collect::<Vec<_>>();
|
|
69
|
+
|
|
70
|
+
let presets_found = addons_found
|
|
71
|
+
.iter()
|
|
72
|
+
.filter(|addon| addon.addon_type == "preset")
|
|
73
|
+
.cloned()
|
|
74
|
+
.collect::<Vec<_>>();
|
|
75
|
+
|
|
76
|
+
let templates_found = addons_found
|
|
77
|
+
.iter()
|
|
78
|
+
.filter(|addon| addon.addon_type == "template")
|
|
79
|
+
.cloned()
|
|
80
|
+
.collect::<Vec<_>>();
|
|
81
|
+
|
|
82
|
+
let mut all_addons = Vec::with_capacity(
|
|
83
|
+
banks_found.len() + plugins_found.len() + presets_found.len() + templates_found.len(),
|
|
84
|
+
);
|
|
85
|
+
all_addons.extend(banks_found.iter().cloned());
|
|
86
|
+
all_addons.extend(plugins_found.iter().cloned());
|
|
87
|
+
all_addons.extend(presets_found.iter().cloned());
|
|
88
|
+
all_addons.extend(templates_found.iter().cloned());
|
|
89
|
+
|
|
90
|
+
println!();
|
|
91
|
+
|
|
92
|
+
if all_addons.is_empty() {
|
|
93
|
+
logger.log_message(LogLevel::Error, "No addons found in the '.deva' folder");
|
|
94
|
+
return Ok(());
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if !banks_found.is_empty() {
|
|
98
|
+
let mut bank_traces: Vec<String> = Vec::new();
|
|
99
|
+
|
|
100
|
+
for addon in &banks_found {
|
|
101
|
+
bank_traces.push(addon.name.to_string());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let trace_refs: Vec<&str> = bank_traces.iter().map(|s| s.as_str()).collect();
|
|
105
|
+
|
|
106
|
+
logger.log_message_with_trace(
|
|
107
|
+
LogLevel::Info,
|
|
108
|
+
format!("Found {} compiled banks in workspace", banks_found.len()).as_str(),
|
|
109
|
+
trace_refs,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if !plugins_found.is_empty() {
|
|
114
|
+
let mut plugin_traces: Vec<String> = Vec::new();
|
|
115
|
+
|
|
116
|
+
for addon in &plugins_found {
|
|
117
|
+
plugin_traces.push(addon.name.to_string());
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let trace_refs: Vec<&str> = plugin_traces.iter().map(|s| s.as_str()).collect();
|
|
121
|
+
|
|
122
|
+
logger.log_message_with_trace(
|
|
123
|
+
LogLevel::Info,
|
|
124
|
+
format!(
|
|
125
|
+
"Found {} compiled plugins in workspace",
|
|
126
|
+
plugins_found.len()
|
|
127
|
+
)
|
|
128
|
+
.as_str(),
|
|
129
|
+
trace_refs,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if !presets_found.is_empty() {
|
|
134
|
+
let mut preset_traces: Vec<String> = Vec::new();
|
|
135
|
+
|
|
136
|
+
for addon in &presets_found {
|
|
137
|
+
preset_traces.push(addon.name.to_string());
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
let trace_refs: Vec<&str> = preset_traces.iter().map(|s| s.as_str()).collect();
|
|
141
|
+
|
|
142
|
+
logger.log_message_with_trace(
|
|
143
|
+
LogLevel::Info,
|
|
144
|
+
format!(
|
|
145
|
+
"Found {} compiled presets in workspace",
|
|
146
|
+
presets_found.len()
|
|
147
|
+
)
|
|
148
|
+
.as_str(),
|
|
149
|
+
trace_refs,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if !templates_found.is_empty() {
|
|
154
|
+
let mut template_traces: Vec<String> = Vec::new();
|
|
155
|
+
|
|
156
|
+
for addon in &templates_found {
|
|
157
|
+
template_traces.push(addon.name.to_string());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let trace_refs: Vec<&str> = template_traces.iter().map(|s| s.as_str()).collect();
|
|
161
|
+
|
|
162
|
+
logger.log_message_with_trace(
|
|
163
|
+
LogLevel::Info,
|
|
164
|
+
format!(
|
|
165
|
+
"Found {} compiled templates in workspace",
|
|
166
|
+
templates_found.len()
|
|
167
|
+
)
|
|
168
|
+
.as_str(),
|
|
169
|
+
trace_refs,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
println!();
|
|
174
|
+
|
|
175
|
+
// Build user-friendly, unique labels tied to each addon by including the path
|
|
176
|
+
let choice_labels: Vec<String> = all_addons
|
|
177
|
+
.iter()
|
|
178
|
+
.map(|addon| {
|
|
179
|
+
format!(
|
|
180
|
+
"{}: {} ({})",
|
|
181
|
+
addon.addon_type,
|
|
182
|
+
addon.name,
|
|
183
|
+
addon.path.display()
|
|
184
|
+
)
|
|
185
|
+
})
|
|
186
|
+
.collect();
|
|
187
|
+
|
|
188
|
+
let selected_addons = match inquire::MultiSelect::new(
|
|
189
|
+
"Select addons to install:",
|
|
190
|
+
choice_labels.clone(),
|
|
191
|
+
)
|
|
192
|
+
.prompt()
|
|
193
|
+
{
|
|
194
|
+
Ok(selected_addons) => selected_addons,
|
|
195
|
+
Err(err) => {
|
|
196
|
+
logger.log_message(
|
|
197
|
+
LogLevel::Error,
|
|
198
|
+
format!("Error selecting addons: {}", err).as_str(),
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
return Err(format!("Error selecting addons: {}", err));
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
let spinner = start_spinner("Installing addons...");
|
|
206
|
+
|
|
207
|
+
let addons_to_install = selected_addons
|
|
208
|
+
.iter()
|
|
209
|
+
.filter_map(|label| {
|
|
210
|
+
all_addons.iter().find(|addon| {
|
|
211
|
+
let candidate = format!(
|
|
212
|
+
"{}: {} ({})",
|
|
213
|
+
addon.addon_type,
|
|
214
|
+
addon.name,
|
|
215
|
+
addon.path.display()
|
|
216
|
+
);
|
|
217
|
+
&candidate == label
|
|
218
|
+
})
|
|
219
|
+
})
|
|
220
|
+
.cloned()
|
|
221
|
+
.collect::<Vec<_>>();
|
|
222
|
+
|
|
223
|
+
let install_selected_addons_result = install_selected_addons(addons_to_install).await;
|
|
224
|
+
match install_selected_addons_result {
|
|
225
|
+
Ok(addons_enriched) => {
|
|
226
|
+
if let Err(e) = add_addons_to_config(addons_enriched).await {
|
|
227
|
+
spinner.finish_and_clear();
|
|
228
|
+
logger.log_message(
|
|
229
|
+
LogLevel::Error,
|
|
230
|
+
format!("Failed to add addons to config: {}", e).as_str(),
|
|
231
|
+
);
|
|
232
|
+
return Err(format!("Failed to add addons to config: {}", e));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
spinner.finish_and_clear();
|
|
236
|
+
println!();
|
|
237
|
+
logger.log_message(
|
|
238
|
+
LogLevel::Success,
|
|
239
|
+
"Successfully installed addons !".to_string().as_str(),
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
Err(e) => {
|
|
243
|
+
spinner.finish_and_clear();
|
|
244
|
+
logger.log_message(
|
|
245
|
+
LogLevel::Error,
|
|
246
|
+
format!("Failed to install addons: {}", e).as_str(),
|
|
247
|
+
);
|
|
248
|
+
return Err(format!("Failed to install addons: {}", e));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
Ok(())
|
|
253
|
+
}
|