spicedb_embedded_sys/
memory.rs1use 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#[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
29pub type RpcResult<T> = Result<T, RpcError>;
31
32fn 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 request_len = c_int::try_from(request_bytes.len())
46 .map_err(|_| RpcError("request too large for FFI (exceeds c_int::MAX)".into()))?;
47
48 let mut out_response_bytes: *mut c_uchar = std::ptr::null_mut();
49 let mut out_response_len: c_int = 0;
50 let mut out_error: *mut c_char = std::ptr::null_mut();
51
52 unsafe {
53 f(
54 handle as c_ulonglong,
55 request_bytes.as_ptr(),
56 request_len,
57 &mut out_response_bytes,
58 &mut out_response_len,
59 &mut out_error,
60 );
61 }
62
63 if !out_error.is_null() {
64 let s = unsafe { std::ffi::CStr::from_ptr(out_error) }
65 .to_string_lossy()
66 .into_owned();
67 unsafe { crate::spicedb_free(out_error) };
68 return Err(RpcError(s));
69 }
70
71 if out_response_len < 0 {
72 return Err(RpcError(format!(
73 "FFI returned negative response length: {}",
74 out_response_len
75 )));
76 }
77 let len = out_response_len as usize;
78 let bytes = if len == 0 {
79 vec![]
80 } else {
81 unsafe { std::slice::from_raw_parts(out_response_bytes, len).to_vec() }
82 };
83 if !out_response_bytes.is_null() {
84 unsafe { crate::spicedb_free_bytes(out_response_bytes as *mut std::ffi::c_void) };
85 }
86 Ok(bytes)
87}
88
89pub fn check_permission(
93 handle: u64,
94 request: &CheckPermissionRequest,
95) -> RpcResult<CheckPermissionResponse> {
96 let mut buf = Vec::new();
97 request
98 .encode(&mut buf)
99 .map_err(|e| RpcError(e.to_string()))?;
100 let bytes = raw_call(handle, &buf, crate::spicedb_permissions_check_permission)?;
101 CheckPermissionResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
102}
103
104pub fn write_relationships(
106 handle: u64,
107 request: &WriteRelationshipsRequest,
108) -> RpcResult<WriteRelationshipsResponse> {
109 let mut buf = Vec::new();
110 request
111 .encode(&mut buf)
112 .map_err(|e| RpcError(e.to_string()))?;
113 let bytes = raw_call(handle, &buf, crate::spicedb_permissions_write_relationships)?;
114 WriteRelationshipsResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
115}
116
117pub fn delete_relationships(
119 handle: u64,
120 request: &DeleteRelationshipsRequest,
121) -> RpcResult<DeleteRelationshipsResponse> {
122 let mut buf = Vec::new();
123 request
124 .encode(&mut buf)
125 .map_err(|e| RpcError(e.to_string()))?;
126 let bytes = raw_call(
127 handle,
128 &buf,
129 crate::spicedb_permissions_delete_relationships,
130 )?;
131 DeleteRelationshipsResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
132}
133
134pub fn check_bulk_permissions(
136 handle: u64,
137 request: &CheckBulkPermissionsRequest,
138) -> RpcResult<CheckBulkPermissionsResponse> {
139 let mut buf = Vec::new();
140 request
141 .encode(&mut buf)
142 .map_err(|e| RpcError(e.to_string()))?;
143 let bytes = raw_call(
144 handle,
145 &buf,
146 crate::spicedb_permissions_check_bulk_permissions,
147 )?;
148 CheckBulkPermissionsResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
149}
150
151pub fn expand_permission_tree(
153 handle: u64,
154 request: &ExpandPermissionTreeRequest,
155) -> RpcResult<ExpandPermissionTreeResponse> {
156 let mut buf = Vec::new();
157 request
158 .encode(&mut buf)
159 .map_err(|e| RpcError(e.to_string()))?;
160 let bytes = raw_call(
161 handle,
162 &buf,
163 crate::spicedb_permissions_expand_permission_tree,
164 )?;
165 ExpandPermissionTreeResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
166}
167
168pub fn read_schema(handle: u64, request: &ReadSchemaRequest) -> RpcResult<ReadSchemaResponse> {
172 let mut buf = Vec::new();
173 request
174 .encode(&mut buf)
175 .map_err(|e| RpcError(e.to_string()))?;
176 let bytes = raw_call(handle, &buf, crate::spicedb_schema_read_schema)?;
177 ReadSchemaResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
178}
179
180pub fn write_schema(handle: u64, request: &WriteSchemaRequest) -> RpcResult<WriteSchemaResponse> {
182 let mut buf = Vec::new();
183 request
184 .encode(&mut buf)
185 .map_err(|e| RpcError(e.to_string()))?;
186 let bytes = raw_call(handle, &buf, crate::spicedb_schema_write_schema)?;
187 WriteSchemaResponse::decode(&bytes[..]).map_err(|e| RpcError(e.to_string()))
188}