@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.
Files changed (220) hide show
  1. package/.devalang +9 -10
  2. package/Cargo.toml +5 -4
  3. package/README.md +7 -5
  4. package/docs/CHANGELOG.md +42 -0
  5. package/docs/ROADMAP.md +5 -1
  6. package/docs/TODO.md +3 -14
  7. package/examples/bus.deva +10 -0
  8. package/examples/effect.deva +2 -0
  9. package/examples/filter.deva +11 -0
  10. package/examples/lfo.deva +9 -0
  11. package/examples/synth.deva +11 -1
  12. package/examples/synth_types.deva +17 -0
  13. package/out-tsc/core/functions/index.d.ts +5 -0
  14. package/out-tsc/core/functions/index.js +11 -0
  15. package/out-tsc/pkg/devalang_core.d.ts +2 -0
  16. package/out-tsc/pkg/devalang_core.js +17 -2
  17. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +8 -7
  18. package/package.json +1 -1
  19. package/project-version.json +3 -3
  20. package/rust/cli/bank/api.rs +122 -122
  21. package/rust/cli/bank/commands.rs +33 -2
  22. package/rust/cli/bank/mod.rs +29 -29
  23. package/rust/cli/build/commands.rs +53 -3
  24. package/rust/cli/build/mod.rs +2 -2
  25. package/rust/cli/build/process.rs +26 -7
  26. package/rust/cli/check/mod.rs +2 -2
  27. package/rust/cli/discover/commands.rs +253 -253
  28. package/rust/cli/discover/config.rs +111 -111
  29. package/rust/cli/discover/fs.rs +19 -19
  30. package/rust/cli/discover/install.rs +103 -103
  31. package/rust/cli/discover/metadata.rs +48 -48
  32. package/rust/cli/discover/mod.rs +5 -5
  33. package/rust/cli/install/addon.rs +118 -118
  34. package/rust/cli/install/bank.rs +22 -3
  35. package/rust/cli/install/commands.rs +35 -35
  36. package/rust/cli/install/mod.rs +4 -4
  37. package/rust/cli/install/plugin.rs +80 -61
  38. package/rust/cli/login/commands.rs +124 -124
  39. package/rust/cli/mod.rs +12 -12
  40. package/rust/cli/parser.rs +46 -1
  41. package/rust/cli/play/commands.rs +71 -20
  42. package/rust/cli/play/mod.rs +5 -5
  43. package/rust/cli/play/process.rs +14 -5
  44. package/rust/cli/play/realtime.rs +91 -91
  45. package/rust/cli/telemetry/commands.rs +22 -22
  46. package/rust/cli/telemetry/event_creator.rs +80 -80
  47. package/rust/cli/telemetry/mod.rs +3 -3
  48. package/rust/cli/telemetry/send.rs +51 -51
  49. package/rust/cli/template/commands.rs +69 -69
  50. package/rust/config/driver.rs +112 -103
  51. package/rust/config/mod.rs +3 -3
  52. package/rust/config/ops.rs +26 -26
  53. package/rust/config/settings.rs +101 -101
  54. package/rust/core/audio/engine/driver.rs +220 -0
  55. package/rust/core/audio/engine/export.rs +169 -0
  56. package/rust/core/audio/engine/helpers.rs +178 -170
  57. package/rust/core/audio/engine/mod.rs +51 -2
  58. package/rust/core/audio/engine/notes/dsp.rs +85 -0
  59. package/rust/core/audio/engine/notes/mod.rs +44 -0
  60. package/rust/core/audio/engine/notes/params.rs +294 -0
  61. package/rust/core/audio/engine/sample/insert.rs +199 -0
  62. package/rust/core/audio/engine/sample/mod.rs +40 -0
  63. package/rust/core/audio/engine/sample/padding.rs +170 -0
  64. package/rust/core/audio/evaluator/condition.rs +61 -0
  65. package/rust/core/audio/evaluator/mod.rs +9 -0
  66. package/rust/core/audio/{evaluator.rs → evaluator/numeric.rs} +1 -159
  67. package/rust/core/audio/evaluator/rhs.rs +16 -0
  68. package/rust/core/audio/evaluator/string_expr.rs +94 -0
  69. package/rust/core/audio/interpreter/driver.rs +55 -23
  70. package/rust/core/audio/interpreter/mod.rs +1 -13
  71. package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +175 -0
  72. package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +384 -0
  73. package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +2 -0
  74. package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +316 -0
  75. package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -0
  76. package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -0
  77. package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -0
  78. package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -0
  79. package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -0
  80. package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -0
  81. package/rust/core/audio/interpreter/{automate.rs → statements/automate.rs} +16 -18
  82. package/rust/core/audio/interpreter/{call.rs → statements/call.rs} +5 -4
  83. package/rust/core/audio/interpreter/{condition.rs → statements/condition.rs} +2 -1
  84. package/rust/core/audio/interpreter/{function.rs → statements/function.rs} +2 -4
  85. package/rust/core/audio/interpreter/{let_.rs → statements/let_.rs} +2 -4
  86. package/rust/core/audio/interpreter/{load.rs → statements/load.rs} +2 -4
  87. package/rust/core/audio/interpreter/{loop_.rs → statements/loop_.rs} +2 -1
  88. package/rust/core/audio/interpreter/statements/mod.rs +12 -0
  89. package/rust/core/audio/interpreter/{sleep.rs → statements/sleep.rs} +28 -28
  90. package/rust/core/audio/interpreter/{spawn.rs → statements/spawn.rs} +3 -2
  91. package/rust/core/audio/interpreter/{tempo.rs → statements/tempo.rs} +40 -40
  92. package/rust/core/audio/interpreter/{trigger.rs → statements/trigger.rs} +1 -1
  93. package/rust/core/audio/loader/trigger.rs +2 -1
  94. package/rust/core/audio/mod.rs +6 -7
  95. package/rust/core/audio/player.rs +70 -70
  96. package/rust/core/audio/special/easing.rs +189 -189
  97. package/rust/core/audio/special/env.rs +45 -45
  98. package/rust/core/audio/special/math.rs +134 -134
  99. package/rust/core/audio/special/mod.rs +9 -9
  100. package/rust/core/audio/special/modulator.rs +143 -143
  101. package/rust/core/builder/mod.rs +45 -2
  102. package/rust/core/debugger/lexer.rs +27 -27
  103. package/rust/core/debugger/{module.rs → logs.rs} +3 -6
  104. package/rust/core/debugger/mod.rs +30 -30
  105. package/rust/core/debugger/preprocessor.rs +27 -27
  106. package/rust/core/debugger/store.rs +2 -4
  107. package/rust/core/error/mod.rs +269 -269
  108. package/rust/core/lexer/driver.rs +59 -61
  109. package/rust/core/lexer/handler/arrow.rs +82 -82
  110. package/rust/core/lexer/handler/at.rs +21 -21
  111. package/rust/core/lexer/handler/brace.rs +41 -41
  112. package/rust/core/lexer/handler/colon.rs +21 -21
  113. package/rust/core/lexer/handler/comment.rs +30 -30
  114. package/rust/core/lexer/handler/dot.rs +21 -21
  115. package/rust/core/lexer/handler/driver.rs +337 -337
  116. package/rust/core/lexer/handler/identifier.rs +47 -47
  117. package/rust/core/lexer/handler/indent.rs +66 -66
  118. package/rust/core/lexer/handler/mod.rs +15 -15
  119. package/rust/core/lexer/handler/newline.rs +23 -23
  120. package/rust/core/lexer/handler/number.rs +31 -31
  121. package/rust/core/lexer/handler/operator.rs +46 -46
  122. package/rust/core/lexer/handler/parenthesis.rs +41 -41
  123. package/rust/core/lexer/handler/slash.rs +21 -21
  124. package/rust/core/lexer/handler/string.rs +63 -63
  125. package/rust/core/lexer/mod.rs +3 -3
  126. package/rust/core/mod.rs +0 -1
  127. package/rust/core/parser/driver/block.rs +111 -0
  128. package/rust/core/parser/driver/cursor.rs +82 -0
  129. package/rust/core/parser/driver/driver_impl.rs +139 -0
  130. package/rust/core/parser/driver/mod.rs +6 -0
  131. package/rust/core/parser/driver/parse_array.rs +120 -0
  132. package/rust/core/parser/driver/parse_map.rs +223 -0
  133. package/rust/core/parser/driver/parser.rs +160 -0
  134. package/rust/core/parser/handler/arrow_call.rs +28 -4
  135. package/rust/core/parser/handler/at.rs +279 -279
  136. package/rust/core/parser/handler/bank.rs +104 -104
  137. package/rust/core/parser/handler/condition.rs +83 -83
  138. package/rust/core/parser/handler/dot.rs +148 -148
  139. package/rust/core/parser/handler/identifier/automate.rs +254 -254
  140. package/rust/core/parser/handler/identifier/call.rs +91 -91
  141. package/rust/core/parser/handler/identifier/emit.rs +70 -70
  142. package/rust/core/parser/handler/identifier/function.rs +113 -113
  143. package/rust/core/parser/handler/identifier/group.rs +89 -89
  144. package/rust/core/parser/handler/identifier/let_.rs +173 -173
  145. package/rust/core/parser/handler/identifier/mod.rs +55 -55
  146. package/rust/core/parser/handler/identifier/on.rs +107 -107
  147. package/rust/core/parser/handler/identifier/print.rs +49 -49
  148. package/rust/core/parser/handler/identifier/sleep.rs +96 -43
  149. package/rust/core/parser/handler/identifier/spawn.rs +91 -91
  150. package/rust/core/parser/handler/identifier/synth.rs +135 -135
  151. package/rust/core/parser/handler/loop_.rs +194 -194
  152. package/rust/core/parser/handler/mod.rs +9 -9
  153. package/rust/core/parser/handler/pattern.rs +1 -1
  154. package/rust/core/parser/handler/tempo.rs +105 -57
  155. package/rust/core/parser/statement.rs +10 -11
  156. package/rust/core/plugin/loader.rs +1 -1
  157. package/rust/core/plugin/mod.rs +2 -2
  158. package/rust/core/plugin/runner/mod.rs +11 -0
  159. package/rust/core/plugin/{runner.rs → runner/non_wasm.rs} +297 -347
  160. package/rust/core/plugin/runner/wasm32.rs +43 -0
  161. package/rust/core/preprocessor/loader/inject.rs +278 -0
  162. package/rust/core/preprocessor/loader/loader_helpers.rs +110 -0
  163. package/rust/core/preprocessor/loader/mod.rs +235 -0
  164. package/rust/core/preprocessor/module.rs +2 -7
  165. package/rust/core/preprocessor/{processor.rs → processor/handlers.rs} +6 -13
  166. package/rust/core/preprocessor/processor/mod.rs +1 -0
  167. package/rust/core/preprocessor/resolver/bank.rs +49 -49
  168. package/rust/core/preprocessor/resolver/call.rs +124 -124
  169. package/rust/core/preprocessor/resolver/condition.rs +95 -95
  170. package/rust/core/preprocessor/resolver/driver.rs +324 -324
  171. package/rust/core/preprocessor/resolver/function.rs +2 -2
  172. package/rust/core/preprocessor/resolver/group.rs +46 -18
  173. package/rust/core/preprocessor/resolver/let_.rs +32 -32
  174. package/rust/core/preprocessor/resolver/loop_.rs +318 -318
  175. package/rust/core/preprocessor/resolver/mod.rs +16 -16
  176. package/rust/core/preprocessor/resolver/pattern.rs +83 -83
  177. package/rust/core/preprocessor/resolver/spawn.rs +99 -99
  178. package/rust/core/preprocessor/resolver/synth.rs +54 -54
  179. package/rust/core/preprocessor/resolver/tempo.rs +48 -48
  180. package/rust/core/preprocessor/resolver/trigger.rs +116 -116
  181. package/rust/core/preprocessor/resolver/value.rs +176 -176
  182. package/rust/core/store/global.rs +2 -6
  183. package/rust/core/store/mod.rs +1 -5
  184. package/rust/lib.rs +18 -3
  185. package/rust/main.rs +27 -3
  186. package/rust/types/Cargo.toml +1 -1
  187. package/rust/types/src/addons.rs +55 -55
  188. package/rust/types/src/config.rs +84 -74
  189. package/rust/types/src/lib.rs +15 -12
  190. package/rust/types/src/plugin.rs +20 -0
  191. package/rust/types/src/store.rs +139 -0
  192. package/rust/types/src/telemetry.rs +85 -85
  193. package/rust/utils/Cargo.toml +2 -2
  194. package/rust/utils/src/file.rs +94 -94
  195. package/rust/utils/src/first_usage.rs +97 -97
  196. package/rust/utils/src/lib.rs +9 -9
  197. package/rust/utils/src/logger.rs +200 -200
  198. package/rust/utils/src/path.rs +129 -88
  199. package/rust/utils/src/signature.rs +41 -41
  200. package/rust/utils/src/spinner.rs +20 -20
  201. package/rust/utils/src/version.rs +27 -27
  202. package/rust/utils/src/watcher.rs +46 -46
  203. package/rust/web/api.rs +5 -5
  204. package/rust/web/cdn.rs +34 -34
  205. package/rust/web/mod.rs +3 -3
  206. package/tests/integration.rs +21 -21
  207. package/typescript/core/functions/index.ts +11 -0
  208. package/typescript/pkg/devalang_core.ts +20 -4
  209. package/rust/core/audio/engine/sample.rs +0 -366
  210. package/rust/core/audio/engine/synth.rs +0 -325
  211. package/rust/core/audio/interpreter/arrow_call.rs +0 -311
  212. package/rust/core/audio/renderer.rs +0 -54
  213. package/rust/core/parser/driver.rs +0 -584
  214. package/rust/core/preprocessor/loader.rs +0 -637
  215. package/rust/core/store/export.rs +0 -28
  216. package/rust/core/store/function.rs +0 -40
  217. package/rust/core/store/import.rs +0 -28
  218. package/rust/core/store/variable.rs +0 -51
  219. package/rust/core/utils/mod.rs +0 -1
  220. package/rust/core/utils/path.rs +0 -37
