1use nomiscript::EntityKind;
23use scripting::runtime::{read_entity_string_field, read_ratio_arg, read_string_arg};
24use wasmtime::{ArrayRef, Caller, Linker, Rooted, StructRef};
25
26use crate::draft::{DraftSplit, DraftTag};
27use crate::session::SessionData;
28
29pub const REGISTERED_COMMANDS: &[&str] = &[
30 "set-draft-note",
31 "set-draft-date",
32 "draft-split",
33 "draft-tag",
34 "draft-split-tag",
35];
36
37type StringArg = Option<Rooted<ArrayRef>>;
38type StructArg = Option<Rooted<StructRef>>;
39
40type Fut<'a, O> = Box<dyn std::future::Future<Output = wasmtime::Result<O>> + Send + 'a>;
41
42pub fn register(linker: &mut Linker<SessionData>) -> wasmtime::Result<()> {
43 linker.func_wrap_async(
44 "nomi",
45 "template_set_draft_note",
46 |mut caller: Caller<'_, SessionData>, (note_arg,): (StringArg,)| -> Fut<'_, i32> {
47 Box::new(async move {
48 let note = read_string_arg(&mut caller, note_arg)?
49 .ok_or_else(|| wasmtime::Error::msg("set-draft-note: missing note"))?;
50 caller.data().with_draft(|d| d.set_note(note))?;
51 Ok(1)
52 })
53 },
54 )?;
55 linker.func_wrap_async(
56 "nomi",
57 "template_set_draft_date",
58 |mut caller: Caller<'_, SessionData>, (date_arg,): (StringArg,)| -> Fut<'_, i32> {
59 Box::new(async move {
60 let date = read_string_arg(&mut caller, date_arg)?
61 .ok_or_else(|| wasmtime::Error::msg("set-draft-date: missing date"))?;
62 caller.data().with_draft(|d| d.set_date(date))?;
63 Ok(1)
64 })
65 },
66 )?;
67 linker.func_wrap_async(
68 "nomi",
69 "template_draft_split",
70 |mut caller: Caller<'_, SessionData>,
71 (account_arg, commodity_arg, amount_arg): (StructArg, StructArg, StructArg)|
72 -> Fut<'_, i32> {
73 Box::new(async move {
74 let account_id =
75 read_entity_string_field(&mut caller, account_arg, EntityKind::Account, "id")?
76 .ok_or_else(|| wasmtime::Error::msg("draft-split: missing account"))?;
77 let commodity_id = read_entity_string_field(
78 &mut caller,
79 commodity_arg,
80 EntityKind::Commodity,
81 "id",
82 )?
83 .ok_or_else(|| wasmtime::Error::msg("draft-split: missing commodity"))?;
84 let (value_num, value_denom) = read_ratio_arg(&mut caller, amount_arg)?
85 .ok_or_else(|| wasmtime::Error::msg("draft-split: missing amount"))?;
86 let mut index: Option<usize> = None;
92 caller.data().with_draft(|d| {
93 if i32::try_from(d.split_count()).is_ok() {
94 index = Some(d.add_split(DraftSplit {
95 account_id,
96 commodity_id,
97 value_num,
98 value_denom,
99 tags: Vec::new(),
100 }));
101 }
102 })?;
103 let index =
104 index.ok_or_else(|| wasmtime::Error::msg("draft-split: too many splits"))?;
105 i32::try_from(index)
106 .map_err(|_| wasmtime::Error::msg("draft-split: too many splits"))
107 })
108 },
109 )?;
110 linker.func_wrap_async(
111 "nomi",
112 "template_draft_tag",
113 |mut caller: Caller<'_, SessionData>,
114 (name_arg, value_arg): (StringArg, StringArg)|
115 -> Fut<'_, i32> {
116 Box::new(async move {
117 let name = read_string_arg(&mut caller, name_arg)?
118 .ok_or_else(|| wasmtime::Error::msg("draft-tag: missing name"))?;
119 let value = read_string_arg(&mut caller, value_arg)?
120 .ok_or_else(|| wasmtime::Error::msg("draft-tag: missing value"))?;
121 caller
122 .data()
123 .with_draft(|d| d.add_tag(DraftTag { name, value }))?;
124 Ok(1)
125 })
126 },
127 )?;
128 linker.func_wrap_async(
129 "nomi",
130 "template_draft_split_tag",
131 |mut caller: Caller<'_, SessionData>,
132 (index_arg, name_arg, value_arg): (i32, StringArg, StringArg)|
133 -> Fut<'_, i32> {
134 Box::new(async move {
135 let index = usize::try_from(index_arg).map_err(|_| {
136 wasmtime::Error::msg("draft-split-tag: split index must be non-negative")
137 })?;
138 let name = read_string_arg(&mut caller, name_arg)?
139 .ok_or_else(|| wasmtime::Error::msg("draft-split-tag: missing name"))?;
140 let value = read_string_arg(&mut caller, value_arg)?
141 .ok_or_else(|| wasmtime::Error::msg("draft-split-tag: missing value"))?;
142 let mut ok = false;
143 caller
144 .data()
145 .with_draft(|d| ok = d.add_split_tag(index, DraftTag { name, value }))?;
146 if !ok {
147 return Err(wasmtime::Error::msg(format!(
148 "draft-split-tag: no draft split at index {index} \
149 (pass the value returned by draft-split)"
150 )));
151 }
152 Ok(1)
153 })
154 },
155 )?;
156 Ok(())
157}