1
2
3
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
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
use cached::proc_macro::cached;
use sqlx::{Pool, Postgres};
use std::sync::Arc;
use tracing::instrument;

use super::{core::*, entity::*};
use crate::{error::*, primitives::*};

/// Provides methods to interact with `TxTemplateCore` entities.
#[derive(Debug, Clone)]
pub struct TxTemplates {
    pool: Pool<Postgres>,
}

impl TxTemplates {
    pub fn new(pool: &Pool<Postgres>) -> Self {
        Self { pool: pool.clone() }
    }

    #[instrument(name = "sqlx_ledger.tx_templates.create", skip_all)]
    pub async fn create(
        &self,
        NewTxTemplate {
            id,
            code,
            description,
            params,
            tx_input,
            entries,
            metadata,
        }: NewTxTemplate,
    ) -> Result<TxTemplateId, SqlxLedgerError> {
        let params_json = serde_json::to_value(&params)?;
        let tx_input_json = serde_json::to_value(&tx_input)?;
        let entries_json = serde_json::to_value(&entries)?;
        let record = sqlx::query!(
            r#"INSERT INTO sqlx_ledger_tx_templates (id, code, description, params, tx_input, entries, metadata)
            VALUES ($1, $2, $3, $4, $5, $6, $7)
            RETURNING id, version, created_at"#,
            id as TxTemplateId,
            code,
            description,
            params_json,
            tx_input_json,
            entries_json,
            metadata
        )
        .fetch_one(&self.pool)
        .await?;
        Ok(TxTemplateId::from(record.id))
    }

    #[instrument(level = "trace", name = "sqlx_ledger.tx_templates.find_core", skip_all)]
    pub(crate) async fn find_core(
        &self,
        code: &str,
    ) -> Result<Arc<TxTemplateCore>, SqlxLedgerError> {
        cached_find_core(&self.pool, code).await
    }
}

#[cached(
    key = "String",
    convert = r#"{ code.to_string() }"#,
    result = true,
    sync_writes = true
)]
async fn cached_find_core(
    pool: &Pool<Postgres>,
    code: &str,
) -> Result<Arc<TxTemplateCore>, SqlxLedgerError> {
    let record = sqlx::query!(
            r#"SELECT id, code, params, tx_input, entries FROM sqlx_ledger_tx_templates WHERE code = $1 LIMIT 1"#,
            code
        )
        .fetch_one(pool)
        .await?;
    let params = match record.params {
        Some(serde_json::Value::Null) => None,
        Some(params) => Some(serde_json::from_value(params)?),
        None => None,
    };
    let tx_input = serde_json::from_value(record.tx_input)?;
    Ok(Arc::new(TxTemplateCore {
        id: TxTemplateId::from(record.id),
        _code: record.code,
        params,
        entries: serde_json::from_value(record.entries)?,
        tx_input,
    }))
}