1
use std::collections::HashMap;
2
use std::sync::{Arc, Mutex};
3

            
4
use finance::commodity::{Commodity, CommodityBuilder};
5
use log;
6
use rust_i18n::t;
7
use wasmtime::{Engine, Linker, Module, Store};
8

            
9
use crate::error::HookError;
10
use crate::wrappers::{
11
    DataStore, wrap_wasm_i64, wrap_wasm_i64_get, wrap_wasm_str_list_get, wrap_wasm_str_map,
12
    wrap_wasm_str_transform,
13
};
14

            
15
16
async fn update(
16
16
    commodity: Commodity,
17
16
    tags: Arc<Mutex<HashMap<String, String>>>,
18
16
    script: &[u8],
19
18
) -> Result<Commodity, HookError> {
20
16
    let engine = Engine::default();
21
16
    let mut linker = Linker::new(&engine);
22
16
    let mut builder = CommodityBuilder::new();
23
16
    builder.id(commodity.id);
24

            
25
16
    let store_data = Arc::new(DataStore {
26
16
        data: Mutex::new((builder, commodity.fraction, tags.clone())),
27
16
        memory: Mutex::new(None),
28
16
    });
29

            
30
16
    let mut store = Store::new(&engine, store_data.clone());
31

            
32
16
    let set_frac = wrap_wasm_i64(&mut store, |builder, value| {
33
16
        builder.0.fraction(value);
34
16
    });
35

            
36
16
    let get_frac = wrap_wasm_i64_get(&mut store, |builder| builder.1);
37

            
38
16
    let module = Module::new(store.engine(), script).map_err(|e| {
39
        log::error!("{}: {}", t!("Module creation error"), e);
40
        HookError::WASM(e)
41
    })?;
42

            
43
16
    linker.define(&store, "env", "get_frac", get_frac)?;
44
16
    linker.define(&store, "env", "set_frac", set_frac)?;
45

            
46
16
    let get_tag_list = wrap_wasm_str_list_get(&mut store, |data| {
47
16
        let tags = data
48
16
            .2
49
16
            .lock()
50
16
            .map_err(|_| anyhow::anyhow!("Failed to lock tags"))?;
51
16
        log::info!("{:?}", tags.keys().cloned());
52
16
        Ok(tags.keys().cloned().collect())
53
16
    });
54

            
55
16
    linker.define(&store, "env", "get_tag_list", get_tag_list)?;
56

            
57
24
    let update_tag = wrap_wasm_str_map(&mut store, |data, key, value| {
58
24
        let mut tags = data
59
24
            .2
60
24
            .lock()
61
24
            .map_err(|_| anyhow::anyhow!("Failed to lock tags"))?;
62
24
        log::info!("Inserting {key:?} {value:?}");
63
24
        tags.insert(key, value);
64
24
        Ok(())
65
24
    });
66

            
67
16
    linker.define(&store, "env", "update_tag", update_tag)?;
68

            
69
16
    let get_tag = wrap_wasm_str_transform(&mut store, |data, key| {
70
        // Lock the tags HashMap
71
8
        let tags = data
72
8
            .2
73
8
            .lock()
74
8
            .map_err(|_| anyhow::anyhow!("Failed to lock tags HashMap"))?;
75

            
76
8
        log::info!("Found value: {:?}", tags.get(&key).cloned());
77
        // Fetch the value corresponding to the key
78
8
        tags.get(&key)
79
8
            .cloned()
80
8
            .ok_or_else(|| anyhow::anyhow!("Key not found in tags: {}", key))
81
8
    });
82

            
83
16
    linker.define(&store, "env", "get_tag", get_tag)?;
84

            
85
16
    let instance = linker.instantiate(&mut store, &module)?;
86
16
    if let Some(memory) = instance.get_memory(&mut store, "memory") {
87
16
        store_data.memory.lock()?.replace(memory);
88
    } else {
89
        log::error!("{}", t!("WASM memory access problem"));
90
        return Err(HookError::WASMMem);
91
    };
92

            
93
16
    let process = instance.get_typed_func::<(), ()>(&mut store, "process")?;
94
16
    process.call(&mut store, ())?;
95

            
96
16
    log::info!("{:?}", store_data.clone().data.lock()?.2);
97

            
98
16
    let result = store_data.clone().data.lock()?.0.build()?;
99
16
    Ok(result)
100
16
}
101

            
102
16
pub async fn apply_commodity_hook(
103
16
    commodity: Commodity,
104
16
    tags: Arc<Mutex<HashMap<String, String>>>,
105
16
    hook: &[u8],
106
18
) -> Result<Commodity, HookError> {
107
16
    let updated = update(commodity, tags, hook).await?;
108
16
    Ok(updated)
109
16
}