@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.
Files changed (239) hide show
  1. package/.cargo/config.toml +2 -0
  2. package/.devalang +10 -10
  3. package/.github/workflows/ci.yml +0 -1
  4. package/Cargo.toml +18 -2
  5. package/README.md +82 -34
  6. package/docs/CHANGELOG.md +91 -0
  7. package/docs/ROADMAP.md +7 -4
  8. package/docs/TODO.md +1 -1
  9. package/examples/index.deva +55 -35
  10. package/examples/pattern.deva +5 -5
  11. package/out-tsc/bin/index.d.ts +2 -0
  12. package/out-tsc/core/functions/index.d.ts +37 -0
  13. package/out-tsc/core/functions/index.js +76 -0
  14. package/out-tsc/core/index.d.ts +6 -0
  15. package/out-tsc/core/index.js +22 -0
  16. package/out-tsc/core/types/index.d.ts +4 -0
  17. package/out-tsc/core/types/index.js +20 -0
  18. package/out-tsc/core/types/plugin.d.ts +18 -0
  19. package/out-tsc/core/types/plugin.js +2 -0
  20. package/out-tsc/core/types/result.d.ts +27 -0
  21. package/out-tsc/core/types/result.js +2 -0
  22. package/out-tsc/core/types/statement.d.ts +106 -0
  23. package/out-tsc/core/types/statement.js +2 -0
  24. package/out-tsc/core/types/value.d.ts +43 -0
  25. package/out-tsc/core/types/value.js +2 -0
  26. package/out-tsc/index.d.ts +7 -0
  27. package/out-tsc/index.js +41 -2
  28. package/out-tsc/pkg/devalang_core.d.ts +7 -0
  29. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +33 -0
  30. package/out-tsc/scripts/copy-wasm-dts.d.ts +1 -0
  31. package/out-tsc/scripts/copy-wasm-dts.js +73 -0
  32. package/out-tsc/scripts/postinstall.d.ts +1 -0
  33. package/out-tsc/scripts/postinstall.js +33 -23
  34. package/out-tsc/scripts/version/bump.d.ts +1 -0
  35. package/out-tsc/scripts/version/fetch.d.ts +1 -0
  36. package/out-tsc/scripts/version/index.d.ts +1 -0
  37. package/out-tsc/scripts/version/sync.d.ts +1 -0
  38. package/package.json +16 -4
  39. package/project-version.json +3 -3
  40. package/rust/cli/bank/api.rs +122 -0
  41. package/rust/cli/bank/commands.rs +275 -0
  42. package/rust/cli/bank/mod.rs +29 -0
  43. package/rust/cli/build/commands.rs +107 -0
  44. package/rust/cli/build/mod.rs +2 -0
  45. package/rust/cli/build/process.rs +146 -0
  46. package/rust/cli/{check.rs → check/mod.rs} +18 -31
  47. package/rust/cli/discover/commands.rs +253 -0
  48. package/rust/cli/discover/config.rs +111 -0
  49. package/rust/cli/discover/fs.rs +19 -0
  50. package/rust/cli/discover/install.rs +103 -0
  51. package/rust/cli/discover/metadata.rs +48 -0
  52. package/rust/cli/discover/mod.rs +5 -0
  53. package/rust/cli/{init.rs → init/commands.rs} +88 -87
  54. package/rust/cli/init/mod.rs +1 -0
  55. package/rust/cli/install/addon.rs +126 -0
  56. package/rust/cli/install/bank.rs +53 -0
  57. package/rust/cli/{install.rs → install/commands.rs} +9 -9
  58. package/rust/{installer → cli/install}/mod.rs +2 -3
  59. package/rust/cli/install/plugin.rs +61 -0
  60. package/rust/cli/{login.rs → login/commands.rs} +8 -11
  61. package/rust/cli/login/mod.rs +1 -0
  62. package/rust/cli/mod.rs +2 -2
  63. package/rust/cli/{driver.rs → parser.rs} +7 -2
  64. package/rust/cli/play/commands.rs +324 -0
  65. package/rust/cli/play/io.rs +17 -0
  66. package/rust/cli/play/mod.rs +5 -0
  67. package/rust/cli/play/process.rs +150 -0
  68. package/rust/cli/play/realtime.rs +91 -0
  69. package/rust/cli/play/utils.rs +23 -0
  70. package/rust/cli/{telemetry.rs → telemetry/commands.rs} +4 -4
  71. package/rust/cli/telemetry/event_creator.rs +80 -0
  72. package/rust/cli/telemetry/mod.rs +3 -0
  73. package/rust/cli/telemetry/send.rs +51 -0
  74. package/rust/cli/{template.rs → template/commands.rs} +1 -1
  75. package/rust/cli/template/mod.rs +1 -0
  76. package/rust/cli/{update.rs → update/commands.rs} +6 -6
  77. package/rust/cli/update/mod.rs +1 -0
  78. package/rust/config/driver.rs +57 -72
  79. package/rust/config/mod.rs +1 -2
  80. package/rust/config/ops.rs +26 -0
  81. package/rust/config/settings.rs +40 -42
  82. package/rust/core/audio/engine/helpers.rs +158 -0
  83. package/rust/core/audio/engine/mod.rs +7 -0
  84. package/rust/core/audio/engine/sample.rs +359 -0
  85. package/rust/core/audio/engine/synth.rs +325 -0
  86. package/rust/core/audio/evaluator.rs +68 -27
  87. package/rust/core/audio/interpreter/arrow_call.rs +113 -33
  88. package/rust/core/audio/interpreter/call.rs +232 -56
  89. package/rust/core/audio/interpreter/condition.rs +3 -2
  90. package/rust/core/audio/interpreter/driver.rs +206 -151
  91. package/rust/core/audio/interpreter/let_.rs +1 -1
  92. package/rust/core/audio/interpreter/load.rs +2 -1
  93. package/rust/core/audio/interpreter/loop_.rs +7 -6
  94. package/rust/core/audio/interpreter/sleep.rs +2 -1
  95. package/rust/core/audio/interpreter/spawn.rs +186 -54
  96. package/rust/core/audio/interpreter/tempo.rs +31 -10
  97. package/rust/core/audio/interpreter/trigger.rs +2 -2
  98. package/rust/core/audio/loader/trigger.rs +4 -7
  99. package/rust/core/audio/player.rs +6 -0
  100. package/rust/core/audio/renderer.rs +5 -7
  101. package/rust/core/audio/special/env.rs +3 -1
  102. package/rust/core/audio/special/math.rs +26 -6
  103. package/rust/core/audio/special/modulator.rs +2 -2
  104. package/rust/core/builder/mod.rs +9 -3
  105. package/rust/core/debugger/lexer.rs +1 -1
  106. package/rust/core/debugger/mod.rs +6 -0
  107. package/rust/core/debugger/module.rs +4 -4
  108. package/rust/core/debugger/preprocessor.rs +1 -1
  109. package/rust/core/debugger/store.rs +2 -2
  110. package/rust/core/error/mod.rs +189 -0
  111. package/rust/core/lexer/driver.rs +61 -0
  112. package/rust/core/lexer/handler/arrow.rs +1 -1
  113. package/rust/core/lexer/handler/at.rs +1 -1
  114. package/rust/core/lexer/handler/brace.rs +2 -2
  115. package/rust/core/lexer/handler/colon.rs +1 -1
  116. package/rust/core/lexer/handler/comment.rs +1 -1
  117. package/rust/core/lexer/handler/dot.rs +1 -1
  118. package/rust/core/lexer/handler/driver.rs +1 -1
  119. package/rust/core/lexer/handler/identifier.rs +4 -3
  120. package/rust/core/lexer/handler/mod.rs +1 -2
  121. package/rust/core/lexer/handler/number.rs +1 -1
  122. package/rust/core/lexer/handler/operator.rs +1 -1
  123. package/rust/core/lexer/handler/parenthesis.rs +2 -2
  124. package/rust/core/lexer/handler/slash.rs +1 -1
  125. package/rust/core/lexer/handler/string.rs +1 -1
  126. package/rust/core/lexer/mod.rs +1 -52
  127. package/rust/core/lexer/token.rs +91 -97
  128. package/rust/core/mod.rs +0 -1
  129. package/rust/core/parser/driver.rs +78 -22
  130. package/rust/core/parser/handler/arrow_call.rs +28 -8
  131. package/rust/core/parser/handler/at.rs +55 -21
  132. package/rust/core/parser/handler/bank.rs +14 -4
  133. package/rust/core/parser/handler/condition.rs +6 -3
  134. package/rust/core/parser/handler/dot.rs +5 -3
  135. package/rust/core/parser/handler/identifier/automate.rs +13 -16
  136. package/rust/core/parser/handler/identifier/call.rs +4 -4
  137. package/rust/core/parser/handler/identifier/emit.rs +9 -5
  138. package/rust/core/parser/handler/identifier/function.rs +20 -7
  139. package/rust/core/parser/handler/identifier/group.rs +11 -7
  140. package/rust/core/parser/handler/identifier/let_.rs +24 -9
  141. package/rust/core/parser/handler/identifier/mod.rs +6 -5
  142. package/rust/core/parser/handler/identifier/on.rs +16 -7
  143. package/rust/core/parser/handler/identifier/print.rs +6 -9
  144. package/rust/core/parser/handler/identifier/sleep.rs +12 -5
  145. package/rust/core/parser/handler/identifier/spawn.rs +4 -4
  146. package/rust/core/parser/handler/identifier/synth.rs +79 -9
  147. package/rust/core/parser/handler/loop_.rs +38 -13
  148. package/rust/core/parser/handler/mod.rs +1 -0
  149. package/rust/core/parser/handler/pattern.rs +74 -0
  150. package/rust/core/parser/handler/tempo.rs +9 -5
  151. package/rust/core/parser/mod.rs +0 -1
  152. package/rust/core/parser/statement.rs +6 -137
  153. package/rust/core/plugin/loader.rs +41 -27
  154. package/rust/core/plugin/runner.rs +68 -17
  155. package/rust/core/preprocessor/loader.rs +181 -99
  156. package/rust/core/preprocessor/processor.rs +9 -9
  157. package/rust/core/preprocessor/resolver/bank.rs +6 -8
  158. package/rust/core/preprocessor/resolver/call.rs +47 -23
  159. package/rust/core/preprocessor/resolver/condition.rs +6 -8
  160. package/rust/core/preprocessor/resolver/driver.rs +28 -28
  161. package/rust/core/preprocessor/resolver/function.rs +6 -6
  162. package/rust/core/preprocessor/resolver/group.rs +6 -8
  163. package/rust/core/preprocessor/resolver/loop_.rs +8 -10
  164. package/rust/core/preprocessor/resolver/mod.rs +1 -0
  165. package/rust/core/preprocessor/resolver/pattern.rs +75 -0
  166. package/rust/core/preprocessor/resolver/spawn.rs +45 -22
  167. package/rust/core/preprocessor/resolver/synth.rs +6 -8
  168. package/rust/core/preprocessor/resolver/tempo.rs +6 -8
  169. package/rust/core/preprocessor/resolver/trigger.rs +22 -19
  170. package/rust/core/preprocessor/resolver/value.rs +99 -4
  171. package/rust/core/store/export.rs +28 -28
  172. package/rust/core/store/function.rs +6 -0
  173. package/rust/core/store/global.rs +7 -1
  174. package/rust/core/store/import.rs +28 -28
  175. package/rust/core/store/variable.rs +16 -2
  176. package/rust/core/utils/mod.rs +0 -1
  177. package/rust/lib.rs +102 -9
  178. package/rust/main.rs +159 -45
  179. package/rust/types/Cargo.toml +11 -0
  180. package/rust/types/src/addons.rs +55 -0
  181. package/rust/types/src/ast.rs +202 -0
  182. package/rust/types/src/config.rs +74 -0
  183. package/rust/types/src/lib.rs +12 -0
  184. package/rust/types/src/telemetry.rs +85 -0
  185. package/rust/utils/Cargo.toml +26 -0
  186. package/rust/utils/{error.rs → src/error.rs} +186 -200
  187. package/rust/utils/src/file.rs +94 -0
  188. package/rust/utils/src/first_usage.rs +97 -0
  189. package/rust/utils/{mod.rs → src/lib.rs} +1 -1
  190. package/rust/utils/{logger.rs → src/logger.rs} +17 -12
  191. package/rust/utils/src/path.rs +88 -0
  192. package/rust/utils/src/signature.rs +41 -0
  193. package/rust/utils/{spinner.rs → src/spinner.rs} +3 -5
  194. package/rust/utils/src/version.rs +27 -0
  195. package/rust/utils/{watcher.rs → src/watcher.rs} +13 -1
  196. package/rust/web/cdn.rs +34 -0
  197. package/templates/minimal/README.md +98 -54
  198. package/templates/welcome/README.md +98 -54
  199. package/templates/welcome/src/index.deva +56 -8
  200. package/templates/welcome/src/variables.deva +2 -4
  201. package/tests/rust/TODO.md +0 -0
  202. package/tests/typescript/index.spec.ts +136 -0
  203. package/tests/typescript/playhead.spec.ts +36 -0
  204. package/tests/typescript/render_e2e.spec.ts +77 -0
  205. package/tsconfig.json +1 -1
  206. package/typescript/core/functions/index.ts +83 -0
  207. package/typescript/core/index.ts +6 -0
  208. package/typescript/core/types/index.ts +4 -0
  209. package/typescript/core/types/plugin.ts +19 -0
  210. package/typescript/core/types/result.ts +29 -0
  211. package/typescript/core/types/statement.ts +47 -0
  212. package/typescript/core/types/value.ts +29 -0
  213. package/typescript/index.ts +7 -2
  214. package/typescript/pkg/devalang_core.d.ts +4 -0
  215. package/typescript/scripts/copy-wasm-dts.ts +41 -0
  216. package/rust/cli/bank.rs +0 -462
  217. package/rust/cli/build.rs +0 -252
  218. package/rust/cli/play.rs +0 -1123
  219. package/rust/common/cdn.rs +0 -5
  220. package/rust/config/loader.rs +0 -165
  221. package/rust/config/stats.rs +0 -257
  222. package/rust/core/audio/engine.rs +0 -696
  223. package/rust/core/shared/bank.rs +0 -21
  224. package/rust/core/shared/duration.rs +0 -9
  225. package/rust/core/shared/mod.rs +0 -3
  226. package/rust/core/shared/value.rs +0 -35
  227. package/rust/core/utils/validation.rs +0 -35
  228. package/rust/installer/addon.rs +0 -84
  229. package/rust/installer/bank.rs +0 -62
  230. package/rust/installer/plugin.rs +0 -54
  231. package/rust/installer/utils.rs +0 -56
  232. package/rust/utils/file.rs +0 -38
  233. package/rust/utils/first_usage.rs +0 -83
  234. package/rust/utils/signature.rs +0 -19
  235. package/rust/utils/telemetry.rs +0 -292
  236. package/rust/utils/version.rs +0 -15
  237. /package/rust/{common → web}/api.rs +0 -0
  238. /package/rust/{common → web}/mod.rs +0 -0
  239. /package/rust/{common → web}/sso.rs +0 -0
