Skip to main content

fraiseql_functions/store/
mod.rs

1//! Function deployment store for persisting and retrieving compiled function artifacts.
2//!
3//! The store holds versioned function bytecode so the server can load functions at
4//! runtime without re-reading from disk or a remote registry.
5//!
6//! # Implementations
7//!
8//! - [`InMemoryFunctionStore`](memory::InMemoryFunctionStore) — for unit tests and local
9//!   development
10//! - `PgFunctionStore` — PostgreSQL-backed persistent store (feature: `host-live`)
11
12pub mod memory;
13
14use async_trait::async_trait;
15use fraiseql_error::Result;
16
17use crate::types::RuntimeType;
18
19/// Deployment status of a function version.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[non_exhaustive]
22pub enum FunctionStatus {
23    /// This version is the active (serving) version.
24    Active,
25    /// This version has been superseded by a newer deploy.
26    Inactive,
27}
28
29impl FunctionStatus {
30    /// Serialize as the DB column string.
31    #[must_use]
32    pub const fn as_str(self) -> &'static str {
33        match self {
34            Self::Active => "active",
35            Self::Inactive => "inactive",
36        }
37    }
38
39    /// Parse from a DB column string.
40    ///
41    /// Returns `None` if the value is unrecognized.
42    #[must_use]
43    pub fn parse(s: &str) -> Option<Self> {
44        match s {
45            "active" => Some(Self::Active),
46            "inactive" => Some(Self::Inactive),
47            _ => None,
48        }
49    }
50}
51
52/// A stored function record representing one deployed version.
53#[derive(Debug, Clone)]
54pub struct FunctionRecord {
55    /// Trinity-pattern primary key (`pk_function`).
56    pub pk_function: i64,
57    /// Unique function name (scoped per tenant).
58    pub name:        String,
59    /// Which runtime executes this function.
60    pub runtime:     RuntimeType,
61    /// Compiled bytecode or source text.
62    pub bytecode:    bytes::Bytes,
63    /// Monotonically increasing deploy version (1-based).
64    pub version:     i32,
65    /// When this version was deployed.
66    pub deployed_at: chrono::DateTime<chrono::Utc>,
67    /// Whether this version is active.
68    pub status:      FunctionStatus,
69}
70
71/// Trait for function deployment stores.
72///
73/// Implementors persist and retrieve versioned function bytecode.
74///
75/// The `#[async_trait]` attribute generates a dyn-compatible vtable so that
76/// `Box<dyn FunctionStore>` / `Arc<dyn FunctionStore>` work for dynamic dispatch.
77#[async_trait]
78pub trait FunctionStore: Send + Sync {
79    /// Store a new version of a function, bumping its version number.
80    ///
81    /// If a function with `name` already exists, a new version is created
82    /// and previous versions are marked `inactive`.
83    ///
84    /// # Errors
85    ///
86    /// Returns `Err` if the write fails.
87    async fn store_function(
88        &self,
89        name: &str,
90        runtime: RuntimeType,
91        bytecode: bytes::Bytes,
92    ) -> Result<FunctionRecord>;
93
94    /// Retrieve the latest active version of a function by name.
95    ///
96    /// Returns `Ok(None)` if no active function with that name exists.
97    ///
98    /// # Errors
99    ///
100    /// Returns `Err` if the read fails.
101    async fn get_function(&self, name: &str) -> Result<Option<FunctionRecord>>;
102
103    /// List all active function records.
104    ///
105    /// # Errors
106    ///
107    /// Returns `Err` if the read fails.
108    async fn list_functions(&self) -> Result<Vec<FunctionRecord>>;
109
110    /// Delete (deactivate) all versions of a function by name.
111    ///
112    /// Returns `true` if at least one active record was found and deactivated,
113    /// `false` if no active function with that name existed.
114    ///
115    /// # Errors
116    ///
117    /// Returns `Err` if the write fails.
118    async fn delete_function(&self, name: &str) -> Result<bool>;
119}