@@ -1,118 +1,118 @@
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::LogLevel;
26
- use devalang_utils::logger::Logger;
27
-
28
- // Require an authenticated user for addon installation: token must be present and non-empty
29
- let stored_token_opt = get_user_config().and_then(|cfg| {
30
- let t = cfg.session.clone();
31
- if t.trim().is_empty() { None } else { Some(t) }
32
- });
33
-
34
- if stored_token_opt.is_none() {
35
- let logger = Logger::new();
36
- let msg = "Authentication required — run `devalang login` to authenticate";
37
- logger.log_message(LogLevel::Error, msg);
38
- return Err("Authentication required: run 'devalang login' to authenticate".to_string());
39
- }
40
-
41
- let request_url = if let Some(token) = &stored_token_opt {
42
- format!(
43
- "{}/v1/assets/url?type={}&slug={}&token={}",
44
- api_url,
45
- match addon_type {
46
- AddonType::Bank => "bank",
47
- AddonType::Plugin => "plugin",
48
- AddonType::Preset => "preset",
49
- AddonType::Template => "template",
50
- },
51
- slug,
52
- token
53
- )
54
- } else {
55
- format!(
56
- "{}/v1/assets/url?type={}&slug={}",
57
- api_url,
58
- match addon_type {
59
- AddonType::Bank => "bank",
60
- AddonType::Plugin => "plugin",
61
- AddonType::Preset => "preset",
62
- AddonType::Template => "template",
63
- },
64
- slug
65
- )
66
- };
67
-
68
- let mut headers = reqwest::header::HeaderMap::new();
69
- if let Some(token) = stored_token_opt {
70
- headers.insert(
71
- "Authorization",
72
- format!("Bearer {}", token).parse().unwrap(),
73
- );
74
- }
75
-
76
- let client: reqwest::Client = reqwest::Client::builder()
77
- .default_headers(headers)
78
- .build()
79
- .map_err(|_| "Failed to build HTTP client".to_string())?;
80
-
81
- let resp = client
82
- .get(&request_url)
83
- .send()
84
- .await
85
- .map_err(|e| format!("Failed to receive response: {}", e))?;
86
-
87
- let status = resp.status();
88
- let body_text = resp
89
- .text()
90
- .await
91
- .map_err(|e| format!("Failed to read response body: {}", e))?;
92
-
93
- // Try to parse JSON; if parsing fails, return body for diagnostics
94
- let json: serde_json::Value = match serde_json::from_str(&body_text) {
95
- Ok(v) => v,
96
- Err(_) => {
97
- return Err(format!(
98
- "Invalid JSON response (status {}): {}",
99
- status, body_text
100
- ));
101
- }
102
- };
103
-
104
- // Extract payload.url safely
105
- let signed_url_opt = json
106
- .get("payload")
107
- .and_then(|p| p.get("url"))
108
- .and_then(|u| u.as_str())
109
- .map(|s| s.to_string());
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::{
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::LogLevel;
26
+ use devalang_utils::logger::Logger;
27
+
28
+ // Require an authenticated user for addon installation: token must be present and non-empty
29
+ let stored_token_opt = get_user_config().and_then(|cfg| {
30
+ let t = cfg.session.clone();
31
+ if t.trim().is_empty() { None } else { Some(t) }
32
+ });
33
+
34
+ if stored_token_opt.is_none() {
35
+ let logger = Logger::new();
36
+ let msg = "Authentication required — run `devalang login` to authenticate";
37
+ logger.log_message(LogLevel::Error, msg);
38
+ return Err("Authentication required: run 'devalang login' to authenticate".to_string());
39
+ }
40
+
41
+ let request_url = if let Some(token) = &stored_token_opt {
42
+ format!(
43
+ "{}/v1/assets/url?type={}&slug={}&token={}",
44
+ api_url,
45
+ match addon_type {
46
+ AddonType::Bank => "bank",
47
+ AddonType::Plugin => "plugin",
48
+ AddonType::Preset => "preset",
49
+ AddonType::Template => "template",
50
+ },
51
+ slug,
52
+ token
53
+ )
54
+ } else {
55
+ format!(
56
+ "{}/v1/assets/url?type={}&slug={}",
57
+ api_url,
58
+ match addon_type {
59
+ AddonType::Bank => "bank",
60
+ AddonType::Plugin => "plugin",
61
+ AddonType::Preset => "preset",
62
+ AddonType::Template => "template",
63
+ },
64
+ slug
65
+ )
66
+ };
67
+
68
+ let mut headers = reqwest::header::HeaderMap::new();
69
+ if let Some(token) = stored_token_opt {
70
+ headers.insert(
71
+ "Authorization",
72
+ format!("Bearer {}", token).parse().unwrap(),
73
+ );
74
+ }
75
+
76
+ let client: reqwest::Client = reqwest::Client::builder()
77
+ .default_headers(headers)
78
+ .build()
79
+ .map_err(|_| "Failed to build HTTP client".to_string())?;
80
+
81
+ let resp = client
82
+ .get(&request_url)
83
+ .send()
84
+ .await
85
+ .map_err(|e| format!("Failed to receive response: {}", e))?;
86
+
87
+ let status = resp.status();
88
+ let body_text = resp
89
+ .text()
90
+ .await
91
+ .map_err(|e| format!("Failed to read response body: {}", e))?;
92
+
93
+ // Try to parse JSON; if parsing fails, return body for diagnostics
94
+ let json: serde_json::Value = match serde_json::from_str(&body_text) {
95
+ Ok(v) => v,
96
+ Err(_) => {
97
+ return Err(format!(
98
+ "Invalid JSON response (status {}): {}",
99
+ status, body_text
100
+ ));
101
+ }
102
+ };
103
+
104
+ // Extract payload.url safely
105
+ let signed_url_opt = json
106
+ .get("payload")
107
+ .and_then(|p| p.get("url"))
108
+ .and_then(|u| u.as_str())
109
+ .map(|s| s.to_string());
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
+ }
@@ -2,7 +2,9 @@ use crate::{
2
2
  cli::install::addon::ask_api_for_signed_url, config::ops::load_config,
3
3
  web::cdn::download_from_cdn,
4
4
  };
