@devaloop/devalang 0.0.1-alpha.16-hotfix.3 → 0.0.1-alpha.18
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/config.toml +2 -0
- package/.devalang +10 -10
- package/.github/workflows/ci.yml +0 -1
- package/Cargo.toml +18 -2
- package/README.md +82 -34
- package/docs/CHANGELOG.md +91 -0
- package/docs/ROADMAP.md +7 -4
- package/docs/TODO.md +1 -1
- package/examples/index.deva +55 -35
- package/examples/pattern.deva +5 -5
- package/out-tsc/bin/index.d.ts +2 -0
- package/out-tsc/core/functions/index.d.ts +37 -0
- package/out-tsc/core/functions/index.js +76 -0
- package/out-tsc/core/index.d.ts +6 -0
- package/out-tsc/core/index.js +22 -0
- package/out-tsc/core/types/index.d.ts +4 -0
- package/out-tsc/core/types/index.js +20 -0
- package/out-tsc/core/types/plugin.d.ts +18 -0
- package/out-tsc/core/types/plugin.js +2 -0
- package/out-tsc/core/types/result.d.ts +27 -0
- package/out-tsc/core/types/result.js +2 -0
- package/out-tsc/core/types/statement.d.ts +106 -0
- package/out-tsc/core/types/statement.js +2 -0
- package/out-tsc/core/types/value.d.ts +43 -0
- package/out-tsc/core/types/value.js +2 -0
- package/out-tsc/index.d.ts +7 -0
- package/out-tsc/index.js +41 -2
- package/out-tsc/pkg/devalang_core.d.ts +7 -0
- package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +33 -0
- package/out-tsc/scripts/copy-wasm-dts.d.ts +1 -0
- package/out-tsc/scripts/copy-wasm-dts.js +73 -0
- package/out-tsc/scripts/postinstall.d.ts +1 -0
- package/out-tsc/scripts/postinstall.js +33 -23
- package/out-tsc/scripts/version/bump.d.ts +1 -0
- package/out-tsc/scripts/version/fetch.d.ts +1 -0
- package/out-tsc/scripts/version/index.d.ts +1 -0
- package/out-tsc/scripts/version/sync.d.ts +1 -0
- package/package.json +16 -4
- package/project-version.json +3 -3
- package/rust/cli/bank/api.rs +122 -0
- package/rust/cli/bank/commands.rs +275 -0
- package/rust/cli/bank/mod.rs +29 -0
- package/rust/cli/build/commands.rs +107 -0
- package/rust/cli/build/mod.rs +2 -0
- package/rust/cli/build/process.rs +146 -0
- package/rust/cli/{check.rs → check/mod.rs} +18 -31
- package/rust/cli/discover/commands.rs +253 -0
- package/rust/cli/discover/config.rs +111 -0
- package/rust/cli/discover/fs.rs +19 -0
- package/rust/cli/discover/install.rs +103 -0
- package/rust/cli/discover/metadata.rs +48 -0
- package/rust/cli/discover/mod.rs +5 -0
- package/rust/cli/{init.rs → init/commands.rs} +88 -87
- package/rust/cli/init/mod.rs +1 -0
- package/rust/cli/install/addon.rs +126 -0
- package/rust/cli/install/bank.rs +53 -0
- package/rust/cli/{install.rs → install/commands.rs} +9 -9
- package/rust/{installer → cli/install}/mod.rs +2 -3
- package/rust/cli/install/plugin.rs +61 -0
- package/rust/cli/{login.rs → login/commands.rs} +8 -11
- package/rust/cli/login/mod.rs +1 -0
- package/rust/cli/mod.rs +2 -2
- package/rust/cli/{driver.rs → parser.rs} +7 -2
- package/rust/cli/play/commands.rs +324 -0
- package/rust/cli/play/io.rs +17 -0
- package/rust/cli/play/mod.rs +5 -0
- package/rust/cli/play/process.rs +150 -0
- package/rust/cli/play/realtime.rs +91 -0
- package/rust/cli/play/utils.rs +23 -0
- package/rust/cli/{telemetry.rs → telemetry/commands.rs} +4 -4
- package/rust/cli/telemetry/event_creator.rs +80 -0
- package/rust/cli/telemetry/mod.rs +3 -0
- package/rust/cli/telemetry/send.rs +51 -0
- package/rust/cli/{template.rs → template/commands.rs} +1 -1
- package/rust/cli/template/mod.rs +1 -0
- package/rust/cli/{update.rs → update/commands.rs} +6 -6
- package/rust/cli/update/mod.rs +1 -0
- package/rust/config/driver.rs +57 -72
- package/rust/config/mod.rs +1 -2
- package/rust/config/ops.rs +26 -0
- package/rust/config/settings.rs +40 -42
- package/rust/core/audio/engine/helpers.rs +158 -0
- package/rust/core/audio/engine/mod.rs +7 -0
- package/rust/core/audio/engine/sample.rs +359 -0
- package/rust/core/audio/engine/synth.rs +325 -0
- package/rust/core/audio/evaluator.rs +68 -27
- package/rust/core/audio/interpreter/arrow_call.rs +113 -33
- package/rust/core/audio/interpreter/call.rs +232 -56
- package/rust/core/audio/interpreter/condition.rs +3 -2
- package/rust/core/audio/interpreter/driver.rs +206 -151
- package/rust/core/audio/interpreter/let_.rs +1 -1
- package/rust/core/audio/interpreter/load.rs +2 -1
- package/rust/core/audio/interpreter/loop_.rs +7 -6
- package/rust/core/audio/interpreter/sleep.rs +2 -1
- package/rust/core/audio/interpreter/spawn.rs +186 -54
- package/rust/core/audio/interpreter/tempo.rs +31 -10
- package/rust/core/audio/interpreter/trigger.rs +2 -2
- package/rust/core/audio/loader/trigger.rs +4 -7
- package/rust/core/audio/player.rs +6 -0
- package/rust/core/audio/renderer.rs +5 -7
- package/rust/core/audio/special/env.rs +3 -1
- package/rust/core/audio/special/math.rs +26 -6
- package/rust/core/audio/special/modulator.rs +2 -2
- package/rust/core/builder/mod.rs +9 -3
- package/rust/core/debugger/lexer.rs +1 -1
- package/rust/core/debugger/mod.rs +6 -0
- package/rust/core/debugger/module.rs +4 -4
- package/rust/core/debugger/preprocessor.rs +1 -1
- package/rust/core/debugger/store.rs +2 -2
- package/rust/core/error/mod.rs +189 -0
- package/rust/core/lexer/driver.rs +61 -0
- package/rust/core/lexer/handler/arrow.rs +1 -1
- package/rust/core/lexer/handler/at.rs +1 -1
- package/rust/core/lexer/handler/brace.rs +2 -2
- package/rust/core/lexer/handler/colon.rs +1 -1
- package/rust/core/lexer/handler/comment.rs +1 -1
- package/rust/core/lexer/handler/dot.rs +1 -1
- package/rust/core/lexer/handler/driver.rs +1 -1
- package/rust/core/lexer/handler/identifier.rs +4 -3
- package/rust/core/lexer/handler/mod.rs +1 -2
- package/rust/core/lexer/handler/number.rs +1 -1
- package/rust/core/lexer/handler/operator.rs +1 -1
- package/rust/core/lexer/handler/parenthesis.rs +2 -2
- package/rust/core/lexer/handler/slash.rs +1 -1
- package/rust/core/lexer/handler/string.rs +1 -1
- package/rust/core/lexer/mod.rs +1 -52
- package/rust/core/lexer/token.rs +91 -97
- package/rust/core/mod.rs +0 -1
- package/rust/core/parser/driver.rs +78 -22
- package/rust/core/parser/handler/arrow_call.rs +28 -8
- package/rust/core/parser/handler/at.rs +55 -21
- package/rust/core/parser/handler/bank.rs +14 -4
- package/rust/core/parser/handler/condition.rs +6 -3
- package/rust/core/parser/handler/dot.rs +5 -3
- package/rust/core/parser/handler/identifier/automate.rs +13 -16
- package/rust/core/parser/handler/identifier/call.rs +4 -4
- package/rust/core/parser/handler/identifier/emit.rs +9 -5
- package/rust/core/parser/handler/identifier/function.rs +20 -7
- package/rust/core/parser/handler/identifier/group.rs +11 -7
- package/rust/core/parser/handler/identifier/let_.rs +24 -9
- package/rust/core/parser/handler/identifier/mod.rs +6 -5
- package/rust/core/parser/handler/identifier/on.rs +16 -7
- package/rust/core/parser/handler/identifier/print.rs +6 -9
- package/rust/core/parser/handler/identifier/sleep.rs +12 -5
- package/rust/core/parser/handler/identifier/spawn.rs +4 -4
- package/rust/core/parser/handler/identifier/synth.rs +79 -9
- package/rust/core/parser/handler/loop_.rs +38 -13
- package/rust/core/parser/handler/mod.rs +1 -0
- package/rust/core/parser/handler/pattern.rs +74 -0
- package/rust/core/parser/handler/tempo.rs +9 -5
- package/rust/core/parser/mod.rs +0 -1
- package/rust/core/parser/statement.rs +6 -137
- package/rust/core/plugin/loader.rs +41 -27
- package/rust/core/plugin/runner.rs +68 -17
- package/rust/core/preprocessor/loader.rs +181 -99
- package/rust/core/preprocessor/processor.rs +9 -9
- package/rust/core/preprocessor/resolver/bank.rs +6 -8
- package/rust/core/preprocessor/resolver/call.rs +47 -23
- package/rust/core/preprocessor/resolver/condition.rs +6 -8
- package/rust/core/preprocessor/resolver/driver.rs +28 -28
- package/rust/core/preprocessor/resolver/function.rs +6 -6
- package/rust/core/preprocessor/resolver/group.rs +6 -8
- package/rust/core/preprocessor/resolver/loop_.rs +8 -10
- package/rust/core/preprocessor/resolver/mod.rs +1 -0
- package/rust/core/preprocessor/resolver/pattern.rs +75 -0
- package/rust/core/preprocessor/resolver/spawn.rs +45 -22
- package/rust/core/preprocessor/resolver/synth.rs +6 -8
- package/rust/core/preprocessor/resolver/tempo.rs +6 -8
- package/rust/core/preprocessor/resolver/trigger.rs +22 -19
- package/rust/core/preprocessor/resolver/value.rs +99 -4
- package/rust/core/store/export.rs +28 -28
- package/rust/core/store/function.rs +6 -0
- package/rust/core/store/global.rs +7 -1
- package/rust/core/store/import.rs +28 -28
- package/rust/core/store/variable.rs +16 -2
- package/rust/core/utils/mod.rs +0 -1
- package/rust/lib.rs +102 -9
- package/rust/main.rs +159 -45
- package/rust/types/Cargo.toml +11 -0
- package/rust/types/src/addons.rs +55 -0
- package/rust/types/src/ast.rs +202 -0
- package/rust/types/src/config.rs +74 -0
- package/rust/types/src/lib.rs +12 -0
- package/rust/types/src/telemetry.rs +85 -0
- package/rust/utils/Cargo.toml +26 -0
- package/rust/utils/{error.rs → src/error.rs} +186 -200
- package/rust/utils/src/file.rs +94 -0
- package/rust/utils/src/first_usage.rs +97 -0
- package/rust/utils/{mod.rs → src/lib.rs} +1 -1
- package/rust/utils/{logger.rs → src/logger.rs} +17 -12
- package/rust/utils/src/path.rs +88 -0
- package/rust/utils/src/signature.rs +41 -0
- package/rust/utils/{spinner.rs → src/spinner.rs} +3 -5
- package/rust/utils/src/version.rs +27 -0
- package/rust/utils/{watcher.rs → src/watcher.rs} +13 -1
- package/rust/web/cdn.rs +34 -0
- package/templates/minimal/README.md +98 -54
- package/templates/welcome/README.md +98 -54
- package/templates/welcome/src/index.deva +56 -8
- package/templates/welcome/src/variables.deva +2 -4
- package/tests/rust/TODO.md +0 -0
- package/tests/typescript/index.spec.ts +136 -0
- package/tests/typescript/playhead.spec.ts +36 -0
- package/tests/typescript/render_e2e.spec.ts +77 -0
- package/tsconfig.json +1 -1
- package/typescript/core/functions/index.ts +83 -0
- package/typescript/core/index.ts +6 -0
- package/typescript/core/types/index.ts +4 -0
- package/typescript/core/types/plugin.ts +19 -0
- package/typescript/core/types/result.ts +29 -0
- package/typescript/core/types/statement.ts +47 -0
- package/typescript/core/types/value.ts +29 -0
- package/typescript/index.ts +7 -2
- package/typescript/pkg/devalang_core.d.ts +4 -0
- package/typescript/scripts/copy-wasm-dts.ts +41 -0
- package/rust/cli/bank.rs +0 -462
- package/rust/cli/build.rs +0 -252
- package/rust/cli/play.rs +0 -1123
- package/rust/common/cdn.rs +0 -5
- package/rust/config/loader.rs +0 -165
- package/rust/config/stats.rs +0 -257
- package/rust/core/audio/engine.rs +0 -696
- package/rust/core/shared/bank.rs +0 -21
- package/rust/core/shared/duration.rs +0 -9
- package/rust/core/shared/mod.rs +0 -3
- package/rust/core/shared/value.rs +0 -35
- package/rust/core/utils/validation.rs +0 -35
- package/rust/installer/addon.rs +0 -84
- package/rust/installer/bank.rs +0 -62
- package/rust/installer/plugin.rs +0 -54
- package/rust/installer/utils.rs +0 -56
- package/rust/utils/file.rs +0 -38
- package/rust/utils/first_usage.rs +0 -83
- package/rust/utils/signature.rs +0 -19
- package/rust/utils/telemetry.rs +0 -292
- package/rust/utils/version.rs +0 -15
- /package/rust/{common → web}/api.rs +0 -0
- /package/rust/{common → web}/mod.rs +0 -0
- /package/rust/{common → web}/sso.rs +0 -0
|
@@ -1,87 +1,88 @@
|
|
|
1
|
-
use crate::
|
|
2
|
-
use
|
|
3
|
-
use
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
let
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
.
|
|
17
|
-
.
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
.
|
|
46
|
-
.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
1
|
+
use crate::cli::template::commands::get_available_templates;
|
|
2
|
+
use devalang_utils::file::copy_dir_recursive;
|
|
3
|
+
use include_dir::{Dir, include_dir};
|
|
4
|
+
use std::{fs, path::Path};
|
|
5
|
+
|
|
6
|
+
#[cfg(feature = "cli")]
|
|
7
|
+
use inquire::{Confirm, Select};
|
|
8
|
+
|
|
9
|
+
static TEMPLATES_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates");
|
|
10
|
+
|
|
11
|
+
#[cfg(feature = "cli")]
|
|
12
|
+
pub fn handle_init_command(name: Option<String>, template: Option<String>) {
|
|
13
|
+
let current_dir = std::env::current_dir().unwrap();
|
|
14
|
+
let project_name = name.clone().unwrap_or_else(|| {
|
|
15
|
+
current_dir
|
|
16
|
+
.file_name()
|
|
17
|
+
.unwrap_or_default()
|
|
18
|
+
.to_string_lossy()
|
|
19
|
+
.to_string()
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Select a template if not provided
|
|
23
|
+
let selected_template = template.unwrap_or_else(|| {
|
|
24
|
+
Select::new(
|
|
25
|
+
"Select a template for your project:",
|
|
26
|
+
get_available_templates(),
|
|
27
|
+
)
|
|
28
|
+
.prompt()
|
|
29
|
+
.unwrap_or_else(|_| {
|
|
30
|
+
eprintln!("No template selected. Exiting...");
|
|
31
|
+
std::process::exit(1);
|
|
32
|
+
})
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if selected_template.is_empty() {
|
|
36
|
+
eprintln!("Template cannot be empty.");
|
|
37
|
+
std::process::exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if name.is_none() {
|
|
41
|
+
// Case of initialization in the current directory
|
|
42
|
+
if fs::read_dir(¤t_dir).unwrap().next().is_some() {
|
|
43
|
+
let confirm =
|
|
44
|
+
Confirm::new("The current directory is not empty. Do you want to continue?")
|
|
45
|
+
.with_default(false)
|
|
46
|
+
.prompt()
|
|
47
|
+
.unwrap_or(false);
|
|
48
|
+
|
|
49
|
+
if !confirm {
|
|
50
|
+
eprintln!("Operation cancelled by the user.");
|
|
51
|
+
std::process::exit(0);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
scaffold_project_current_dir(current_dir.as_path(), selected_template);
|
|
56
|
+
println!(
|
|
57
|
+
"✅ Initialized '{}' project in current directory: {}",
|
|
58
|
+
project_name,
|
|
59
|
+
current_dir.display()
|
|
60
|
+
);
|
|
61
|
+
} else {
|
|
62
|
+
// Case of initialization in a new directory
|
|
63
|
+
let target_path = current_dir.join(&project_name);
|
|
64
|
+
|
|
65
|
+
if target_path.exists() {
|
|
66
|
+
eprintln!("❌ A folder named '{}' already exists.", project_name);
|
|
67
|
+
std::process::exit(1);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fs::create_dir_all(&target_path).expect("Error creating project directory");
|
|
71
|
+
|
|
72
|
+
scaffold_project_current_dir(&target_path, selected_template);
|
|
73
|
+
println!(
|
|
74
|
+
"✅ Initialized '{}' project in: {}",
|
|
75
|
+
project_name,
|
|
76
|
+
target_path.display()
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
fn scaffold_project_current_dir(path: &Path, template: String) {
|
|
82
|
+
let template_dir = TEMPLATES_DIR.get_dir(&template).unwrap_or_else(|| {
|
|
83
|
+
eprintln!("❌ The template '{}' doesn't exist.", template);
|
|
84
|
+
std::process::exit(1);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
copy_dir_recursive(template_dir, path, template_dir.path());
|
|
88
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pub mod commands;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
use crate::{
|
|
2
|
+
cli::install::{bank::install_bank, plugin::install_plugin},
|
|
3
|
+
config::settings::get_user_config,
|
|
4
|
+
web::api::get_api_url,
|
|
5
|
+
};
|
|
6
|
+
use devalang_types::AddonType;
|
|
7
|
+
use std::path::Path;
|
|
8
|
+
|
|
9
|
+
pub async fn install_addon(
|
|
10
|
+
addon_type: AddonType,
|
|
11
|
+
name: &str,
|
|
12
|
+
target_dir: &Path,
|
|
13
|
+
) -> Result<(), String> {
|
|
14
|
+
match addon_type {
|
|
15
|
+
AddonType::Bank => install_bank(name, target_dir).await,
|
|
16
|
+
AddonType::Plugin => install_plugin(name, target_dir).await,
|
|
17
|
+
AddonType::Preset => Err("Preset installation not implemented".into()),
|
|
18
|
+
AddonType::Template => Err("Template installation not implemented".into()),
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pub async fn ask_api_for_signed_url(addon_type: AddonType, slug: &str) -> Result<String, String> {
|
|
23
|
+
let api_url = get_api_url();
|
|
24
|
+
|
|
25
|
+
use devalang_utils::logger::Logger;
|
|
26
|
+
use devalang_utils::logger::LogLevel;
|
|
27
|
+
|
|
28
|
+
// Require an authenticated user for addon installation: token must be present and non-empty
|
|
29
|
+
let stored_token_opt = get_user_config()
|
|
30
|
+
.and_then(|cfg| {
|
|
31
|
+
let t = cfg.session.clone();
|
|
32
|
+
if t.trim().is_empty() {
|
|
33
|
+
None
|
|
34
|
+
} else {
|
|
35
|
+
Some(t)
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if stored_token_opt.is_none() {
|
|
40
|
+
let logger = Logger::new();
|
|
41
|
+
let msg = "Authentication required — run `devalang login` to authenticate";
|
|
42
|
+
logger.log_message(LogLevel::Error, msg);
|
|
43
|
+
return Err("Authentication required: run 'devalang login' to authenticate".to_string());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let request_url = if let Some(token) = &stored_token_opt {
|
|
47
|
+
format!(
|
|
48
|
+
"{}/v1/assets/url?type={}&slug={}&token={}",
|
|
49
|
+
api_url,
|
|
50
|
+
match addon_type {
|
|
51
|
+
AddonType::Bank => "bank",
|
|
52
|
+
AddonType::Plugin => "plugin",
|
|
53
|
+
AddonType::Preset => "preset",
|
|
54
|
+
AddonType::Template => "template",
|
|
55
|
+
},
|
|
56
|
+
slug,
|
|
57
|
+
token
|
|
58
|
+
)
|
|
59
|
+
} else {
|
|
60
|
+
format!(
|
|
61
|
+
"{}/v1/assets/url?type={}&slug={}",
|
|
62
|
+
api_url,
|
|
63
|
+
match addon_type {
|
|
64
|
+
AddonType::Bank => "bank",
|
|
65
|
+
AddonType::Plugin => "plugin",
|
|
66
|
+
AddonType::Preset => "preset",
|
|
67
|
+
AddonType::Template => "template",
|
|
68
|
+
},
|
|
69
|
+
slug
|
|
70
|
+
)
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
let mut headers = reqwest::header::HeaderMap::new();
|
|
74
|
+
if let Some(token) = stored_token_opt {
|
|
75
|
+
headers.insert(
|
|
76
|
+
"Authorization",
|
|
77
|
+
format!("Bearer {}", token).parse().unwrap(),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let client: reqwest::Client = reqwest::Client::builder()
|
|
82
|
+
.default_headers(headers)
|
|
83
|
+
.build()
|
|
84
|
+
.map_err(|_| "Failed to build HTTP client".to_string())?;
|
|
85
|
+
|
|
86
|
+
let resp = client
|
|
87
|
+
.get(&request_url)
|
|
88
|
+
.send()
|
|
89
|
+
.await
|
|
90
|
+
.map_err(|e| format!("Failed to receive response: {}", e))?;
|
|
91
|
+
|
|
92
|
+
let status = resp.status();
|
|
93
|
+
let body_text = resp
|
|
94
|
+
.text()
|
|
95
|
+
.await
|
|
96
|
+
.map_err(|e| format!("Failed to read response body: {}", e))?;
|
|
97
|
+
|
|
98
|
+
// Try to parse JSON; if parsing fails, return body for diagnostics
|
|
99
|
+
let json: serde_json::Value = match serde_json::from_str(&body_text) {
|
|
100
|
+
Ok(v) => v,
|
|
101
|
+
Err(_) => {
|
|
102
|
+
return Err(format!(
|
|
103
|
+
"Invalid JSON response (status {}): {}",
|
|
104
|
+
status, body_text
|
|
105
|
+
));
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Extract payload.url safely
|
|
110
|
+
let signed_url_opt = json
|
|
111
|
+
.get("payload")
|
|
112
|
+
.and_then(|p| p.get("url"))
|
|
113
|
+
.and_then(|u| u.as_str())
|
|
114
|
+
.map(|s| s.to_string());
|
|
115
|
+
|
|
116
|
+
if let Some(signed_url) = signed_url_opt {
|
|
117
|
+
Ok(signed_url)
|
|
118
|
+
} else {
|
|
119
|
+
// Provide detailed diagnostics to help user understand why it's null
|
|
120
|
+
let err_msg = format!(
|
|
121
|
+
"API returned no URL (status {}): {}",
|
|
122
|
+
status, body_text
|
|
123
|
+
);
|
|
124
|
+
Err(err_msg)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
use crate::{
|
|
2
|
+
cli::install::addon::ask_api_for_signed_url, config::ops::load_config,
|
|
3
|
+
web::cdn::download_from_cdn,
|
|
4
|
+
};
|
|
5
|
+
use devalang_types::AddonType;
|
|
6
|
+
use devalang_utils::{
|
|
7
|
+
logger::{LogLevel, Logger},
|
|
8
|
+
path as path_utils,
|
|
9
|
+
};
|
|
10
|
+
use std::path::Path;
|
|
11
|
+
|
|
12
|
+
pub async fn install_bank(name: &str, target_dir: &Path) -> Result<(), String> {
|
|
13
|
+
let logger = Logger::new();
|
|
14
|
+
|
|
15
|
+
let signed_url = ask_api_for_signed_url(AddonType::Bank, name).await?;
|
|
16
|
+
|
|
17
|
+
let bank_dir = target_dir.join("banks");
|
|
18
|
+
let archive_path = path_utils::ensure_deva_dir()?
|
|
19
|
+
.join("tmp")
|
|
20
|
+
.join(format!("{}.devabank", name));
|
|
21
|
+
let extract_path = bank_dir.join(name);
|
|
22
|
+
|
|
23
|
+
download_from_cdn(&signed_url, &archive_path)
|
|
24
|
+
.await
|
|
25
|
+
.map_err(|e| format!("Failed to download: {}", e))?;
|
|
26
|
+
|
|
27
|
+
if extract_path.exists() {
|
|
28
|
+
logger.log_message(
|
|
29
|
+
LogLevel::Warning,
|
|
30
|
+
&format!(
|
|
31
|
+
"Bank '{}' already exists at '{}'. Skipping install.",
|
|
32
|
+
name,
|
|
33
|
+
extract_path.display()
|
|
34
|
+
),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return Ok(());
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Add the bank to the config
|
|
41
|
+
let config_path = path_utils::get_devalang_config_path()?;
|
|
42
|
+
let _config = load_config(Some(&config_path))
|
|
43
|
+
.ok_or_else(|| format!("Failed to load config from '{}'", config_path.display()))?;
|
|
44
|
+
|
|
45
|
+
let _dependency_path = &format!("devalang://bank/{}", name);
|
|
46
|
+
|
|
47
|
+
devalang_utils::file::extract_zip_safely(&archive_path, &extract_path)
|
|
48
|
+
.map_err(|e| format!("Failed to extract: {}", e))?;
|
|
49
|
+
|
|
50
|
+
// TODO: Add the bank to the config
|
|
51
|
+
|
|
52
|
+
Ok(())
|
|
53
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
use crate::
|
|
1
|
+
use crate::cli::install::addon::install_addon;
|
|
2
|
+
#[cfg(feature = "cli")]
|
|
3
|
+
use devalang_types::AddonType;
|
|
4
|
+
use devalang_utils::path as path_utils;
|
|
2
5
|
|
|
3
6
|
/// Handles the installation command for a given addon type and name.
|
|
4
7
|
#[cfg(feature = "cli")]
|
|
5
8
|
pub async fn handle_install_command(name: String, addon_type: AddonType) -> Result<(), String> {
|
|
6
|
-
use
|
|
9
|
+
use devalang_utils::{
|
|
7
10
|
logger::{LogLevel, Logger},
|
|
8
|
-
spinner::
|
|
11
|
+
spinner::start_spinner,
|
|
9
12
|
};
|
|
10
|
-
use std::{thread, time::Duration};
|
|
11
13
|
|
|
12
14
|
let logger = Logger::new();
|
|
13
|
-
let deva_dir =
|
|
15
|
+
let deva_dir = path_utils::ensure_deva_dir()?;
|
|
14
16
|
|
|
15
|
-
let spinner =
|
|
16
|
-
thread::sleep(Duration::from_millis(800));
|
|
17
|
-
});
|
|
17
|
+
let spinner = start_spinner("Installing...");
|
|
18
18
|
|
|
19
|
-
if let Err(e) = install_addon(addon_type.clone(), name.as_str(), deva_dir).await {
|
|
19
|
+
if let Err(e) = install_addon(addon_type.clone(), name.as_str(), &deva_dir).await {
|
|
20
20
|
spinner.finish_and_clear();
|
|
21
21
|
logger.log_message_with_trace(
|
|
22
22
|
LogLevel::Error,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
use crate::{
|
|
2
|
+
config::ops::load_config,
|
|
3
|
+
web::cdn::{download_from_cdn, get_cdn_url},
|
|
4
|
+
};
|
|
5
|
+
use devalang_utils::path as path_utils;
|
|
6
|
+
use std::{fs, path::Path};
|
|
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("plugins");
|
|
13
|
+
|
|
14
|
+
// Ensure .deva/tmp exists and build archive path
|
|
15
|
+
let deva_dir = path_utils::ensure_deva_dir()?;
|
|
16
|
+
let tmp_dir = deva_dir.join("tmp");
|
|
17
|
+
if !tmp_dir.exists() {
|
|
18
|
+
fs::create_dir_all(&tmp_dir)
|
|
19
|
+
.map_err(|e| format!("Failed to create tmp dir '{}': {}", tmp_dir.display(), e))?;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let archive_path = tmp_dir.join(format!("{}.devaplugin", name));
|
|
23
|
+
let extract_path = plugin_dir.join(name);
|
|
24
|
+
|
|
25
|
+
if extract_path.exists() {
|
|
26
|
+
println!(
|
|
27
|
+
"Plugin '{}' already exists at '{}'. Skipping install.",
|
|
28
|
+
name,
|
|
29
|
+
extract_path.display()
|
|
30
|
+
);
|
|
31
|
+
return Ok(());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
download_from_cdn(&url, &archive_path)
|
|
35
|
+
.await
|
|
36
|
+
.map_err(|e| format!("Failed to download: {}", e))?;
|
|
37
|
+
|
|
38
|
+
// Add the plugin to the config: locate project root from target_dir
|
|
39
|
+
let project_root = path_utils::find_project_root_from(target_dir)
|
|
40
|
+
.ok_or_else(|| "Failed to determine project root from target_dir".to_string())?;
|
|
41
|
+
|
|
42
|
+
let config_path = project_root.join(path_utils::DEVALANG_CONFIG);
|
|
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 _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://plugin/{}", name);
|
|
54
|
+
|
|
55
|
+
devalang_utils::file::extract_zip_safely(&archive_path, &extract_path)
|
|
56
|
+
.map_err(|e| format!("Failed to extract: {}", e))?;
|
|
57
|
+
|
|
58
|
+
// TODO: Add the plugin to the config
|
|
59
|
+
|
|
60
|
+
Ok(())
|
|
61
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
use crate::
|
|
2
|
-
use crate::
|
|
1
|
+
use crate::config::settings::set_user_config_value;
|
|
2
|
+
use crate::web::sso::get_sso_url;
|
|
3
3
|
use std::str::FromStr;
|
|
4
|
-
use std::{thread, time::Duration};
|
|
5
4
|
use tiny_http::Header;
|
|
6
5
|
use tiny_http::{Response, Server};
|
|
7
6
|
use webbrowser;
|
|
@@ -10,9 +9,9 @@ use webbrowser;
|
|
|
10
9
|
/// This function initiates the login process by opening the browser and waiting for the callback.
|
|
11
10
|
#[cfg(feature = "cli")]
|
|
12
11
|
pub async fn handle_login_command() -> Result<(), String> {
|
|
13
|
-
use
|
|
14
|
-
use
|
|
15
|
-
use
|
|
12
|
+
use devalang_utils::logger::LogLevel;
|
|
13
|
+
use devalang_utils::logger::Logger;
|
|
14
|
+
use devalang_utils::spinner::start_spinner;
|
|
16
15
|
|
|
17
16
|
let logger = Logger::new();
|
|
18
17
|
|
|
@@ -49,9 +48,7 @@ pub async fn handle_login_command() -> Result<(), String> {
|
|
|
49
48
|
|
|
50
49
|
let server = Server::http(format!("127.0.0.1:{}", listener_port)).unwrap();
|
|
51
50
|
|
|
52
|
-
let spinner =
|
|
53
|
-
thread::sleep(Duration::from_millis(800));
|
|
54
|
-
});
|
|
51
|
+
let spinner = start_spinner("Waiting for authentication...");
|
|
55
52
|
|
|
56
53
|
for request in server.incoming_requests() {
|
|
57
54
|
let query = request.url().to_string();
|
|
@@ -59,7 +56,7 @@ pub async fn handle_login_command() -> Result<(), String> {
|
|
|
59
56
|
if query.contains("session=") || query.contains("error=") {
|
|
60
57
|
let token = query.split("session=").nth(1).unwrap_or("").to_string();
|
|
61
58
|
|
|
62
|
-
if token.
|
|
59
|
+
if !token.is_empty() {
|
|
63
60
|
let response_html = r#"
|
|
64
61
|
<html>
|
|
65
62
|
<body>
|
|
@@ -123,5 +120,5 @@ pub async fn handle_login_command() -> Result<(), String> {
|
|
|
123
120
|
|
|
124
121
|
/// Save the session token to a file in the user's home directory
|
|
125
122
|
fn save_token(token: &str) {
|
|
126
|
-
|
|
123
|
+
set_user_config_value("session", serde_json::Value::String(token.to_string()));
|
|
127
124
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pub mod commands;
|
package/rust/cli/mod.rs
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
use crate::utils::version::get_version;
|
|
2
1
|
use clap::{Parser, Subcommand};
|
|
3
2
|
|
|
4
3
|
#[derive(Parser)]
|
|
5
4
|
#[command(name = "devalang")]
|
|
6
5
|
#[command(author = "Devaloop")]
|
|
7
|
-
#[command(version = get_version())]
|
|
8
6
|
#[command(about = "🦊 Devalang – A programming language for music and sound.")]
|
|
9
7
|
pub struct Cli {
|
|
10
8
|
#[arg(long, global = true)]
|
|
@@ -25,6 +23,8 @@ pub enum TelemetryCommand {
|
|
|
25
23
|
|
|
26
24
|
#[derive(Subcommand)]
|
|
27
25
|
pub enum InstallCommand {
|
|
26
|
+
/// Installs a template.
|
|
27
|
+
Template { name: String },
|
|
28
28
|
/// Installs a bank.
|
|
29
29
|
Bank { name: String },
|
|
30
30
|
/// Installs a plugin.
|
|
@@ -259,12 +259,17 @@ pub enum Commands {
|
|
|
259
259
|
/// - `template` - Installs a template.
|
|
260
260
|
/// - `bank` - Installs a bank.
|
|
261
261
|
/// - `plugin` - Installs a plugin.
|
|
262
|
+
/// - `preset` - Installs a preset.
|
|
262
263
|
///
|
|
263
264
|
Install {
|
|
264
265
|
#[command(subcommand)]
|
|
265
266
|
command: InstallCommand,
|
|
266
267
|
},
|
|
267
268
|
|
|
269
|
+
/// Discover available local addons for Devalang.
|
|
270
|
+
///
|
|
271
|
+
Discover {},
|
|
272
|
+
|
|
268
273
|
/// Manage banks for Devalang projects.
|
|
269
274
|
///
|
|
270
275
|
/// ### Subcommands
|