fraiseql_functions/store/memory/
mod.rs1use std::{
4 collections::HashMap,
5 sync::{Arc, Mutex},
6};
7
8use async_trait::async_trait;
9use fraiseql_error::{FraiseQLError, Result};
10
11use super::{FunctionRecord, FunctionStatus, FunctionStore};
12use crate::types::RuntimeType;
13
14#[derive(Debug, Clone)]
19pub struct InMemoryFunctionStore {
20 inner: Arc<Mutex<StoreInner>>,
21}
22
23#[derive(Debug, Default)]
24struct StoreInner {
25 records: HashMap<String, FunctionRecord>,
27 next_pk: i64,
29 next_version: HashMap<String, i32>,
31}
32
33impl Default for InMemoryFunctionStore {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl InMemoryFunctionStore {
40 #[must_use]
42 pub fn new() -> Self {
43 Self {
44 inner: Arc::new(Mutex::new(StoreInner {
45 records: HashMap::new(),
46 next_pk: 1,
47 next_version: HashMap::new(),
48 })),
49 }
50 }
51}
52
53#[async_trait]
54impl FunctionStore for InMemoryFunctionStore {
55 async fn store_function(
56 &self,
57 name: &str,
58 runtime: RuntimeType,
59 bytecode: bytes::Bytes,
60 ) -> Result<FunctionRecord> {
61 let mut guard = self.inner.lock().map_err(|_| FraiseQLError::Validation {
62 message: "function store mutex poisoned".to_string(),
63 path: None,
64 })?;
65
66 let pk = guard.next_pk;
67 guard.next_pk += 1;
68
69 let version = guard.next_version.entry(name.to_string()).or_insert(0);
70 *version += 1;
71 let ver = *version;
72
73 let record = FunctionRecord {
74 pk_function: pk,
75 name: name.to_string(),
76 runtime,
77 bytecode,
78 version: ver,
79 deployed_at: chrono::Utc::now(),
80 status: FunctionStatus::Active,
81 };
82
83 guard.records.insert(name.to_string(), record.clone());
85 Ok(record)
86 }
87
88 async fn get_function(&self, name: &str) -> Result<Option<FunctionRecord>> {
89 let guard = self.inner.lock().map_err(|_| FraiseQLError::Validation {
90 message: "function store mutex poisoned".to_string(),
91 path: None,
92 })?;
93
94 let record =
95 guard.records.get(name).filter(|r| r.status == FunctionStatus::Active).cloned();
96
97 Ok(record)
98 }
99
100 async fn list_functions(&self) -> Result<Vec<FunctionRecord>> {
101 let guard = self.inner.lock().map_err(|_| FraiseQLError::Validation {
102 message: "function store mutex poisoned".to_string(),
103 path: None,
104 })?;
105
106 let mut records: Vec<FunctionRecord> = guard
107 .records
108 .values()
109 .filter(|r| r.status == FunctionStatus::Active)
110 .cloned()
111 .collect();
112
113 records.sort_by(|a, b| a.name.cmp(&b.name));
115 Ok(records)
116 }
117
118 async fn delete_function(&self, name: &str) -> Result<bool> {
119 let mut guard = self.inner.lock().map_err(|_| FraiseQLError::Validation {
120 message: "function store mutex poisoned".to_string(),
121 path: None,
122 })?;
123
124 let found = match guard.records.get_mut(name).filter(|r| r.status == FunctionStatus::Active)
125 {
126 Some(r) => {
127 r.status = FunctionStatus::Inactive;
128 true
129 },
130 None => false,
131 };
132
133 Ok(found)
134 }
135}
136
137#[cfg(test)]
138mod tests;