5
+ use devalang_core::config::driver::ProjectConfigExt;
5
6
  use devalang_types::AddonType;
7
+ use devalang_types::ProjectConfigBankEntry;
6
8
  use devalang_utils::{
7
9
  logger::{LogLevel, Logger},
8
10
  path as path_utils,
@@ -39,15 +41,32 @@ pub async fn install_bank(name: &str, target_dir: &Path) -> Result<(), String> {
39
41
 
40
42
  // Add the bank to the config
41
43
  let config_path = path_utils::get_devalang_config_path()?;
42
- let _config = load_config(Some(&config_path))
44
+ let mut config = load_config(Some(&config_path))
43
45
  .ok_or_else(|| format!("Failed to load config from '{}'", config_path.display()))?;
44
46
 
45
- let _dependency_path = &format!("devalang://bank/{}", name);
47
+ let dependency_path = format!("devalang://bank/{}", name);
46
48
 
47
49
  devalang_utils::file::extract_zip_safely(&archive_path, &extract_path)
48
50
  .map_err(|e| format!("Failed to extract: {}", e))?;
49
51
 
50
- // TODO: Add the bank to the config
52
+ // Add the bank to the config if not already present
53
+ if config.banks.is_none() {
54
+ config.banks = Some(Vec::new());
55
+ }
56
+
57
+ if let Some(banks) = config.banks.as_mut() {
58
+ let exists = banks.iter().any(|b| b.path == dependency_path);
59
+ if !exists {
60
+ banks.push(ProjectConfigBankEntry {
61
+ path: dependency_path.clone(),
62
+ version: None,
63
+ });
64
+
65
+ if let Err(e) = config.write_config(&config) {
66
+ eprintln!("Warning: failed to write config: {}", e);
67
+ }
68
+ }
69
+ }
51
70
 
52
71
  Ok(())
53
72
  }
@@ -1,35 +1,35 @@
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;
5
-
6
- /// Handles the installation command for a given addon type and name.
7
- #[cfg(feature = "cli")]
8
- pub async fn handle_install_command(name: String, addon_type: AddonType) -> Result<(), String> {
9
- use devalang_utils::{
10
- logger::{LogLevel, Logger},
11
- spinner::start_spinner,
12
- };
13
-
14
- let logger = Logger::new();
15
- let deva_dir = path_utils::ensure_deva_dir()?;
16
-
17
- let spinner = start_spinner("Installing...");
18
-
19
- if let Err(e) = install_addon(addon_type.clone(), name.as_str(), &deva_dir).await {
20
- spinner.finish_and_clear();
21
- logger.log_message_with_trace(
22
- LogLevel::Error,
23
- &format!("Error installing {:?} '{}'", addon_type, name),
24
- vec![&e],
25
- );
26
- } else {
27
- spinner.finish_and_clear();
28
- logger.log_message(
29
- LogLevel::Success,
30
- &format!("{:?} '{}' installed successfully!", addon_type, name),
31
- );
32
- }
33
-
34
- Ok(())
35
- }
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;
5
+
6
+ /// Handles the installation command for a given addon type and name.
7
+ #[cfg(feature = "cli")]
8
+ pub async fn handle_install_command(name: String, addon_type: AddonType) -> Result<(), String> {
9
+ use devalang_utils::{
10
+ logger::{LogLevel, Logger},
11
+ spinner::start_spinner,
12
+ };
13
+
14
+ let logger = Logger::new();
15
+ let deva_dir = path_utils::ensure_deva_dir()?;
16
+
17
+ let spinner = start_spinner("Installing...");
18
+
19
+ if let Err(e) = install_addon(addon_type.clone(), name.as_str(), &deva_dir).await {
20
+ spinner.finish_and_clear();
21
+ logger.log_message_with_trace(
22
+ LogLevel::Error,
23
+ &format!("Error installing {:?} '{}'", addon_type, name),
24
+ vec![&e],
25
+ );
26
+ } else {
27
+ spinner.finish_and_clear();
28
+ logger.log_message(
29
+ LogLevel::Success,
30
+ &format!("{:?} '{}' installed successfully!", addon_type, name),
31
+ );
32
+ }
33
+
34
+ Ok(())
35
+ }
@@ -1,4 +1,4 @@
1
- pub mod addon;
2
- pub mod bank;
3
- pub mod commands;
4
- pub mod plugin;
1
+ pub mod addon;
2
+ pub mod bank;
3
+ pub mod commands;
4
+ pub mod plugin;
@@ -1,61 +1,80 @@
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
+ use crate::{
2
+ config::ops::load_config,
3
+ web::cdn::{download_from_cdn, get_cdn_url},
4
+ };
5
+ use devalang_core::config::driver::ProjectConfigExt;
6
+ use devalang_types::ProjectConfigPluginEntry;
7
+ use devalang_utils::path as path_utils;
8
+ use std::{fs, path::Path};
9
+
10
+ pub async fn install_plugin(name: &str, target_dir: &Path) -> Result<(), String> {
11
+ let cdn_url = get_cdn_url();
12
+ let url = format!("{}/plugin/{}/download", cdn_url, name);
13
+
14
+ let plugin_dir = target_dir.join("plugins");
15
+
16
+ // Ensure .deva/tmp exists and build archive path
17
+ let deva_dir = path_utils::ensure_deva_dir()?;
18
+ let tmp_dir = deva_dir.join("tmp");
19
+ if !tmp_dir.exists() {
20
+ fs::create_dir_all(&tmp_dir)
21
+ .map_err(|e| format!("Failed to create tmp dir '{}': {}", tmp_dir.display(), e))?;
22
+ }
23
+
24
+ let archive_path = tmp_dir.join(format!("{}.devaplugin", name));
25
+ let extract_path = plugin_dir.join(name);
26
+
27
+ if extract_path.exists() {
28
+ println!(
29
+ "Plugin '{}' already exists at '{}'. Skipping install.",
30
+ name,
31
+ extract_path.display()
32
+ );
33
+ return Ok(());
34
+ }
35
+
36
+ download_from_cdn(&url, &archive_path)
37
+ .await
38
+ .map_err(|e| format!("Failed to download: {}", e))?;
39
+
40
+ // Add the plugin to the config: locate project root from target_dir
41
+ let project_root = path_utils::find_project_root_from(target_dir)
42
+ .ok_or_else(|| "Failed to determine project root from target_dir".to_string())?;
43
+
44
+ let config_path = project_root.join(path_utils::DEVALANG_CONFIG);
45
+ if !config_path.exists() {
46
+ return Err(format!(
47
+ "Config file not found at '{}'. Please run 'devalang init' before adding an addon",
48
+ config_path.display()
49
+ ));
50
+ }
51
+
52
+ let mut config = load_config(Some(&config_path))
53
+ .ok_or_else(|| format!("Failed to load config from '{}'", config_path.display()))?;
54
+
55
+ let dependency_path = format!("devalang://plugin/{}", name);
56
+
57
+ devalang_utils::file::extract_zip_safely(&archive_path, &extract_path)
58
+ .map_err(|e| format!("Failed to extract: {}", e))?;
59
+
60
+ // Add the plugin to the config if not already present
61
+ if config.plugins.is_none() {
62
+ config.plugins = Some(Vec::new());
63
+ }
64
+
65
+ if let Some(plugins) = config.plugins.as_mut() {
66
+ let exists = plugins.iter().any(|p| p.path == dependency_path);
67
+ if !exists {
68
+ plugins.push(ProjectConfigPluginEntry {
69
+ path: dependency_path.clone(),
70
+ version: None,
71
+ });
72
+
73
+ if let Err(e) = config.write_config(&config) {
74
+ eprintln!("Warning: failed to write config: {}", e);
75
+ }
76
+ }
77
+ }
78
+
79
+ Ok(())
80
+ }