@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
|
@@ -1,118 +1,109 @@
|
|
|
1
|
-
use crate::{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
//
|
|
29
|
-
let
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if let Some(signed_url) = signed_url_opt {
|
|
112
|
-
Ok(signed_url)
|
|
113
|
-
} else {
|
|
114
|
-
// Provide detailed diagnostics to help user understand why it's null
|
|
115
|
-
let err_msg = format!("API returned no URL (status {}): {}", status, body_text);
|
|
116
|
-
Err(err_msg)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
1
|
+
use crate::{config::settings::get_user_config, web::forge::get_forge_api_url};
|
|
2
|
+
use devalang_types::AddonType;
|
|
3
|
+
|
|
4
|
+
pub async fn ask_api_for_signed_url(
|
|
5
|
+
addon_type: AddonType,
|
|
6
|
+
publisher: String,
|
|
7
|
+
slug: &str,
|
|
8
|
+
) -> Result<String, String> {
|
|
9
|
+
let forge_api_url = get_forge_api_url();
|
|
10
|
+
|
|
11
|
+
use devalang_utils::logger::LogLevel;
|
|
12
|
+
use devalang_utils::logger::Logger;
|
|
13
|
+
|
|
14
|
+
// Require an authenticated user for addon installation: token must be present and non-empty
|
|
15
|
+
let stored_token_opt = get_user_config().and_then(|cfg| {
|
|
16
|
+
let t = cfg.session.clone();
|
|
17
|
+
if t.trim().is_empty() { None } else { Some(t) }
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if stored_token_opt.is_none() {
|
|
21
|
+
let logger = Logger::new();
|
|
22
|
+
let msg = "Authentication required — run `devalang login` to authenticate";
|
|
23
|
+
logger.log_message(LogLevel::Error, msg);
|
|
24
|
+
return Err("Authentication required: run 'devalang login' to authenticate".to_string());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Build request URL. If publisher is empty, omit the publisher param so the API
|
|
28
|
+
// can resolve publisher by addon name itself (server-side lookup).
|
|
29
|
+
let kind = match addon_type {
|
|
30
|
+
AddonType::Bank => "bank",
|
|
31
|
+
AddonType::Plugin => "plugin",
|
|
32
|
+
AddonType::Preset => "preset",
|
|
33
|
+
AddonType::Template => "template",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let request_url = if let Some(token) = &stored_token_opt {
|
|
37
|
+
if publisher.trim().is_empty() {
|
|
38
|
+
format!(
|
|
39
|
+
"{}/v1/addon/url?type={}&slug={}&token={}",
|
|
40
|
+
forge_api_url, kind, slug, token
|
|
41
|
+
)
|
|
42
|
+
} else {
|
|
43
|
+
format!(
|
|
44
|
+
"{}/v1/addon/url?type={}&publisher={}&slug={}&token={}",
|
|
45
|
+
forge_api_url, kind, publisher, slug, token
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
if publisher.trim().is_empty() {
|
|
50
|
+
format!("{}/v1/addon/url?type={}&slug={}", forge_api_url, kind, slug)
|
|
51
|
+
} else {
|
|
52
|
+
format!(
|
|
53
|
+
"{}/v1/addon/url?type={}&publisher={}&slug={}",
|
|
54
|
+
forge_api_url, kind, publisher, slug
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
let mut headers = reqwest::header::HeaderMap::new();
|
|
60
|
+
if let Some(token) = stored_token_opt {
|
|
61
|
+
headers.insert(
|
|
62
|
+
"Authorization",
|
|
63
|
+
format!("Bearer {}", token).parse().unwrap(),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let client: reqwest::Client = reqwest::Client::builder()
|
|
68
|
+
.default_headers(headers)
|
|
69
|
+
.build()
|
|
70
|
+
.map_err(|_| "Failed to build HTTP client".to_string())?;
|
|
71
|
+
|
|
72
|
+
let resp = client
|
|
73
|
+
.get(&request_url)
|
|
74
|
+
.send()
|
|
75
|
+
.await
|
|
76
|
+
.map_err(|e| format!("Failed to receive response: {}", e))?;
|
|
77
|
+
|
|
78
|
+
let status = resp.status();
|
|
79
|
+
let body_text = resp
|
|
80
|
+
.text()
|
|
81
|
+
.await
|
|
82
|
+
.map_err(|e| format!("Failed to read response body: {}", e))?;
|
|
83
|
+
|
|
84
|
+
// Try to parse JSON; if parsing fails, return body for diagnostics
|
|
85
|
+
let json: serde_json::Value = match serde_json::from_str(&body_text) {
|
|
86
|
+
Ok(v) => v,
|
|
87
|
+
Err(_) => {
|
|
88
|
+
return Err(format!(
|
|
89
|
+
"Invalid JSON response (status {}): {}",
|
|
90
|
+
status, body_text
|
|
91
|
+
));
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Extract payload.url safely
|
|
96
|
+
let signed_url_opt = json
|
|
97
|
+
.get("payload")
|
|
98
|
+
.and_then(|p| p.get("url"))
|
|
99
|
+
.and_then(|u| u.as_str())
|
|
100
|
+
.map(|s| s.to_string());
|
|
101
|
+
|
|
102
|
+
if let Some(signed_url) = signed_url_opt {
|
|
103
|
+
Ok(signed_url)
|
|
104
|
+
} else {
|
|
105
|
+
// Provide detailed diagnostics to help user understand why it's null
|
|
106
|
+
let err_msg = format!("API returned no URL (status {}): {}", status, body_text);
|
|
107
|
+
Err(err_msg)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -1,153 +1,153 @@
|
|
|
1
|
-
use crate::config::driver::ProjectConfig;
|
|
2
|
-
use devalang_utils::logger::{LogLevel, Logger};
|
|
3
|
-
use devalang_utils::path::find_entry_file;
|
|
4
|
-
use devalang_utils::watcher::watch_directory;
|
|
5
|
-
|
|
6
|
-
#[cfg(feature = "cli")]
|
|
7
|
-
pub fn handle_build_command(
|
|
8
|
-
config: Option<ProjectConfig>,
|
|
9
|
-
entry: Option<String>,
|
|
10
|
-
output: Option<String>,
|
|
11
|
-
output_format: Vec<crate::cli::parser::OutputFormat>,
|
|
12
|
-
audio_format: crate::cli::parser::AudioFormat,
|
|
13
|
-
sample_rate: u32,
|
|
14
|
-
watch: bool,
|
|
15
|
-
debug: bool,
|
|
16
|
-
compress: bool,
|
|
17
|
-
) -> Result<(), String> {
|
|
18
|
-
// determine fetched values from config or CLI
|
|
19
|
-
let fetched_entry = if entry.is_none() {
|
|
20
|
-
config
|
|
21
|
-
.as_ref()
|
|
22
|
-
.and_then(|c| c.defaults.entry.clone())
|
|
23
|
-
.unwrap_or_default()
|
|
24
|
-
} else {
|
|
25
|
-
entry.clone().unwrap_or_default()
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
let fetched_output = if output.is_none() {
|
|
29
|
-
config
|
|
30
|
-
.as_ref()
|
|
31
|
-
.and_then(|c| c.defaults.output.clone())
|
|
32
|
-
.unwrap_or_default()
|
|
33
|
-
} else {
|
|
34
|
-
output.clone().unwrap_or_default()
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
let fetched_watch = if watch {
|
|
38
|
-
watch
|
|
39
|
-
} else {
|
|
40
|
-
config
|
|
41
|
-
.as_ref()
|
|
42
|
-
.and_then(|c| c.defaults.watch)
|
|
43
|
-
.unwrap_or(false)
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
let logger = Logger::new();
|
|
47
|
-
|
|
48
|
-
// Determine final audio_format: prefer CLI, else config default if present
|
|
49
|
-
let mut final_audio_format = audio_format;
|
|
50
|
-
if let Some(cfg) = config.as_ref() {
|
|
51
|
-
if let Some(af) = cfg.defaults.audio_format.as_ref() {
|
|
52
|
-
// Only override if CLI provided an empty/placeholder — clap provides a default, so we only override when CLI wasn't explicit (rare).
|
|
53
|
-
// We'll accept values: "wav16", "wav24", "wav32" (case-insensitive)
|
|
54
|
-
let af_low = af.to_lowercase();
|
|
55
|
-
final_audio_format = match af_low.as_str() {
|
|
56
|
-
"wav24" => crate::cli::parser::AudioFormat::Wav24,
|
|
57
|
-
"wav32" => crate::cli::parser::AudioFormat::Wav32,
|
|
58
|
-
_ => crate::cli::parser::AudioFormat::Wav16,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Determine final output_format: prefer CLI vector, else config default list
|
|
64
|
-
let mut final_output_format: Vec<crate::cli::parser::OutputFormat> = output_format.clone();
|
|
65
|
-
if final_output_format.is_empty() {
|
|
66
|
-
if let Some(cfg) = config.as_ref() {
|
|
67
|
-
if let Some(ofs) = cfg.defaults.output_format.as_ref() {
|
|
68
|
-
for s in ofs {
|
|
69
|
-
match s.to_lowercase().as_str() {
|
|
70
|
-
"mid" | "midi" => {
|
|
71
|
-
final_output_format.push(crate::cli::parser::OutputFormat::Mid);
|
|
72
|
-
}
|
|
73
|
-
_ => final_output_format.push(crate::cli::parser::OutputFormat::Wav),
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if fetched_entry.is_empty() {
|
|
81
|
-
logger.log_message(
|
|
82
|
-
LogLevel::Error,
|
|
83
|
-
"Entry path is not specified. Please provide a valid entry path.",
|
|
84
|
-
);
|
|
85
|
-
return Err("missing entry path".to_string());
|
|
86
|
-
}
|
|
87
|
-
if fetched_output.is_empty() {
|
|
88
|
-
logger.log_message(
|
|
89
|
-
LogLevel::Error,
|
|
90
|
-
"Output directory is not specified. Please provide a valid output directory.",
|
|
91
|
-
);
|
|
92
|
-
return Err("missing output directory".to_string());
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
let entry_file = match find_entry_file(&fetched_entry) {
|
|
96
|
-
Some(p) => p,
|
|
97
|
-
None => {
|
|
98
|
-
logger.log_message(
|
|
99
|
-
LogLevel::Error,
|
|
100
|
-
&format!("❌ index.deva not found in directory: {}", fetched_entry),
|
|
101
|
-
);
|
|
102
|
-
return Err("index.deva not found".to_string());
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
// SECTION Begin build
|
|
107
|
-
if fetched_watch {
|
|
108
|
-
let _ = crate::cli::build::process::process_build(
|
|
109
|
-
entry_file.clone(),
|
|
110
|
-
fetched_output.clone(),
|
|
111
|
-
final_output_format.clone(),
|
|
112
|
-
final_audio_format,
|
|
113
|
-
sample_rate,
|
|
114
|
-
debug,
|
|
115
|
-
compress,
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
logger.log_message(
|
|
119
|
-
LogLevel::Watcher,
|
|
120
|
-
&format!("Watching for changes in '{}'...", fetched_entry),
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
watch_directory(entry_file.clone(), move || {
|
|
124
|
-
logger.log_message(LogLevel::Watcher, "Detected changes, re-building...");
|
|
125
|
-
|
|
126
|
-
let _ = crate::cli::build::process::process_build(
|
|
127
|
-
entry_file.clone(),
|
|
128
|
-
fetched_output.clone(),
|
|
129
|
-
final_output_format.clone(),
|
|
130
|
-
final_audio_format,
|
|
131
|
-
sample_rate,
|
|
132
|
-
debug,
|
|
133
|
-
compress,
|
|
134
|
-
);
|
|
135
|
-
})
|
|
136
|
-
.unwrap();
|
|
137
|
-
} else {
|
|
138
|
-
let res = crate::cli::build::process::process_build(
|
|
139
|
-
entry_file,
|
|
140
|
-
fetched_output,
|
|
141
|
-
final_output_format,
|
|
142
|
-
final_audio_format,
|
|
143
|
-
sample_rate,
|
|
144
|
-
debug,
|
|
145
|
-
compress,
|
|
146
|
-
);
|
|
147
|
-
if let Err(e) = res {
|
|
148
|
-
return Err(e);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
Ok(())
|
|
153
|
-
}
|
|
1
|
+
use crate::config::driver::ProjectConfig;
|
|
2
|
+
use devalang_utils::logger::{LogLevel, Logger};
|
|
3
|
+
use devalang_utils::path::find_entry_file;
|
|
4
|
+
use devalang_utils::watcher::watch_directory;
|
|
5
|
+
|
|
6
|
+
#[cfg(feature = "cli")]
|
|
7
|
+
pub fn handle_build_command(
|
|
8
|
+
config: Option<ProjectConfig>,
|
|
9
|
+
entry: Option<String>,
|
|
10
|
+
output: Option<String>,
|
|
11
|
+
output_format: Vec<crate::cli::parser::OutputFormat>,
|
|
12
|
+
audio_format: crate::cli::parser::AudioFormat,
|
|
13
|
+
sample_rate: u32,
|
|
14
|
+
watch: bool,
|
|
15
|
+
debug: bool,
|
|
16
|
+
compress: bool,
|
|
17
|
+
) -> Result<(), String> {
|
|
18
|
+
// determine fetched values from config or CLI
|
|
19
|
+
let fetched_entry = if entry.is_none() {
|
|
20
|
+
config
|
|
21
|
+
.as_ref()
|
|
22
|
+
.and_then(|c| c.defaults.entry.clone())
|
|
23
|
+
.unwrap_or_default()
|
|
24
|
+
} else {
|
|
25
|
+
entry.clone().unwrap_or_default()
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
let fetched_output = if output.is_none() {
|
|
29
|
+
config
|
|
30
|
+
.as_ref()
|
|
31
|
+
.and_then(|c| c.defaults.output.clone())
|
|
32
|
+
.unwrap_or_default()
|
|
33
|
+
} else {
|
|
34
|
+
output.clone().unwrap_or_default()
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
let fetched_watch = if watch {
|
|
38
|
+
watch
|
|
39
|
+
} else {
|
|
40
|
+
config
|
|
41
|
+
.as_ref()
|
|
42
|
+
.and_then(|c| c.defaults.watch)
|
|
43
|
+
.unwrap_or(false)
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
let logger = Logger::new();
|
|
47
|
+
|
|
48
|
+
// Determine final audio_format: prefer CLI, else config default if present
|
|
49
|
+
let mut final_audio_format = audio_format;
|
|
50
|
+
if let Some(cfg) = config.as_ref() {
|
|
51
|
+
if let Some(af) = cfg.defaults.audio_format.as_ref() {
|
|
52
|
+
// Only override if CLI provided an empty/placeholder — clap provides a default, so we only override when CLI wasn't explicit (rare).
|
|
53
|
+
// We'll accept values: "wav16", "wav24", "wav32" (case-insensitive)
|
|
54
|
+
let af_low = af.to_lowercase();
|
|
55
|
+
final_audio_format = match af_low.as_str() {
|
|
56
|
+
"wav24" => crate::cli::parser::AudioFormat::Wav24,
|
|
57
|
+
"wav32" => crate::cli::parser::AudioFormat::Wav32,
|
|
58
|
+
_ => crate::cli::parser::AudioFormat::Wav16,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Determine final output_format: prefer CLI vector, else config default list
|
|
64
|
+
let mut final_output_format: Vec<crate::cli::parser::OutputFormat> = output_format.clone();
|
|
65
|
+
if final_output_format.is_empty() {
|
|
66
|
+
if let Some(cfg) = config.as_ref() {
|
|
67
|
+
if let Some(ofs) = cfg.defaults.output_format.as_ref() {
|
|
68
|
+
for s in ofs {
|
|
69
|
+
match s.to_lowercase().as_str() {
|
|
70
|
+
"mid" | "midi" => {
|
|
71
|
+
final_output_format.push(crate::cli::parser::OutputFormat::Mid);
|
|
72
|
+
}
|
|
73
|
+
_ => final_output_format.push(crate::cli::parser::OutputFormat::Wav),
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if fetched_entry.is_empty() {
|
|
81
|
+
logger.log_message(
|
|
82
|
+
LogLevel::Error,
|
|
83
|
+
"Entry path is not specified. Please provide a valid entry path.",
|
|
84
|
+
);
|
|
85
|
+
return Err("missing entry path".to_string());
|
|
86
|
+
}
|
|
87
|
+
if fetched_output.is_empty() {
|
|
88
|
+
logger.log_message(
|
|
89
|
+
LogLevel::Error,
|
|
90
|
+
"Output directory is not specified. Please provide a valid output directory.",
|
|
91
|
+
);
|
|
92
|
+
return Err("missing output directory".to_string());
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let entry_file = match find_entry_file(&fetched_entry) {
|
|
96
|
+
Some(p) => p,
|
|
97
|
+
None => {
|
|
98
|
+
logger.log_message(
|
|
99
|
+
LogLevel::Error,
|
|
100
|
+
&format!("❌ index.deva not found in directory: {}", fetched_entry),
|
|
101
|
+
);
|
|
102
|
+
return Err("index.deva not found".to_string());
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// SECTION Begin build
|
|
107
|
+
if fetched_watch {
|
|
108
|
+
let _ = crate::cli::build::process::process_build(
|
|
109
|
+
entry_file.clone(),
|
|
110
|
+
fetched_output.clone(),
|
|
111
|
+
final_output_format.clone(),
|
|
112
|
+
final_audio_format,
|
|
113
|
+
sample_rate,
|
|
114
|
+
debug,
|
|
115
|
+
compress,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
logger.log_message(
|
|
119
|
+
LogLevel::Watcher,
|
|
120
|
+
&format!("Watching for changes in '{}'...", fetched_entry),
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
watch_directory(entry_file.clone(), move || {
|
|
124
|
+
logger.log_message(LogLevel::Watcher, "Detected changes, re-building...");
|
|
125
|
+
|
|
126
|
+
let _ = crate::cli::build::process::process_build(
|
|
127
|
+
entry_file.clone(),
|
|
128
|
+
fetched_output.clone(),
|
|
129
|
+
final_output_format.clone(),
|
|
130
|
+
final_audio_format,
|
|
131
|
+
sample_rate,
|
|
132
|
+
debug,
|
|
133
|
+
compress,
|
|
134
|
+
);
|
|
135
|
+
})
|
|
136
|
+
.unwrap();
|
|
137
|
+
} else {
|
|
138
|
+
let res = crate::cli::build::process::process_build(
|
|
139
|
+
entry_file,
|
|
140
|
+
fetched_output,
|
|
141
|
+
final_output_format,
|
|
142
|
+
final_audio_format,
|
|
143
|
+
sample_rate,
|
|
144
|
+
debug,
|
|
145
|
+
compress,
|
|
146
|
+
);
|
|
147
|
+
if let Err(e) = res {
|
|
148
|
+
return Err(e);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
Ok(())
|
|
153
|
+
}
|