Skip to main content

spicedb_embedded_sys/
memory.rs

1//! Safe, typed wrappers for the memory-transport FFI.
2//!
3//! All marshalling/unmarshalling and unsafe FFI calls are contained here.
4//! The main crate uses these functions for idiomatic, safe APIs.
5
6use std::os::raw::{c_char, c_int, c_uchar, c_ulonglong};
7
8use prost::Message;
9use spicedb_grpc_tonic::v1::{
10    CheckBulkPermissionsRequest, CheckBulkPermissionsResponse, CheckPermissionRequest,
11    CheckPermissionResponse, DeleteRelationshipsRequest, DeleteRelationshipsResponse,
12    ExpandPermissionTreeRequest, ExpandPermissionTreeResponse, ReadSchemaRequest,
13    ReadSchemaResponse, WriteRelationshipsRequest, WriteRelationshipsResponse, WriteSchemaRequest,
14    WriteSchemaResponse,
15};
16
17/// Error from a memory-transport RPC (FFI or decode).
18#[derive(Debug, Clone)]
19pub struct RpcError(pub String);
20
21impl std::fmt::Display for RpcError {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        write!(f, "{}", self.0)
24    }
25}
26
27impl std::error::Error for RpcError {}
28
29/// Result type for memory-transport RPCs.
30pub type RpcResult<T> = Result<T, RpcError>;
31
32/// Raw call: request bytes in, response bytes or error string. Handles all unsafe FFI and buffer freeing.
33fn raw_call(
34    handle: u64,
35    request_bytes: &[u8],
36    f: unsafe extern "C" fn(
37        c_ulonglong,
38        *const c_uchar,
39        c_int,
40        *mut *mut c_uchar,
41        *mut c_int,
42        *mut *mut c_char,
43    ),
44) -> RpcResult<Vec<u8>> {
45    let mut out_response_bytes: *mut c_uchar = std::ptr::null_mut();
46    let mut out_response_len: c_int = 0;
47    let mut out_error: *mut c_char = std::ptr::null_mut();
48
49    unsafe {
50        f(
51            handle as c_ulonglong,
52            request_bytes.as_ptr(),
53            request_bytes.len() as c_int,
54            &mut out_response_bytes,
55            &mut out_response_len,
56            &mut out_error,
57        );
58    }
59
60    if !out_error.is_null() {
61        let s = unsafe { std::ffi::CStr::from_ptr(out_error) }
62            .to_string_lossy()
63            .into_owned();
64        unsafe { crate::spicedb_free(out_error) };
65        return Err(RpcError(s));
66    }
67
68    let len = out_response_len as usize;
69    let bytes = if len == 0 {
70        vec![]
71    } else {
72        unsafe { std::slice::from_raw_parts(out_response_bytes, len).to_vec() }
73    };
74    if !out_response_bytes.is_null() {
75        unsafe { crate::spicedb_free_bytes(out_response_bytes as *mut std::ffi::c_void) };
76    }
77    Ok(bytes)
78}
79
80// --- PermissionsService ---
81
82/// PermissionsService.CheckPermission.
83pub fn check_permission(
84    handle: u64,
85    request: &CheckPermissionRequest,
86) -> RpcResult<CheckPermissionResponse> {
87    let mut buf = Vec::new();
88    request
89        .encode(&mut buf)
90        .map_err(|e| RpcError(e.to_string()))?;
91    let bytes = raw_call(handle, &buf, crate::spicedb_permissions_check_permission)?;
92    CheckPermissionResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
93}
94
95/// PermissionsService.WriteRelationships.
96pub fn write_relationships(
97    handle: u64,
98    request: &WriteRelationshipsRequest,
99) -> RpcResult<WriteRelationshipsResponse> {
100    let mut buf = Vec::new();
101    request
102        .encode(&mut buf)
103        .map_err(|e| RpcError(e.to_string()))?;
104    let bytes = raw_call(handle, &buf, crate::spicedb_permissions_write_relationships)?;
105    WriteRelationshipsResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
106}
107
108/// PermissionsService.DeleteRelationships.
109pub fn delete_relationships(
110    handle: u64,
111    request: &DeleteRelationshipsRequest,
112) -> RpcResult<DeleteRelationshipsResponse> {
113    let mut buf = Vec::new();
114    request
115        .encode(&mut buf)
116        .map_err(|e| RpcError(e.to_string()))?;
117    let bytes = raw_call(
118        handle,
119        &buf,
120        crate::spicedb_permissions_delete_relationships,
121    )?;
122    DeleteRelationshipsResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
123}
124
125/// PermissionsService.CheckBulkPermissions.
126pub fn check_bulk_permissions(
127    handle: u64,
128    request: &CheckBulkPermissionsRequest,
129) -> RpcResult<CheckBulkPermissionsResponse> {
130    let mut buf = Vec::new();
131    request
132        .encode(&mut buf)
133        .map_err(|e| RpcError(e.to_string()))?;
134    let bytes = raw_call(
135        handle,
136        &buf,
137        crate::spicedb_permissions_check_bulk_permissions,
138    )?;
139    CheckBulkPermissionsResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
140}
141
142/// PermissionsService.ExpandPermissionTree.
143pub fn expand_permission_tree(
144    handle: u64,
145    request: &ExpandPermissionTreeRequest,
146) -> RpcResult<ExpandPermissionTreeResponse> {
147    let mut buf = Vec::new();
148    request
149        .encode(&mut buf)
150        .map_err(|e| RpcError(e.to_string()))?;
151    let bytes = raw_call(
152        handle,
153        &buf,
154        crate::spicedb_permissions_expand_permission_tree,
155    )?;
156    ExpandPermissionTreeResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
157}
158
159// --- SchemaService ---
160
161/// SchemaService.ReadSchema.
162pub fn read_schema(handle: u64, request: &ReadSchemaRequest) -> RpcResult<ReadSchemaResponse> {
163    let mut buf = Vec::new();
164    request
165        .encode(&mut buf)
166        .map_err(|e| RpcError(e.to_string()))?;
167    let bytes = raw_call(handle, &buf, crate::spicedb_schema_read_schema)?;
168    ReadSchemaResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
169}
170
171/// SchemaService.WriteSchema.
172pub fn write_schema(handle: u64, request: &WriteSchemaRequest) -> RpcResult<WriteSchemaResponse> {
173    let mut buf = Vec::new();
174    request
175        .encode(&mut buf)
176        .map_err(|e| RpcError(e.to_string()))?;
177    let bytes = raw_call(handle, &buf, crate::spicedb_schema_write_schema)?;
178    WriteSchemaResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
179}