ns_indexer/db/
model_service_state.rs

1use ns_axum_web::erring::HTTPError;
2use ns_scylla_orm::{ColumnsMap, ToCqlVal};
3use ns_scylla_orm_macros::CqlOrm;
4
5use ns_protocol::state;
6
7use crate::db::scylladb;
8
9#[derive(Debug, Default, Clone, CqlOrm, PartialEq)]
10pub struct ServiceState {
11    pub name: String,
12    pub code: i64,
13    pub sequence: i64,
14    pub data: Vec<u8>,
15
16    pub _fields: Vec<String>, // selected fields,field with `_` will be ignored by CqlOrm
17}
18
19impl ServiceState {
20    pub fn with_pk(name: String, code: i64) -> Self {
21        Self {
22            name,
23            code,
24            ..Default::default()
25        }
26    }
27
28    pub fn from_index(value: &state::ServiceState) -> anyhow::Result<Self> {
29        let data = state::to_bytes(&value.data)?;
30        Ok(Self {
31            name: value.name.clone(),
32            code: value.code as i64,
33            sequence: value.sequence as i64,
34            data,
35            _fields: Self::fields(),
36        })
37    }
38
39    pub fn to_index(&self) -> anyhow::Result<state::ServiceState> {
40        let data = state::from_bytes(&self.data)?;
41        Ok(state::ServiceState {
42            name: self.name.clone(),
43            code: self.code as u64,
44            sequence: self.sequence as u64,
45            data,
46        })
47    }
48
49    pub fn select_fields(select_fields: Vec<String>, with_pk: bool) -> anyhow::Result<Vec<String>> {
50        if select_fields.is_empty() {
51            return Ok(Self::fields());
52        }
53
54        let fields = Self::fields();
55        let mut select_fields = select_fields;
56        for field in &select_fields {
57            if !fields.contains(field) {
58                return Err(HTTPError::new(400, format!("Invalid field: {}", field)).into());
59            }
60        }
61
62        let field = "sequence".to_string();
63        if !select_fields.contains(&field) {
64            select_fields.push(field);
65        }
66
67        if with_pk {
68            let field = "name".to_string();
69            if !select_fields.contains(&field) {
70                select_fields.push(field);
71            }
72            let field = "code".to_string();
73            if !select_fields.contains(&field) {
74                select_fields.push(field);
75            }
76        }
77
78        Ok(select_fields)
79    }
80
81    pub async fn get_one(
82        &mut self,
83        db: &scylladb::ScyllaDB,
84        select_fields: Vec<String>,
85    ) -> anyhow::Result<()> {
86        let fields = Self::select_fields(select_fields, false)?;
87        self._fields = fields.clone();
88
89        let query = format!(
90            "SELECT {} FROM service_state WHERE name=? AND code=? LIMIT 1",
91            fields.join(",")
92        );
93        let params = (self.name.to_cql(), self.code.to_cql());
94        let res = db.execute(query, params).await?.single_row()?;
95
96        let mut cols = ColumnsMap::with_capacity(fields.len());
97        cols.fill(res, &fields)?;
98        self.fill(&cols);
99
100        Ok(())
101    }
102
103    pub async fn list_by_name(
104        db: &scylladb::ScyllaDB,
105        name: &String,
106        select_fields: Vec<String>,
107    ) -> anyhow::Result<Vec<Self>> {
108        let fields = Self::select_fields(select_fields, true)?;
109
110        let query = format!(
111            "SELECT {} FROM service_state WHERE name=? USING TIMEOUT 3s",
112            fields.clone().join(",")
113        );
114        let params = (name.to_cql(),);
115        let rows = db.execute_iter(query, params).await?;
116
117        let mut res: Vec<Self> = Vec::with_capacity(rows.len());
118        for row in rows {
119            let mut doc = Self::default();
120            let mut cols = ColumnsMap::with_capacity(fields.len());
121            cols.fill(row, &fields)?;
122            doc.fill(&cols);
123            doc._fields = fields.clone();
124            res.push(doc);
125        }
126
127        Ok(res)
128    }
129}