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
7433
pub fn register(linker: &mut Linker<SessionData>) -> wasmtime::Result<()> {
30
7433
    register_readonly(linker)?;
31
7433
    register_mutators(linker)?;
32
7433
    Ok(())
33
7433
}
34

            
35
7573
pub fn register_readonly(linker: &mut Linker<SessionData>) -> wasmtime::Result<()> {
36
7573
    linker.func_wrap_async(
37
7573
        "nomi",
38
7573
        "account_list_accounts",
39
        |mut caller: Caller<'_, SessionData>,
40
         ()|
41
         -> Box<
42
            dyn std::future::Future<Output = wasmtime::Result<Option<Rooted<StructRef>>>> + Send,
43
130
        > {
44
130
            Box::new(async move {
45
130
                let user_id = caller.data().ctx().user_id;
46
130
                let result = ListAccounts::new().user_id(user_id).run().await;
47
130
                let entries = list::list_account_entries("list-accounts", result)?;
48
130
                list::alloc_account_chain(&mut caller, entries).await
49
130
            })
50
130
        },
51
    )?;
52
7573
    linker.func_wrap_async(
53
7573
        "nomi",
54
7573
        "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
390
        > {
60
390
            Box::new(async move {
61
390
                let user_id = caller.data().ctx().user_id;
62
390
                let key = read_string_arg(&mut caller, key_arg)?;
63
390
                list::run_get_account(&mut caller, user_id, key).await
64
390
            })
65
390
        },
66
    )?;
67
7573
    linker.func_wrap_async(
68
7573
        "nomi",
69
7573
        "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
26
        > {
75
26
            Box::new(async move {
76
26
                let user_id = caller.data().ctx().user_id;
77
26
                let id = read_string_arg(&mut caller, id_arg)?;
78
26
                let (numer, denom) = balance::run_get_balance_single(user_id, id).await?;
79
26
                Ok(Some(alloc_ratio_ref(&mut caller, numer, denom)?))
80
26
            })
81
26
        },
82
    )?;
83
7573
    linker.func_wrap_async(
84
7573
        "nomi",
85
7573
        "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
286
        > {
91
286
            Box::new(async move {
92
286
                let user_id = caller.data().ctx().user_id;
93
286
                let id = read_string_arg(&mut caller, id_arg)?;
94
286
                let lines = balance::run_get_balances(user_id, id).await?;
95
260
                balance::alloc_string_pair_chain(&mut caller, lines).await
96
286
            })
97
286
        },
98
    )?;
99
7573
    linker.func_wrap_async(
100
7573
        "nomi",
101
7573
        "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
26
        > {
107
26
            Box::new(async move {
108
26
                let user_id = caller.data().ctx().user_id;
109
26
                let id = read_string_arg(&mut caller, id_arg)?;
110
26
                list::run_get_account_commodities(&mut caller, user_id, id).await
111
26
            })
112
26
        },
113
    )?;
114
7573
    linker.func_wrap_async(
115
7573
        "nomi",
116
7573
        "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
26
        > {
122
26
            Box::new(async move {
123
26
                let user_id = caller.data().ctx().user_id;
124
26
                let result = ListAccountsForManage::new().user_id(user_id).run().await;
125
26
                let entries = list::list_account_entries("list-accounts-for-manage", result)?;
126
26
                list::alloc_account_chain(&mut caller, entries).await
127
26
            })
128
26
        },
129
    )?;
130
7573
    linker.func_wrap_async(
131
7573
        "nomi",
132
7573
        "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
52
        > {
138
52
            Box::new(async move {
139
52
                let user_id = caller.data().ctx().user_id;
140
52
                let id = read_string_arg(&mut caller, id_arg)?;
141
52
                list::run_get_account_for_manage(&mut caller, user_id, id).await
142
52
            })
143
52
        },
144
    )?;
145
7573
    linker.func_wrap_async(
146
7573
        "nomi",
147
7573
        "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
7573
    linker.func_wrap_async(
158
7573
        "nomi",
159
7573
        "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
260
        > {
165
260
            Box::new(async move {
166
260
                let user_id = caller.data().ctx().user_id;
167
260
                let id = read_string_arg(&mut caller, id_arg)?;
168
260
                let (numer, denom, commodity_id) = balance::resolve_balance(user_id, id).await?;
169
234
                let ref_ = alloc_commodity_ref(&mut caller, numer, denom, commodity_id).await?;
170
234
                Ok(Some(ref_))
171
260
            })
172
260
        },
173
    )?;
174
7573
    Ok(())
175
7573
}
176

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

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