@@ -0,0 +1,122 @@
1
+ use crate::web::cdn::get_cdn_url;
2
+ use devalang_types::{BankFile, BankInfo};
3
+ use serde::Deserialize;
4
+
5
+ #[derive(Debug, Deserialize)]
6
+ pub struct BankList {
7
+ pub bank: Vec<BankInfo>,
8
+ }
9
+
10
+ #[derive(Debug, Deserialize)]
11
+ pub struct BankVersion {
12
+ pub version: String,
13
+ }
14
+
15
+ pub async fn handle_bank_info_command(
16
+ name: String,
17
+ ) -> Result<BankInfo, Box<dyn std::error::Error>> {
18
+ let cdn_url = get_cdn_url();
19
+ let url = format!("{}/bank/{}/info", cdn_url, name);
20
+
21
+ let response = reqwest::get(&url).await?;
22
+
23
+ if !response.status().is_success() {
24
+ return Err(format!("Failed to fetch bank info: HTTP {}", response.status()).into());
25
+ }
26
+
27
+ let bytes = response.bytes().await?;
28
+
29
+ let parsed: BankInfo = serde_json::from_slice(&bytes)?;
30
+
31
+ println!("📦 Bank Info for '{}':", name);
32
+ println!(" - Name: {}", parsed.name);
33
+ println!(" - Version: {}", parsed.version);
34
+ println!(" - Description: {}", parsed.description);
35
+ println!(" - Author: {}", parsed.author);
36
+
37
+ Ok(parsed)
38
+ }
39
+
40
+ pub async fn fetch_latest_version(
41
+ bank_name: String,
42
+ ) -> Result<BankVersion, Box<dyn std::error::Error>> {
43
+ let cdn_url = get_cdn_url();
44
+ let url = format!("{}/bank/{}/version", cdn_url, bank_name);
45
+
46
+ let response = reqwest::get(url).await?;
47
+
48
+ if !response.status().is_success() {
49
+ return Err(format!("❌ Failed to fetch version: HTTP {}", response.status()).into());
50
+ }
51
+
52
+ let bytes = response.bytes().await?;
53
+
54
+ let version: BankVersion = serde_json::from_slice(&bytes)?;
55
+
56
+ Ok(version)
57
+ }
58
+
59
+ pub async fn list_external_banks() -> Result<Vec<BankInfo>, Box<dyn std::error::Error>> {
60
+ let cdn_url = get_cdn_url();
61
+ let url = format!("{}/bank/list", cdn_url);
62
+
63
+ let response = reqwest::get(url).await?;
64
+
65
+ if !response.status().is_success() {
66
+ return Err(format!("❌ Failed to fetch bank list: HTTP {}", response.status()).into());
67
+ }
68
+
69
+ let bytes = response.bytes().await?;
70
+
71
+ let parsed: BankList = serde_json::from_slice(&bytes)?;
72
+
73
+ Ok(parsed.bank)
74
+ }
75
+
76
+ pub async fn list_installed_banks() -> Result<Vec<BankFile>, String> {
77
+ let deva_dir = devalang_utils::path::ensure_deva_dir()?;
78
+ let bank_dir = deva_dir.join("banks");
79
+
80
+ let mut banks = Vec::new();
81
+
82
+ if !bank_dir.exists() {
83
+ return Ok(banks); // No banks installed
84
+ }
85
+
86
+ let config_path = devalang_utils::path::get_devalang_config_path()?;
87
+ let config = crate::config::ops::load_config(Some(&config_path))
88
+ .ok_or_else(|| format!("Failed to load config from '{}'", config_path.display()))?;
89
+
90
+ let config_banks = config.banks.clone();
91
+
92
+ if let Some(banks_in_toml) = config_banks {
93
+ for bank in banks_in_toml {
94
+ let bank_name = bank
95
+ .path
96
+ .strip_prefix("devalang://bank/")
97
+ .unwrap_or(&bank.path)
98
+ .to_string();
99
+
100
+ let bank_path = bank_dir.join(&bank_name);
101
+ if bank_path.exists() {
102
+ let bank_info_path = bank_path.join("bank.toml");
103
+
104
+ if bank_info_path.exists() {
105
+ let content = std::fs::read_to_string(&bank_info_path)
106
+ .map_err(|e| format!("Failed to read bank info: {}", e))?;
107
+
108
+ match toml::from_str::<BankFile>(&content) {
109
+ Ok(bank_info) => banks.push(bank_info),
110
+ Err(_err) => {
111
+ eprintln!("❌ Error parsing bank info for '{}'", bank_name);
112
+ }
113
+ }
114
+ } else {
115
+ eprintln!("❌ Bank info file not found for '{}'", bank_name);
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ Ok(banks)
122
+ }
@@ -0,0 +1,275 @@
1
+ use crate::cli::install::bank::install_bank;
2
+ use crate::config::ops::load_config;
3
+ use devalang_types::{BankFile, BankInfo};
4
+ use devalang_utils::path as path_utils;
5
+ use std::fs;
6
+
7
+ pub async fn handle_update_bank_command(name: Option<String>) -> Result<(), String> {
8
+ let deva_dir = path_utils::ensure_deva_dir()?;
9
+ let bank_dir = deva_dir.join("banks");
10
+
11
+ if !bank_dir.exists() {
12
+ fs::create_dir_all(bank_dir.clone())
13
+ .map_err(|e| format!("Failed to create bank directory: {}", e))?;
14
+ }
15
+
16
+ if let Some(name) = name {
17
+ let bank_path = bank_dir.join(&name);
18
+ if !bank_path.exists() {
19
+ return Err(format!("Bank '{}' is not installed", name));
20
+ }
21
+
22
+ // Update specific bank
23
+ let latest_version = match crate::cli::bank::api::fetch_latest_version(name.clone()).await {
24
+ Ok(version) => version,
25
+ Err(err) => {
26
+ eprintln!("❌ Error fetching latest version for '{}': {}", name, err);
27
+ return Err(format!("Failed to fetch latest version for '{}'", name));
28
+ }
29
+ };
30
+
31
+ let local_bank_info_path = bank_path.join("bank.toml");
32
+ let local_version = match fs::read_to_string(&local_bank_info_path)
33
+ .ok()
34
+ .and_then(|content| toml::from_str::<BankFile>(&content).ok())
35
+ .map(|bf| bf.bank.version)
36
+ {
37
+ Some(version) => version,
38
+ None => {
39
+ eprintln!(
40
+ "⚠️ Unable to read local version for '{}', forcing reinstall...",
41
+ name
42
+ );
43
+ "".to_string() // Force update
44
+ }
45
+ };
46
+
47
+ if local_version != latest_version.version {
48
+ if let Err(e) = update_bank(&name, &latest_version.version).await {
49
+ eprintln!("❌ Error updating bank '{}': {}", name, e);
50
+ } else {
51
+ println!(
52
+ "✅ Bank '{}' updated to version '{}'",
53
+ name, latest_version.version
54
+ );
55
+ }
56
+ } else {
57
+ println!(
58
+ "Bank '{}' is already up-to-date (version {})",
59
+ name, latest_version.version
60
+ );
61
+
62
+ // Verify if the bank directory exists
63
+ if !bank_path.exists() {
64
+ eprintln!(
65
+ "❌ Bank directory for '{}' does not exist, reinstalling...",
66
+ name
67
+ );
68
+ if let Err(e) = install_bank(&name, &deva_dir).await {
69
+ eprintln!("❌ Error reinstalling bank '{}': {}", name, e);
70
+ } else {
71
+ println!("✅ Bank '{}' reinstalled successfully!", name);
72
+ }
73
+ }
74
+ }
75
+ } else {
76
+ // Update all banks
77
+ let config_path = path_utils::get_devalang_config_path()?;
78
+ let config = load_config(Some(&config_path))
79
+ .ok_or_else(|| format!("Failed to load config from '{}'", config_path.display()))?;
80
+
81
+ let config_banks = config.banks.clone();
82
+
83
+ if let Some(banks) = config_banks {
84
+ for bank in banks {
85
+ let bank_name = bank
86
+ .path
87
+ .strip_prefix("devalang://bank/")
88
+ .unwrap_or(&bank.path)
89
+ .to_string();
90
+
91
+ let latest_version =
92
+ match crate::cli::bank::api::fetch_latest_version(bank_name.clone()).await {
93
+ Ok(version) => version,
94
+ Err(err) => {
95
+ eprintln!(
96
+ "❌ Error fetching latest version for '{}': {}",
97
+ bank_name, err
98
+ );
99
+ continue;
100
+ }
101
+ };
102
+
103
+ if let Some(local_bank_version) = bank.version {
104
+ if latest_version.version != local_bank_version {
105
+ if let Err(e) = update_bank(&bank_name, &latest_version.version).await {
106
+ eprintln!("❌ Error updating bank '{}': {}", bank_name, e);
107
+ } else {
108
+ println!(
109
+ "✅ Bank '{}' updated to version '{}'",
110
+ bank_name, latest_version.version
111
+ );
112
+ }
113
+ } else {
114
+ println!(
115
+ "Bank '{}' is already up-to-date (version {})",
116
+ bank_name, local_bank_version
117
+ );
118
+
119
+ // Verify if the bank directory exists
120
+ let bank_path = bank_dir.join(&bank_name);
121
+
122
+ if !bank_path.exists() {
123
+ eprintln!(
124
+ "❌ Bank directory for '{}' does not exist, reinstalling...",
125
+ bank_name
126
+ );
127
+ if let Err(e) = install_bank(&bank_name, &deva_dir).await {
128
+ eprintln!("❌ Error reinstalling bank '{}': {}", bank_name, e);
129
+ } else {
130
+ println!("✅ Bank '{}' reinstalled successfully!", bank_name);
131
+ }
132
+ }
133
+ }
134
+ } else {
135
+ // If the bank version is not specified in the config, install it
136
+ if let Err(e) = install_bank(&bank_name, &deva_dir).await {
137
+ eprintln!("❌ Error installing bank '{}': {}", bank_name, e);
138
+ } else {
139
+ println!("✅ Bank '{}' installed successfully!", bank_name);
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+
146
+ Ok(())
147
+ }
148
+
149
+ async fn update_bank(bank_name: &str, _latest_version: &str) -> Result<(), String> {
150
+ let deva_dir = path_utils::ensure_deva_dir()?;
151
+
152
+ // First, delete the existing bank directory
153
+ let bank_dir = deva_dir.join("banks").join(bank_name);
154
+
155
+ if bank_dir.exists() {
156
+ std::fs::remove_dir_all(&bank_dir).unwrap_or_else(|_| {
157
+ eprintln!(
158
+ "⚠️ Failed to remove old bank directory for '{}', aborting !",
159
+ bank_name
160
+ );
161
+ std::process::exit(1);
162
+ });
163
+ }
164
+
165
+ // Now, install the new version
166
+ if let Err(e) = install_bank(bank_name, &deva_dir).await {
167
+ eprintln!("❌ Error installing bank '{}': {}", bank_name, e);
168
+ } else {
169
+ println!("✅ Bank '{}' installed successfully!", bank_name);
170
+ }
171
+
172
+ let config_path = path_utils::get_devalang_config_path()?;
173
+ let _config = load_config(Some(&config_path))
174
+ .ok_or_else(|| format!("Failed to load config from '{}'", config_path.display()))?;
175
+
176
+ // TODO Update the bank version in the config
177
+
178
+ Ok(())
179
+ }
180
+
181
+ pub async fn handle_remove_bank_command(name: String) -> Result<(), String> {
182
+ let deva_dir = path_utils::ensure_deva_dir()?;
183
+ let bank_dir = deva_dir.join("banks");
184
+
185
+ let bank_path = bank_dir.join(&name);
186
+
187
+ if !bank_path.exists() {
188
+ return Err(format!("Bank '{}' is not installed", name));
189
+ }
190
+
191
+ std::fs::remove_dir_all(&bank_path).map_err(|e| format!("Failed to remove bank: {}", e))?;
192
+
193
+ // Remove the bank from the config
194
+ let config_path = path_utils::get_devalang_config_path()?;
195
+ let _config = load_config(Some(&config_path))
196
+ .ok_or_else(|| format!("Failed to load config from '{}'", config_path.display()))?;
197
+
198
+ // TODO Remove the bank from the config
199
+
200
+ println!("✅ Bank '{}' removed successfully", name);
201
+
202
+ Ok(())
203
+ }
204
+
205
+ pub async fn handle_bank_available_command() -> Result<(), String> {
206
+ let bank_list = match crate::cli::bank::api::list_external_banks().await {
207
+ Ok(list) => list,
208
+ Err(_err) => {
209
+ eprintln!("❌ Error fetching bank list");
210
+ return Err("Failed to fetch bank list".into());
211
+ }
212
+ };
213
+
214
+ println!("Available banks for current project :");
215
+ println!(" ");
216
+
217
+ for bank in bank_list {
218
+ println!("📦 {}", bank.name);
219
+ println!(" - Version: {}", bank.version);
220
+ println!(" - Description: {}", bank.description);
221
+ println!(" - Author: {}", bank.author);
222
+ println!(" ");
223
+ }
224
+
225
+ Ok(())
226
+ }
227
+
228
+ pub async fn handle_bank_info_command(
229
+ name: String,
230
+ ) -> Result<BankInfo, Box<dyn std::error::Error>> {
231
+ crate::cli::bank::api::handle_bank_info_command(name).await
232
+ }
233
+
234
+ pub async fn handle_bank_list_command() -> Result<(), String> {
235
+ let bank_list = match crate::cli::bank::api::list_installed_banks().await {
236
+ Ok(list) => list,
237
+ Err(_err) => {
238
+ eprintln!("❌ Error fetching bank list");
239
+ return Err("Failed to fetch bank list".into());
240
+ }
241
+ };
242
+
243
+ println!("Installed banks for current project :");
244
+
245
+ for bank_toml in bank_list {
246
+ let latest_version =
247
+ match crate::cli::bank::api::fetch_latest_version(bank_toml.bank.name.clone()).await {
248
+ Ok(version) => version,
249
+ Err(_err) => {
250
+ eprintln!(
251
+ "❌ Error fetching latest version for '{}'",
252
+ bank_toml.bank.name
253
+ );
254
+ continue;
255
+ }
256
+ };
257
+
258
+ let is_latest = if latest_version.version == bank_toml.bank.version {
259
+ "✅"
260
+ } else {
261
+ "❗"
262
+ };
263
+
264
+ println!(" ");
265
+ println!("📦 {}", bank_toml.bank.name);
266
+ println!(
267
+ " - Version: v{} {} (latest: v{})",
268
+ bank_toml.bank.version, is_latest, latest_version.version
269
+ );
270
+ println!(" - Description: {}", bank_toml.bank.description);
271
+ println!(" - Author: {}", bank_toml.bank.author);
272
+ }
273
+
274
+ Ok(())
275
+ }
@@ -0,0 +1,29 @@
1
+ use devalang_types::BankInfo;
2
+ use serde::Deserialize;
3
+
4
+ pub mod api;
5
+ pub mod commands;
6
+
7
+ #[derive(Debug, Deserialize)]
8
+ pub struct BankList {
9
+ pub bank: Vec<BankInfo>,
10
+ }
11
+
12
+ #[derive(Debug, Deserialize)]
13
+ pub struct BankInfoFetched {
14
+ pub name: String,
15
+ pub version: String,
16
+ pub description: String,
17
+ pub author: String,
18
+ pub latest_version: String,
19
+ }
20
+
21
+ #[derive(Debug, Deserialize)]
22
+ pub struct BankVersion {
23
+ pub version: String,
24
+ }
25
+
26
+ pub use commands::{
27
+ handle_bank_available_command, handle_bank_info_command, handle_bank_list_command,
28
+ handle_remove_bank_command, handle_update_bank_command,
29
+ };
@@ -0,0 +1,107 @@
1
+ use crate::{config::driver::ProjectConfig, core::utils::path::find_entry_file};
2
+ use devalang_utils::logger::{LogLevel, Logger};
3
+ use devalang_utils::watcher::watch_directory;
4
+
5
+ #[cfg(feature = "cli")]
6
+ pub fn handle_build_command(
7
+ config: Option<ProjectConfig>,
8
+ entry: Option<String>,
9
+ output: Option<String>,
10
+ watch: bool,
11
+ debug: bool,
12
+ compress: bool,
13
+ ) -> Result<(), String> {
14
+ let fetched_entry = if entry.is_none() {
15
+ config
16
+ .as_ref()
17
+ .and_then(|c| c.defaults.entry.clone())
18
+ .unwrap_or_default()
19
+ } else {
20
+ entry.clone().unwrap_or_default()
21
+ };
22
+
23
+ let fetched_output = if output.is_none() {
24
+ config
25
+ .as_ref()
26
+ .and_then(|c| c.defaults.output.clone())
27
+ .unwrap_or_default()
28
+ } else {
29
+ output.clone().unwrap_or_default()
30
+ };
31
+
32
+ let fetched_watch = if watch {
33
+ watch
34
+ } else {
35
+ config
36
+ .as_ref()
37
+ .and_then(|c| c.defaults.watch)
38
+ .unwrap_or(false)
39
+ };
40
+
41
+ let logger = Logger::new();
42
+
43
+ if fetched_entry.is_empty() {
44
+ logger.log_message(
45
+ LogLevel::Error,
46
+ "Entry path is not specified. Please provide a valid entry path.",
47
+ );
48
+ return Err("missing entry path".to_string());
49
+ }
50
+ if fetched_output.is_empty() {
51
+ logger.log_message(
52
+ LogLevel::Error,
53
+ "Output directory is not specified. Please provide a valid output directory.",
54
+ );
55
+ return Err("missing output directory".to_string());
56
+ }
57
+
58
+ let entry_file = match find_entry_file(&fetched_entry) {
59
+ Some(p) => p,
60
+ None => {
61
+ logger.log_message(
62
+ LogLevel::Error,
63
+ &format!("❌ index.deva not found in directory: {}", fetched_entry),
64
+ );
65
+ return Err("index.deva not found".to_string());
66
+ }
67
+ };
68
+
69
+ // SECTION Begin build
70
+ if fetched_watch {
71
+ let _ = crate::cli::build::process::process_build(
72
+ entry_file.clone(),
73
+ fetched_output.clone(),
74
+ debug,
75
+ compress,
76
+ );
77
+
78
+ logger.log_message(
79
+ LogLevel::Watcher,
80
+ &format!("Watching for changes in '{}'...", fetched_entry),
81
+ );
82
+
83
+ watch_directory(entry_file.clone(), move || {
84
+ logger.log_message(LogLevel::Watcher, "Detected changes, re-building...");
85
+
86
+ let _ = crate::cli::build::process::process_build(
87
+ entry_file.clone(),
88
+ fetched_output.clone(),
89
+ debug,
90
+ compress,
91
+ );
92
+ })
93
+ .unwrap();
94
+ } else {
95
+ let res = crate::cli::build::process::process_build(
96
+ entry_file,
97
+ fetched_output,
98
+ debug,
99
+ compress,
100
+ );
101
+ if let Err(e) = res {
102
+ return Err(e);
103
+ }
104
+ }
105
+
106
+ Ok(())
107
+ }
@@ -0,0 +1,2 @@
1
+ pub mod commands;
2
+ pub mod process;
@@ -0,0 +1,146 @@
1
+ use crate::core::{
2
+ builder::Builder,
3
+ debugger::{
4
+ lexer::write_lexer_log_file,
5
+ module::{write_module_function_log_file, write_module_variable_log_file},
6
+ preprocessor::write_preprocessor_log_file,
7
+ store::{write_function_log_file, write_variables_log_file},
8
+ },
9
+ preprocessor::loader::ModuleLoader,
10
+ store::global::GlobalStore,
11
+ utils::path::normalize_path,
12
+ };
13
+ use devalang_utils::{
14
+ logger::{LogLevel, Logger},
15
+ spinner::start_spinner,
16
+ };
17
+
18
+ pub struct BuildStatsInput {
19
+ pub statements_by_module:
20
+ std::collections::HashMap<String, Vec<crate::core::parser::statement::Statement>>,
21
+ pub global_store: crate::core::store::global::GlobalStore,
22
+ }
23
+
24
+ pub fn process_build(
25
+ entry: String,
26
+ output: String,
27
+ debug: bool,
28
+ compress: bool,
29
+ ) -> Result<BuildStatsInput, String> {
30
+ let spinner = start_spinner("Building...");
31
+
32
+ let duration = std::time::Instant::now();
33
+
34
+ let normalized_entry_file = normalize_path(&entry);
35
+ let normalized_output_dir = normalize_path(&output);
36
+
37
+ let mut global_store = GlobalStore::new();
38
+ let module_loader = ModuleLoader::new(&normalized_entry_file, &normalized_output_dir);
39
+
40
+ // SECTION Load
41
+ // NOTE: We use modules in the build command, so we need to load them
42
+ let (modules_tokens, modules_statements) = module_loader.load_all_modules(&mut global_store);
43
+
44
+ // SECTION Write logs
45
+ if debug {
46
+ for (module_path, module) in global_store.modules.clone() {
47
+ write_module_variable_log_file(
48
+ &normalized_output_dir,
49
+ &module_path,
50
+ &module.variable_table,
51
+ );
52
+ write_module_function_log_file(
53
+ &normalized_output_dir,
54
+ &module_path,
55
+ &module.function_table,
56
+ );
57
+ }
58
+
59
+ write_lexer_log_file(
60
+ &normalized_output_dir,
61
+ "lexer_tokens.log",
62
+ modules_tokens.clone(),
63
+ );
64
+ write_preprocessor_log_file(
65
+ &normalized_output_dir,
66
+ "resolved_statements.log",
67
+ modules_statements.clone(),
68
+ );
69
+ write_variables_log_file(
70
+ &normalized_output_dir,
71
+ "global_variables.log",
72
+ global_store.variables.clone(),
73
+ );
74
+ write_function_log_file(
75
+ &normalized_output_dir,
76
+ "global_functions.log",
77
+ global_store.functions.clone(),
78
+ );
79
+ }
80
+
81
+ // SECTION Detect build-time errors prior to building
82
+ let all_errors = crate::core::error::collect_all_errors_with_modules(&modules_statements);
83
+ let (warnings, criticals) = crate::core::error::partition_errors(all_errors);
84
+ crate::core::error::log_errors_with_stack("Build", &warnings, &criticals);
85
+ if !criticals.is_empty() {
86
+ spinner.finish_and_clear();
87
+ return Err(format!(
88
+ "build failed with {} critical error(s): {}",
89
+ criticals.len(),
90
+ criticals[0].message
91
+ ));
92
+ }
93
+
94
+ // SECTION Building AST and Audio
95
+ let builder = Builder::new();
96
+ builder.build_ast(&modules_statements, &normalized_output_dir, compress);
97
+ builder.build_audio(
98
+ &modules_statements,
99
+ &normalized_output_dir,
100
+ &mut global_store,
101
+ );
102
+
103
+ // SECTION Logging
104
+ let logger = Logger::new();
105
+
106
+ if debug {
107
+ let modules_loaded = global_store.modules.keys().collect::<Vec<_>>();
108
+ let global_variables_loaded = global_store.variables.variables.keys().collect::<Vec<_>>();
109
+ let global_functions_loaded = global_store.functions.functions.keys().collect::<Vec<_>>();
110
+
111
+ logger.log_message_with_trace(
112
+ LogLevel::Debug,
113
+ &format!("Modules loaded: {}", global_store.modules.len()),
114
+ modules_loaded.iter().map(|s| s.as_str()).collect(),
115
+ );
116
+ logger.log_message_with_trace(
117
+ LogLevel::Debug,
118
+ &format!(
119
+ "Global variables: {}",
120
+ global_store.variables.variables.len()
121
+ ),
122
+ global_variables_loaded.iter().map(|s| s.as_str()).collect(),
123
+ );
124
+ logger.log_message_with_trace(
125
+ LogLevel::Debug,
126
+ &format!(
127
+ "Global functions: {}",
128
+ global_store.functions.functions.len()
129
+ ),
130
+ global_functions_loaded.iter().map(|s| s.as_str()).collect(),
131
+ );
132
+ }
133
+
134
+ let success_message = format!(
135
+ "Build completed successfully in {:.2?}. Output files written to: '{}'",
136
+ duration.elapsed(),
137
+ normalized_output_dir
138
+ );
139
+
140
+ spinner.finish_and_clear();
141
+ logger.log_message(LogLevel::Success, &success_message);
142
+ Ok(BuildStatsInput {
143
+ statements_by_module: modules_statements,
144
+ global_store,
145
+ })
146
+ }