1
//! Account-domain natives. Wraps `server::command::{CreateAccount, ListAccounts,
2
//! ListAccountsForManage, GetAccountForManage, SetAccountTag, GetAccount,
3
//! GetAccountCommodities, GetBalance, GetBalances}` as nomiscript-callable host fns.
4

            
5
use scripting::runtime::{alloc_commodity_ref, alloc_ratio_ref, alloc_string_ref, read_string_arg};
6
use server::command::account::{ListAccounts, ListAccountsForManage};
7
use wasmtime::{ArrayRef, Caller, Linker, Rooted, StructRef};
8

            
9
use crate::session::SessionData;
10

            
11
mod balance;
12
mod create;
13
mod list;
14
#[cfg(test)]
15
mod tests;
16

            
17
pub const REGISTERED_COMMANDS: &[&str] = &[
18
    "create-account",
19
    "list-accounts",
20
    "list-accounts-for-manage",
21
    "get-account-for-manage",
22
    "set-account-tag",
23
    "get-account",
24
    "get-account-commodities",
25
    "get-balance",
26
    "get-balances",
27
];
28

            
29
5909
pub fn register(linker: &mut Linker<SessionData>) -> wasmtime::Result<()> {
30
5909
    register_readonly(linker)?;
31
5909
    register_mutators(linker)?;
32
5909
    Ok(())
33
5909
}
34

            
35
6045
pub fn register_readonly(linker: &mut Linker<SessionData>) -> wasmtime::Result<()> {
36
6045
    linker.func_wrap_async(
37
6045
        "nomi",
38
6045
        "account_list_accounts",
39
        |mut caller: Caller<'_, SessionData>,
40
         ()|
41
         -> Box<
42
            dyn std::future::Future<Output = wasmtime::Result<Option<Rooted<StructRef>>>> + Send,
43
125
        > {
44
125
            Box::new(async move {
45
125
                let user_id = caller.data().ctx().user_id;
46
125
                let result = ListAccounts::new().user_id(user_id).run().await;
47
125
                let entries = list::list_account_entries("list-accounts", result)?;
48
125
                list::alloc_account_chain(&mut caller, entries).await
49
125
            })
50
125
        },
51
    )?;
52
6045
    linker.func_wrap_async(
53
6045
        "nomi",
54
6045
        "account_get_account",
55
        |mut caller: Caller<'_, SessionData>,
56
         (key_arg,): (Option<Rooted<ArrayRef>>,)|
57
         -> Box<
58
            dyn std::future::Future<Output = wasmtime::Result<Option<Rooted<StructRef>>>> + Send,
59
375
        > {
60
375
            Box::new(async move {
61
375
                let user_id = caller.data().ctx().user_id;
62
375
                let key = read_string_arg(&mut caller, key_arg)?;
63
375
                list::run_get_account(&mut caller, user_id, key).await
64
375
            })
65
375
        },
66
    )?;
67
6045
    linker.func_wrap_async(
68
6045
        "nomi",
69
6045
        "account_get_balance",
70
        |mut caller: Caller<'_, SessionData>,
71
         (id_arg,): (Option<Rooted<ArrayRef>>,)|
72
         -> Box<
73
            dyn std::future::Future<Output = wasmtime::Result<Option<Rooted<StructRef>>>> + Send,
74
25
        > {
75
25
            Box::new(async move {
76
25
                let user_id = caller.data().ctx().user_id;
77
25
                let id = read_string_arg(&mut caller, id_arg)?;
78
25
                let (numer, denom) = balance::run_get_balance_single(user_id, id).await?;
79
25
                Ok(Some(alloc_ratio_ref(&mut caller, numer, denom)?))
80
25
            })
81
25
        },
82
    )?;
83
6045
    linker.func_wrap_async(
84
6045
        "nomi",
85
6045
        "account_get_balances",
86
        |mut caller: Caller<'_, SessionData>,
87
         (id_arg,): (Option<Rooted<ArrayRef>>,)|
88
         -> Box<
89
            dyn std::future::Future<Output = wasmtime::Result<Option<Rooted<StructRef>>>> + Send,
90
275
        > {
91
275
            Box::new(async move {
92
275
                let user_id = caller.data().ctx().user_id;
93
275
                let id = read_string_arg(&mut caller, id_arg)?;
94
275
                let lines = balance::run_get_balances(user_id, id).await?;
95
250
                balance::alloc_string_pair_chain(&mut caller, lines).await
96
275
            })
97
275
        },
98
    )?;
99
6045
    linker.func_wrap_async(
100
6045
        "nomi",
101
6045
        "account_get_account_commodities",
102
        |mut caller: Caller<'_, SessionData>,
103
         (id_arg,): (Option<Rooted<ArrayRef>>,)|
104
         -> Box<
105
            dyn std::future::Future<Output = wasmtime::Result<Option<Rooted<StructRef>>>> + Send,
106
25
        > {
107
25
            Box::new(async move {
108
25
                let user_id = caller.data().ctx().user_id;
109
25
                let id = read_string_arg(&mut caller, id_arg)?;
110
25
                list::run_get_account_commodities(&mut caller, user_id, id).await
111
25
            })
112
25
        },
113
    )?;
114
6045
    linker.func_wrap_async(
115
6045
        "nomi",
116
6045
        "account_list_accounts_for_manage",
117
        |mut caller: Caller<'_, SessionData>,
118
         ()|
119
         -> Box<
120
            dyn std::future::Future<Output = wasmtime::Result<Option<Rooted<StructRef>>>> + Send,
121
25
        > {
122
25
            Box::new(async move {
123
25
                let user_id = caller.data().ctx().user_id;
124
25
                let result = ListAccountsForManage::new().user_id(user_id).run().await;
125
25
                let entries = list::list_account_entries("list-accounts-for-manage", result)?;
126
25
                list::alloc_account_chain(&mut caller, entries).await
127
25
            })
128
25
        },
129
    )?;
130
6045
    linker.func_wrap_async(
131
6045
        "nomi",
132
6045
        "account_get_account_for_manage",
133
        |mut caller: Caller<'_, SessionData>,
134
         (id_arg,): (Option<Rooted<ArrayRef>>,)|
135
         -> Box<
136
            dyn std::future::Future<Output = wasmtime::Result<Option<Rooted<StructRef>>>> + Send,
137
50
        > {
138
50
            Box::new(async move {
139
50
                let user_id = caller.data().ctx().user_id;
140
50
                let id = read_string_arg(&mut caller, id_arg)?;
141
50
                list::run_get_account_for_manage(&mut caller, user_id, id).await
142
50
            })
143
50
        },
144
    )?;
145
6045
    linker.func_wrap_async(
146
6045
        "nomi",
147
6045
        "account_account_count",
148
        |caller: Caller<'_, SessionData>,
149
         ()|
150
         -> Box<dyn std::future::Future<Output = i32> + Send> {
151
            Box::new(async move {
152
                let user_id = caller.data().ctx().user_id;
153
                balance::count_accounts(user_id).await
154
            })
155
        },
156
    )?;
157
6045
    linker.func_wrap_async(
158
6045
        "nomi",
159
6045
        "account_account_balance",
160
        |mut caller: Caller<'_, SessionData>,
161
         (id_arg,): (Option<Rooted<ArrayRef>>,)|
162
         -> Box<
163
            dyn std::future::Future<Output = wasmtime::Result<Option<Rooted<StructRef>>>> + Send,
164
250
        > {
165
250
            Box::new(async move {
166
250
                let user_id = caller.data().ctx().user_id;
167
250
                let id = read_string_arg(&mut caller, id_arg)?;
168
250
                let (numer, denom, commodity_id) = balance::resolve_balance(user_id, id).await?;
169
225
                let ref_ = alloc_commodity_ref(&mut caller, numer, denom, commodity_id).await?;
170
225
                Ok(Some(ref_))
171
250
            })
172
250
        },
173
    )?;
174
6045
    Ok(())
175
6045
}
176

            
177
5909
pub fn register_mutators(linker: &mut Linker<SessionData>) -> wasmtime::Result<()> {
178
5909
    linker.func_wrap_async(
179
5909
        "nomi",
180
5909
        "account_set_account_tag",
181
        |mut caller: Caller<'_, SessionData>,
182
         (id_arg, name_arg, value_arg): super::StringArgTriple|
183
75
         -> Box<dyn std::future::Future<Output = wasmtime::Result<i32>> + Send> {
184
75
            Box::new(async move {
185
75
                let user_id = caller.data().ctx().user_id;
186
75
                let id = read_string_arg(&mut caller, id_arg)?;
187
75
                let name = read_string_arg(&mut caller, name_arg)?;
188
75
                let value = read_string_arg(&mut caller, value_arg)?;
189
75
                create::run_set_account_tag(user_id, id, name, value).await
190
75
            })
191
75
        },
192
    )?;
193
5909
    linker.func_wrap_async(
194
5909
        "nomi",
195
5909
        "account_create_account",
196
        |mut caller: Caller<'_, SessionData>,
197
         (name_arg, parent_arg): (Option<Rooted<ArrayRef>>, Option<Rooted<ArrayRef>>)|
198
         -> Box<
199
            dyn std::future::Future<Output = wasmtime::Result<Option<Rooted<ArrayRef>>>> + Send,
200
1425
        > {
201
1425
            Box::new(async move {
202
1425
                let user_id = caller.data().ctx().user_id;
203
1425
                let name = read_string_arg(&mut caller, name_arg)?;
204
1425
                let parent = read_string_arg(&mut caller, parent_arg)?;
205
1425
                let id = create::run_create_account(user_id, name, parent).await?;
206
1425
                Ok(Some(alloc_string_ref(&mut caller, id.as_bytes())?))
207
1425
            })
208
1425
        },
209
    )?;
210
5909
    Ok(())
211
5909
}
212

            
213
610
pub(super) fn quote_string(s: &str) -> String {
214
610
    let mut q = String::with_capacity(s.len() + 2);
215
610
    q.push('"');
216
9687
    for ch in s.chars() {
217
9687
        match ch {
218
3
            '"' => q.push_str("\\\""),
219
1
            '\\' => q.push_str("\\\\"),
220
9683
            other => q.push(other),
221
        }
222
    }
223
610
    q.push('"');
224
610
    q
225
610
}