@devaloop/devalang 0.0.1-alpha.15 → 0.0.1-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devalang +2 -0
- package/.github/workflows/ci.yml +92 -0
- package/Cargo.toml +60 -58
- package/README.md +1 -1
- package/docs/CHANGELOG.md +34 -1
- package/docs/CONTRIBUTING.md +101 -1
- package/docs/ROADMAP.md +1 -1
- package/docs/TODO.md +1 -1
- package/examples/automation.deva +1 -3
- package/examples/bank.deva +4 -4
- package/examples/events.deva +12 -0
- package/examples/function.deva +4 -4
- package/examples/index.deva +3 -5
- package/examples/loop.deva +5 -11
- package/examples/pattern.deva +8 -0
- package/examples/plugin.deva +12 -11
- package/examples/variables.deva +1 -1
- package/out-tsc/bin/index.js +51 -7
- package/out-tsc/index.js +3 -1
- package/out-tsc/scripts/postbuild.js +9 -10
- package/out-tsc/scripts/postinstall.js +49 -0
- package/package.json +12 -4
- package/project-version.json +3 -3
- package/rust/cli/bank.rs +462 -455
- package/rust/cli/build.rs +252 -199
- package/rust/cli/check.rs +221 -180
- package/rust/cli/driver.rs +297 -292
- package/rust/cli/generator.rs +1 -0
- package/rust/cli/init.rs +87 -79
- package/rust/cli/install.rs +35 -32
- package/rust/cli/login.rs +127 -134
- package/rust/cli/mod.rs +13 -11
- package/rust/cli/play.rs +1123 -218
- package/rust/cli/telemetry.rs +19 -0
- package/rust/cli/template.rs +69 -57
- package/rust/cli/update.rs +6 -4
- package/rust/common/api.rs +5 -5
- package/rust/common/mod.rs +3 -3
- package/rust/config/driver.rs +118 -94
- package/rust/config/loader.rs +165 -156
- package/rust/config/mod.rs +4 -2
- package/rust/config/settings.rs +91 -0
- package/rust/config/stats.rs +257 -0
- package/rust/core/audio/engine.rs +696 -659
- package/rust/core/audio/evaluator.rs +263 -132
- package/rust/core/audio/interpreter/arrow_call.rs +198 -187
- package/rust/core/audio/interpreter/call.rs +98 -95
- package/rust/core/audio/interpreter/condition.rs +70 -71
- package/rust/core/audio/interpreter/driver.rs +487 -231
- package/rust/core/audio/interpreter/function.rs +26 -21
- package/rust/core/audio/interpreter/let_.rs +38 -26
- package/rust/core/audio/interpreter/load.rs +18 -18
- package/rust/core/audio/interpreter/loop_.rs +113 -106
- package/rust/core/audio/interpreter/mod.rs +14 -14
- package/rust/core/audio/interpreter/sleep.rs +27 -28
- package/rust/core/audio/interpreter/spawn.rs +105 -102
- package/rust/core/audio/interpreter/tempo.rs +19 -16
- package/rust/core/audio/interpreter/trigger.rs +239 -210
- package/rust/core/audio/loader/mod.rs +1 -1
- package/rust/core/audio/loader/trigger.rs +100 -94
- package/rust/core/audio/mod.rs +7 -7
- package/rust/core/audio/player.rs +64 -64
- package/rust/core/audio/renderer.rs +56 -53
- package/rust/core/audio/special/easing.rs +189 -120
- package/rust/core/audio/special/env.rs +43 -41
- package/rust/core/audio/special/math.rs +102 -92
- package/rust/core/audio/special/mod.rs +9 -9
- package/rust/core/audio/special/modulator.rs +143 -120
- package/rust/core/builder/mod.rs +80 -85
- package/rust/core/debugger/lexer.rs +27 -27
- package/rust/core/debugger/mod.rs +24 -23
- package/rust/core/debugger/module.rs +55 -47
- package/rust/core/debugger/preprocessor.rs +27 -27
- package/rust/core/debugger/store.rs +40 -39
- package/rust/core/error/mod.rs +80 -69
- 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 -292
- package/rust/core/lexer/handler/identifier.rs +46 -43
- package/rust/core/lexer/handler/indent.rs +66 -66
- package/rust/core/lexer/handler/mod.rs +16 -16
- 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 +54 -51
- package/rust/core/lexer/token.rs +97 -94
- package/rust/core/mod.rs +11 -11
- package/rust/core/parser/driver.rs +513 -490
- package/rust/core/parser/handler/arrow_call.rs +233 -227
- package/rust/core/parser/handler/at.rs +245 -162
- package/rust/core/parser/handler/bank.rs +94 -69
- package/rust/core/parser/handler/condition.rs +80 -74
- package/rust/core/parser/handler/dot.rs +143 -135
- package/rust/core/parser/handler/identifier/automate.rs +257 -194
- package/rust/core/parser/handler/identifier/call.rs +91 -88
- package/rust/core/parser/handler/identifier/emit.rs +66 -0
- package/rust/core/parser/handler/identifier/function.rs +100 -91
- package/rust/core/parser/handler/identifier/group.rs +85 -75
- package/rust/core/parser/handler/identifier/let_.rs +158 -143
- package/rust/core/parser/handler/identifier/mod.rs +54 -56
- package/rust/core/parser/handler/identifier/on.rs +98 -0
- package/rust/core/parser/handler/identifier/print.rs +52 -29
- package/rust/core/parser/handler/identifier/sleep.rs +36 -33
- package/rust/core/parser/handler/identifier/spawn.rs +91 -88
- package/rust/core/parser/handler/identifier/synth.rs +65 -63
- package/rust/core/parser/handler/loop_.rs +170 -89
- package/rust/core/parser/handler/mod.rs +8 -8
- package/rust/core/parser/handler/tempo.rs +53 -47
- package/rust/core/parser/mod.rs +4 -4
- package/rust/core/parser/statement.rs +142 -113
- package/rust/core/plugin/loader.rs +123 -48
- package/rust/core/plugin/mod.rs +2 -1
- package/rust/core/plugin/runner.rs +296 -0
- package/rust/core/preprocessor/loader.rs +515 -326
- package/rust/core/preprocessor/mod.rs +4 -4
- package/rust/core/preprocessor/module.rs +60 -58
- package/rust/core/preprocessor/processor.rs +99 -101
- package/rust/core/preprocessor/resolver/bank.rs +51 -48
- package/rust/core/preprocessor/resolver/call.rs +100 -101
- package/rust/core/preprocessor/resolver/condition.rs +97 -97
- package/rust/core/preprocessor/resolver/driver.rs +310 -280
- package/rust/core/preprocessor/resolver/function.rs +69 -68
- package/rust/core/preprocessor/resolver/group.rs +96 -91
- package/rust/core/preprocessor/resolver/let_.rs +32 -28
- package/rust/core/preprocessor/resolver/loop_.rs +320 -121
- package/rust/core/preprocessor/resolver/mod.rs +15 -15
- package/rust/core/preprocessor/resolver/spawn.rs +76 -73
- package/rust/core/preprocessor/resolver/synth.rs +56 -50
- package/rust/core/preprocessor/resolver/tempo.rs +50 -49
- package/rust/core/preprocessor/resolver/trigger.rs +113 -115
- package/rust/core/preprocessor/resolver/value.rs +81 -81
- package/rust/core/shared/duration.rs +9 -9
- package/rust/core/shared/mod.rs +3 -3
- package/rust/core/shared/value.rs +35 -32
- package/rust/core/store/function.rs +34 -34
- package/rust/core/store/global.rs +55 -38
- package/rust/core/store/mod.rs +5 -5
- package/rust/core/store/variable.rs +37 -34
- package/rust/core/utils/mod.rs +2 -2
- package/rust/core/utils/path.rs +37 -31
- package/rust/core/utils/validation.rs +35 -36
- package/rust/installer/addon.rs +84 -80
- package/rust/installer/bank.rs +62 -65
- package/rust/installer/mod.rs +5 -5
- package/rust/installer/plugin.rs +54 -55
- package/rust/installer/utils.rs +56 -56
- package/rust/lib.rs +156 -164
- package/rust/main.rs +250 -144
- package/rust/utils/error.rs +200 -51
- package/rust/utils/file.rs +38 -35
- package/rust/utils/first_usage.rs +76 -0
- package/rust/utils/logger.rs +195 -143
- package/rust/utils/mod.rs +9 -7
- package/rust/utils/signature.rs +19 -17
- package/rust/utils/spinner.rs +22 -19
- package/rust/utils/telemetry.rs +292 -0
- package/rust/utils/watcher.rs +34 -33
- package/templates/minimal/README.md +97 -121
- package/templates/welcome/README.md +97 -121
- package/typescript/bin/index.ts +19 -5
- package/typescript/index.ts +3 -1
- package/typescript/scripts/postbuild.ts +10 -6
- package/typescript/scripts/postinstall.ts +56 -0
- package/typescript/scripts/version/bump.ts +0 -1
- package/typescript/scripts/version/index.ts +0 -1
- package/out-tsc/bin/devalang.exe +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
pub mod
|
|
2
|
-
pub mod
|
|
3
|
-
pub mod
|
|
4
|
-
pub mod
|
|
5
|
-
pub mod
|
|
6
|
-
pub mod
|
|
7
|
-
pub mod
|
|
8
|
-
pub mod
|
|
1
|
+
pub mod arrow_call;
|
|
2
|
+
pub mod at;
|
|
3
|
+
pub mod bank;
|
|
4
|
+
pub mod condition;
|
|
5
|
+
pub mod dot;
|
|
6
|
+
pub mod identifier;
|
|
7
|
+
pub mod loop_;
|
|
8
|
+
pub mod tempo;
|
|
@@ -1,47 +1,53 @@
|
|
|
1
|
-
use crate::core::{
|
|
2
|
-
lexer::token::TokenKind,
|
|
3
|
-
parser::{
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
1
|
+
use crate::core::{
|
|
2
|
+
lexer::token::TokenKind,
|
|
3
|
+
parser::{
|
|
4
|
+
driver::Parser,
|
|
5
|
+
statement::{Statement, StatementKind},
|
|
6
|
+
},
|
|
7
|
+
shared::value::Value,
|
|
8
|
+
store::global::GlobalStore,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
pub fn parse_tempo_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
|
|
12
|
+
parser.advance(); // consume 'bpm'
|
|
13
|
+
|
|
14
|
+
let Some(tempo_token) = parser.previous_clone() else {
|
|
15
|
+
return Statement::unknown();
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Expect a number or identifier
|
|
19
|
+
let Some(value_token) = parser.peek_clone() else {
|
|
20
|
+
return Statement::error(
|
|
21
|
+
tempo_token,
|
|
22
|
+
"Expected a number or identifier after 'bpm'".to_string(),
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
let value = match value_token.kind {
|
|
27
|
+
TokenKind::Number => {
|
|
28
|
+
parser.advance();
|
|
29
|
+
Value::Number(value_token.lexeme.parse().unwrap_or(0.0))
|
|
30
|
+
}
|
|
31
|
+
TokenKind::Identifier => {
|
|
32
|
+
parser.advance();
|
|
33
|
+
Value::Identifier(value_token.lexeme.clone())
|
|
34
|
+
}
|
|
35
|
+
_ => {
|
|
36
|
+
return Statement::error(
|
|
37
|
+
value_token.clone(),
|
|
38
|
+
format!(
|
|
39
|
+
"Expected a number or identifier after 'bpm', got {:?}",
|
|
40
|
+
value_token.kind
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
Statement {
|
|
47
|
+
kind: StatementKind::Tempo,
|
|
48
|
+
value,
|
|
49
|
+
indent: tempo_token.indent,
|
|
50
|
+
line: tempo_token.line,
|
|
51
|
+
column: tempo_token.column,
|
|
52
|
+
}
|
|
53
|
+
}
|
package/rust/core/parser/mod.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
pub mod driver;
|
|
2
|
-
|
|
3
|
-
pub mod
|
|
4
|
-
pub mod
|
|
1
|
+
pub mod driver;
|
|
2
|
+
|
|
3
|
+
pub mod handler;
|
|
4
|
+
pub mod statement;
|
|
@@ -1,113 +1,142 @@
|
|
|
1
|
-
use
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
pub
|
|
10
|
-
pub
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
1
|
+
use crate::core::{
|
|
2
|
+
lexer::token::Token,
|
|
3
|
+
shared::{duration::Duration, value::Value},
|
|
4
|
+
};
|
|
5
|
+
use serde::{Deserialize, Serialize};
|
|
6
|
+
|
|
7
|
+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
8
|
+
pub struct Statement {
|
|
9
|
+
pub kind: StatementKind,
|
|
10
|
+
pub value: Value,
|
|
11
|
+
pub indent: usize,
|
|
12
|
+
pub line: usize,
|
|
13
|
+
pub column: usize,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
impl Statement {
|
|
17
|
+
pub fn unknown() -> Self {
|
|
18
|
+
Statement {
|
|
19
|
+
kind: StatementKind::Unknown,
|
|
20
|
+
value: Value::Null,
|
|
21
|
+
indent: 0,
|
|
22
|
+
line: 0,
|
|
23
|
+
column: 0,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
pub fn unknown_from_token(token: &Token) -> Self {
|
|
28
|
+
Statement {
|
|
29
|
+
kind: StatementKind::Unknown,
|
|
30
|
+
value: Value::Null,
|
|
31
|
+
indent: token.indent,
|
|
32
|
+
line: token.line,
|
|
33
|
+
column: token.column,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
pub fn error(token: Token, message: String) -> Self {
|
|
38
|
+
Statement {
|
|
39
|
+
kind: StatementKind::Error { message },
|
|
40
|
+
value: Value::Null,
|
|
41
|
+
indent: token.indent,
|
|
42
|
+
line: token.line,
|
|
43
|
+
column: token.column,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
#[derive(Debug, Serialize, Clone, Deserialize, PartialEq)]
|
|
49
|
+
pub enum StatementKind {
|
|
50
|
+
// ───── Core Instructions ─────
|
|
51
|
+
Tempo,
|
|
52
|
+
Bank {
|
|
53
|
+
alias: Option<String>,
|
|
54
|
+
},
|
|
55
|
+
Print,
|
|
56
|
+
Load {
|
|
57
|
+
source: String,
|
|
58
|
+
alias: String,
|
|
59
|
+
},
|
|
60
|
+
Use {
|
|
61
|
+
name: String,
|
|
62
|
+
alias: Option<String>,
|
|
63
|
+
},
|
|
64
|
+
Let {
|
|
65
|
+
name: String,
|
|
66
|
+
},
|
|
67
|
+
Automate {
|
|
68
|
+
target: String,
|
|
69
|
+
},
|
|
70
|
+
ArrowCall {
|
|
71
|
+
target: String,
|
|
72
|
+
method: String,
|
|
73
|
+
args: Vec<Value>,
|
|
74
|
+
},
|
|
75
|
+
Function {
|
|
76
|
+
name: String,
|
|
77
|
+
parameters: Vec<String>,
|
|
78
|
+
body: Vec<Statement>,
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
// ───── Instruments ─────
|
|
82
|
+
Synth,
|
|
83
|
+
|
|
84
|
+
// ───── Playback / Scheduling ─────
|
|
85
|
+
Trigger {
|
|
86
|
+
entity: String,
|
|
87
|
+
duration: Duration,
|
|
88
|
+
effects: Option<Value>,
|
|
89
|
+
},
|
|
90
|
+
Sleep,
|
|
91
|
+
Call {
|
|
92
|
+
name: String,
|
|
93
|
+
args: Vec<Value>,
|
|
94
|
+
},
|
|
95
|
+
Spawn {
|
|
96
|
+
name: String,
|
|
97
|
+
args: Vec<Value>,
|
|
98
|
+
},
|
|
99
|
+
Loop,
|
|
100
|
+
|
|
101
|
+
// ───── Structure & Logic ─────
|
|
102
|
+
Group,
|
|
103
|
+
|
|
104
|
+
// ───── Module System ─────
|
|
105
|
+
Include(String),
|
|
106
|
+
Export {
|
|
107
|
+
names: Vec<String>,
|
|
108
|
+
source: String,
|
|
109
|
+
},
|
|
110
|
+
Import {
|
|
111
|
+
names: Vec<String>,
|
|
112
|
+
source: String,
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// ───── Conditions ─────
|
|
116
|
+
If,
|
|
117
|
+
Else,
|
|
118
|
+
ElseIf,
|
|
119
|
+
|
|
120
|
+
// ───── Internal / Utility ─────
|
|
121
|
+
Comment,
|
|
122
|
+
Indent,
|
|
123
|
+
Dedent,
|
|
124
|
+
NewLine,
|
|
125
|
+
|
|
126
|
+
// ───── Events / Live coding ─────
|
|
127
|
+
On {
|
|
128
|
+
event: String,
|
|
129
|
+
args: Option<Vec<Value>>,
|
|
130
|
+
body: Vec<Statement>,
|
|
131
|
+
},
|
|
132
|
+
Emit {
|
|
133
|
+
event: String,
|
|
134
|
+
payload: Option<Value>,
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
// ───── Error Handling ─────
|
|
138
|
+
Unknown,
|
|
139
|
+
Error {
|
|
140
|
+
message: String,
|
|
141
|
+
},
|
|
142
|
+
}
|
|
@@ -1,48 +1,123 @@
|
|
|
1
|
-
use
|
|
2
|
-
use
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
pub
|
|
8
|
-
pub
|
|
9
|
-
pub
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
pub
|
|
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
|
-
|
|
1
|
+
use serde::Deserialize;
|
|
2
|
+
use std::path::Path;
|
|
3
|
+
use toml::Value as TomlValue;
|
|
4
|
+
|
|
5
|
+
#[derive(Debug, Deserialize, Clone)]
|
|
6
|
+
pub struct PluginInfo {
|
|
7
|
+
pub name: String,
|
|
8
|
+
pub version: Option<String>,
|
|
9
|
+
pub description: Option<String>,
|
|
10
|
+
pub author: Option<String>,
|
|
11
|
+
#[serde(skip)]
|
|
12
|
+
pub exports: Vec<ExportEntry>,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
#[derive(Debug, Deserialize, Clone)]
|
|
16
|
+
pub struct ExportEntry {
|
|
17
|
+
pub name: String,
|
|
18
|
+
#[serde(rename = "type")]
|
|
19
|
+
pub kind: String,
|
|
20
|
+
#[serde(default)]
|
|
21
|
+
pub default: Option<TomlValue>,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#[derive(Debug, Deserialize, Clone)]
|
|
25
|
+
pub struct PluginFile {
|
|
26
|
+
pub plugin: PluginInfo,
|
|
27
|
+
#[serde(default)]
|
|
28
|
+
pub export: Vec<ExportEntry>,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Load a plugin from local .deva directory given author and name
|
|
32
|
+
pub fn load_plugin(author: &str, name: &str) -> Result<(PluginInfo, Vec<u8>), String> {
|
|
33
|
+
// Align with other loaders (banks) that use relative ./.deva paths
|
|
34
|
+
let root = Path::new("./.deva");
|
|
35
|
+
// Preferred layout: ./.deva/plugin/<author>.<name>/
|
|
36
|
+
let plugin_dir_preferred = root.join("plugin").join(format!("{}.{}", author, name));
|
|
37
|
+
let toml_path_preferred = plugin_dir_preferred.join("plugin.toml");
|
|
38
|
+
let wasm_path_preferred_bg = plugin_dir_preferred.join(format!("{}_bg.wasm", name));
|
|
39
|
+
let wasm_path_preferred_plain = plugin_dir_preferred.join(format!("{}.wasm", name));
|
|
40
|
+
|
|
41
|
+
// Legacy layout (fallback): ./.deva/plugin/<author>/<name>/
|
|
42
|
+
let plugin_dir_fallback = root.join("plugin").join(author).join(name);
|
|
43
|
+
let toml_path_fallback = plugin_dir_fallback.join("plugin.toml");
|
|
44
|
+
let wasm_path_fallback_bg = plugin_dir_fallback.join(format!("{}_bg.wasm", name));
|
|
45
|
+
let wasm_path_fallback_plain = plugin_dir_fallback.join(format!("{}.wasm", name));
|
|
46
|
+
|
|
47
|
+
// Resolve actual paths to use
|
|
48
|
+
let (toml_path, wasm_path) = if toml_path_preferred.exists() && wasm_path_preferred_bg.exists()
|
|
49
|
+
{
|
|
50
|
+
(toml_path_preferred, wasm_path_preferred_bg)
|
|
51
|
+
} else if toml_path_preferred.exists() && wasm_path_preferred_plain.exists() {
|
|
52
|
+
(toml_path_preferred, wasm_path_preferred_plain)
|
|
53
|
+
} else if toml_path_fallback.exists() && wasm_path_fallback_bg.exists() {
|
|
54
|
+
(toml_path_fallback, wasm_path_fallback_bg)
|
|
55
|
+
} else if toml_path_fallback.exists() && wasm_path_fallback_plain.exists() {
|
|
56
|
+
(toml_path_fallback, wasm_path_fallback_plain)
|
|
57
|
+
} else {
|
|
58
|
+
// If either file is missing in both layouts, produce specific errors for missing files in preferred layout
|
|
59
|
+
if !toml_path_preferred.exists() {
|
|
60
|
+
return Err(format!(
|
|
61
|
+
"❌ Plugin file not found: {}",
|
|
62
|
+
toml_path_preferred.display()
|
|
63
|
+
));
|
|
64
|
+
}
|
|
65
|
+
if !wasm_path_preferred_bg.exists() && !wasm_path_preferred_plain.exists() {
|
|
66
|
+
return Err(format!(
|
|
67
|
+
"❌ Plugin wasm not found: '{}' or '{}'",
|
|
68
|
+
wasm_path_preferred_bg.display(),
|
|
69
|
+
wasm_path_preferred_plain.display()
|
|
70
|
+
));
|
|
71
|
+
}
|
|
72
|
+
unreachable!();
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
let toml_content = std::fs::read_to_string(&toml_path)
|
|
76
|
+
.map_err(|e| format!("Failed to read '{}': {}", toml_path.display(), e))?;
|
|
77
|
+
let plugin_file: PluginFile = toml::from_str(&toml_content)
|
|
78
|
+
.map_err(|e| format!("Failed to parse '{}': {}", toml_path.display(), e))?;
|
|
79
|
+
|
|
80
|
+
let wasm_bytes = std::fs::read(&wasm_path)
|
|
81
|
+
.map_err(|e| format!("Failed to read '{}': {}", wasm_path.display(), e))?;
|
|
82
|
+
|
|
83
|
+
let mut info = plugin_file.plugin.clone();
|
|
84
|
+
info.exports = plugin_file.export.clone();
|
|
85
|
+
|
|
86
|
+
Ok((info, wasm_bytes))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// Load a plugin from dot notation: "author.name"
|
|
90
|
+
pub fn load_plugin_from_dot(dot: &str) -> Result<(PluginInfo, Vec<u8>), String> {
|
|
91
|
+
let mut parts = dot.split('.');
|
|
92
|
+
let author = parts
|
|
93
|
+
.next()
|
|
94
|
+
.ok_or_else(|| "Invalid plugin name, missing author".to_string())?;
|
|
95
|
+
let name = parts
|
|
96
|
+
.next()
|
|
97
|
+
.ok_or_else(|| "Invalid plugin name, missing name".to_string())?;
|
|
98
|
+
if parts.next().is_some() {
|
|
99
|
+
return Err("Invalid plugin name format, expected <author>.<name>".into());
|
|
100
|
+
}
|
|
101
|
+
load_plugin(author, name)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
pub fn load_plugin_from_uri(uri: &str) -> Result<(PluginInfo, Vec<u8>), String> {
|
|
105
|
+
if !uri.starts_with("devalang://plugin/") {
|
|
106
|
+
return Err("Invalid plugin URI".into());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Expect format: devalang://plugin/author.name
|
|
110
|
+
let payload = uri.trim_start_matches("devalang://plugin/");
|
|
111
|
+
let mut parts = payload.split('.');
|
|
112
|
+
let author = parts
|
|
113
|
+
.next()
|
|
114
|
+
.ok_or_else(|| "Invalid plugin URI, missing author".to_string())?;
|
|
115
|
+
let name = parts
|
|
116
|
+
.next()
|
|
117
|
+
.ok_or_else(|| "Invalid plugin URI, missing name".to_string())?;
|
|
118
|
+
if parts.next().is_some() {
|
|
119
|
+
return Err("Invalid plugin URI format, expected devalang://plugin/<author>.<name>".into());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
load_plugin(author, name)
|
|
123
|
+
}
|
package/rust/core/plugin/mod.rs
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
pub mod loader;
|
|
1
|
+
pub mod loader;
|
|
2
|
+
pub mod runner;
|