1mod local;
19
20mod distributed;
21
22mod format;
23
24#[cfg(feature = "experimental_datastorage_formats")]
25pub use format::{Format, MessagePack};
26use std::marker::PhantomData;
27
28#[cfg(feature = "ll")]
29pub mod ll {
31 pub mod local {
33 pub use crate::local::*;
34 }
35
36 pub mod distributed {
38 pub use crate::distributed::*;
39 }
40}
41
42use pdk_core::classy::extract::context::ConfigureContext;
43use pdk_core::classy::extract::{Extract, FromContext};
44use pdk_core::logger;
45use serde::{de::DeserializeOwned, Serialize};
46use std::rc::Rc;
47use thiserror::Error;
48use url::form_urlencoded;
49
50use crate::distributed::DistributedStorage;
51use crate::format::{DefaultFormat, FormatWrapper};
52use crate::local::LocalStorage;
53
54#[derive(PartialEq, Eq, Debug, Clone)]
59pub enum StoreMode {
60 Always,
65 Absent,
70 Cas(String),
75}
76
77#[derive(Debug, Error)]
82#[non_exhaustive]
83pub enum DataStorageError {
84 #[error("CAS mismatch.")]
86 CasMismatch,
87 #[error("Serialization error: {0}.")]
88 #[cfg(feature = "experimental_datastorage_formats")]
89 Format(Box<dyn std::error::Error>),
91 #[error("Serialization error: {0}.")]
92 Serialization(#[from] serde_fixint::Error),
94 #[error("CAS parse error: {0}.")]
96 CasParseError(#[from] std::num::ParseIntError),
97 #[error("Timeout.")]
99 Timeout,
100 #[error("HTTP Client Error.")]
102 HttpClient,
103 #[error("Unexpected error: {0}.")]
105 Unexpected(String),
106}
107
108#[derive(Debug, Error)]
113#[non_exhaustive]
114pub enum DataStorageBuilderError {
115 #[error("Local storage not available")]
117 LocalStorageRequired,
118
119 #[error("Policy metadata not available")]
121 MetadataRequired,
122}
123
124impl From<crate::local::LocalStorageError> for DataStorageError {
125 fn from(error: crate::local::LocalStorageError) -> Self {
126 match error {
127 crate::local::LocalStorageError::CasMismatch => DataStorageError::CasMismatch,
128 _ => DataStorageError::Unexpected(error.to_string()),
129 }
130 }
131}
132
133impl From<crate::distributed::DistributedStorageError> for DataStorageError {
134 fn from(error: crate::distributed::DistributedStorageError) -> Self {
135 match error {
136 crate::distributed::DistributedStorageError::CasMismatch => {
137 DataStorageError::CasMismatch
138 }
139 crate::distributed::DistributedStorageError::Timeout => DataStorageError::Timeout,
140 crate::distributed::DistributedStorageError::HttpClient(_) => {
141 DataStorageError::HttpClient
142 }
143 error => DataStorageError::Unexpected(error.to_string()),
144 }
145 }
146}
147
148#[allow(async_fn_in_trait)]
154pub trait DataStorage {
155 async fn get_keys(&self) -> Result<Vec<String>, DataStorageError>;
157
158 async fn store<T: Serialize>(
160 &self,
161 key: &str,
162 mode: &StoreMode,
163 item: &T,
164 ) -> Result<(), DataStorageError>;
165
166 async fn get<T: DeserializeOwned>(
169 &self,
170 key: &str,
171 ) -> Result<Option<(T, String)>, DataStorageError>;
172
173 async fn delete(&self, key: &str) -> Result<(), DataStorageError>;
175
176 async fn delete_all(&self) -> Result<(), DataStorageError>;
178}
179
180pub struct LocalDataStorage<F: format::Format = format::DefaultFormat> {
185 storage: crate::local::SharedData,
186 namespace: String,
187 format: FormatWrapper<F>,
188}
189
190impl<F: format::Format> LocalDataStorage<F> {
191 pub(crate) fn new(
193 storage: crate::local::SharedData,
194 namespace: String,
195 format: FormatWrapper<F>,
196 ) -> Self {
197 Self {
198 storage,
199 namespace,
200 format,
201 }
202 }
203
204 fn convert_store_mode(
205 &self,
206 mode: &StoreMode,
207 ) -> Result<crate::local::StoreMode, DataStorageError> {
208 match mode {
209 StoreMode::Always => Ok(crate::local::StoreMode::Always),
210 StoreMode::Absent => Ok(crate::local::StoreMode::Absent),
211 StoreMode::Cas(cas_str) => {
212 let cas: u32 = cas_str.parse()?;
213 Ok(crate::local::StoreMode::Cas(cas))
214 }
215 }
216 }
217
218 fn namespaced_key(&self, key: &str) -> String {
219 format!("{}:{}", self.namespace, key)
220 }
221}
222
223impl<F: format::Format> DataStorage for LocalDataStorage<F> {
224 async fn get_keys(&self) -> Result<Vec<String>, DataStorageError> {
225 let all_keys = self.storage.keys();
226 let namespace_prefix = format!("{}:", self.namespace);
227
228 let filtered_keys: Vec<String> = all_keys
230 .into_iter()
231 .filter(|key| key.starts_with(&namespace_prefix))
232 .map(|key| {
233 key.strip_prefix(&namespace_prefix)
235 .unwrap_or(&key)
236 .to_string()
237 })
238 .collect();
239
240 Ok(filtered_keys)
241 }
242
243 async fn store<T: Serialize>(
244 &self,
245 key: &str,
246 mode: &StoreMode,
247 item: &T,
248 ) -> Result<(), DataStorageError> {
249 let serialized = self.format.serialize(item)?;
250 let local_mode = self.convert_store_mode(mode)?;
251 let namespaced_key = self.namespaced_key(key);
252 self.storage.set(&namespaced_key, &serialized, local_mode)?;
253 Ok(())
254 }
255
256 async fn get<T: DeserializeOwned>(
257 &self,
258 key: &str,
259 ) -> Result<Option<(T, String)>, DataStorageError> {
260 let namespaced_key = self.namespaced_key(key);
261 match self.storage.get(&namespaced_key)? {
262 Some((data, cas)) => {
263 let deserialized: T = self.format.deserialize(&data)?;
264 Ok(Some((deserialized, cas.to_string())))
265 }
266 None => Ok(None),
267 }
268 }
269
270 async fn delete(&self, key: &str) -> Result<(), DataStorageError> {
271 let namespaced_key = self.namespaced_key(key);
272 self.storage.delete(&namespaced_key)?;
273 Ok(())
274 }
275
276 async fn delete_all(&self) -> Result<(), DataStorageError> {
277 let all_keys = self.storage.keys();
278 let namespace_prefix = format!("{}:", self.namespace);
279
280 for key in all_keys {
282 if key.starts_with(&namespace_prefix) {
283 self.storage.delete(&key)?;
284 }
285 }
286 Ok(())
287 }
288}
289
290pub struct RemoteDataStorage<F: format::Format = format::DefaultFormat> {
292 storage: Rc<crate::distributed::DistributedStorageClient>,
293 sanitized_store: String,
294 sanitized_partition: String,
295 ttl_millis: u32,
296 format: FormatWrapper<F>,
297}
298
299impl<F: format::Format> RemoteDataStorage<F> {
300 pub(crate) fn new(
303 storage: Rc<crate::distributed::DistributedStorageClient>,
304 store: String,
305 partition: String,
306 ttl_millis: u32,
307 format: FormatWrapper<F>,
308 ) -> Self {
309 let sanitized_store = form_urlencoded::byte_serialize(store.as_bytes()).collect();
311 let sanitized_partition = form_urlencoded::byte_serialize(partition.as_bytes()).collect();
312 Self {
313 storage,
314 sanitized_store,
315 sanitized_partition,
316 ttl_millis,
317 format,
318 }
319 }
320
321 fn convert_store_mode(&self, mode: &StoreMode) -> crate::distributed::StoreMode {
322 match mode {
323 StoreMode::Always => crate::distributed::StoreMode::Always,
324 StoreMode::Absent => crate::distributed::StoreMode::Absent,
325 StoreMode::Cas(cas_str) => crate::distributed::StoreMode::Cas(cas_str.clone()),
326 }
327 }
328
329 fn sanitize_key(&self, key: &str) -> String {
330 form_urlencoded::byte_serialize(key.as_bytes()).collect()
331 }
332}
333
334impl<F: format::Format> DataStorage for RemoteDataStorage<F> {
335 async fn get_keys(&self) -> Result<Vec<String>, DataStorageError> {
336 match self
338 .storage
339 .get_keys(&self.sanitized_store, &self.sanitized_partition)
340 .await
341 {
342 Ok(keys) => {
343 let decoded_keys: Vec<String> = keys
345 .into_iter()
346 .filter_map(|encoded_key| {
347 let decoded = form_urlencoded::parse(encoded_key.as_bytes())
348 .next()
349 .map(|(key, _)| key.into_owned());
350
351 if decoded.is_none() {
352 logger::debug!("Key not URL-encoded or decode failed: {encoded_key}");
353 }
354
355 decoded
356 })
357 .collect();
358
359 Ok(decoded_keys)
360 }
361 Err(e) => {
362 logger::warn!("Error getting keys: {e}");
363 Ok(vec![])
364 }
365 }
366 }
367
368 async fn store<T: Serialize>(
369 &self,
370 key: &str,
371 mode: &StoreMode,
372 item: &T,
373 ) -> Result<(), DataStorageError> {
374 let serialized = self.format.serialize(item)?;
375 let distributed_mode = self.convert_store_mode(mode);
376 let sanitized_key = self.sanitize_key(key);
377
378 match self
380 .storage
381 .store(
382 &self.sanitized_store,
383 &self.sanitized_partition,
384 &sanitized_key,
385 &distributed_mode,
386 &serialized,
387 )
388 .await
389 {
390 Ok(()) => Ok(()),
391 Err(crate::distributed::DistributedStorageError::StoreNotFound) => {
392 let store = crate::distributed::Store::new(
394 self.sanitized_store.clone(),
395 Some(self.ttl_millis),
396 None,
397 );
398
399 if let Err(e) = self.storage.upsert_store(&store).await {
401 logger::warn!("Error creating store: {e}");
402 }
403
404 self.storage
406 .store(
407 &self.sanitized_store,
408 &self.sanitized_partition,
409 &sanitized_key,
410 &distributed_mode,
411 &serialized,
412 )
413 .await?;
414 Ok(())
415 }
416 Err(e) => Err(e.into()), }
418 }
419
420 async fn get<T: DeserializeOwned>(
421 &self,
422 key: &str,
423 ) -> Result<Option<(T, String)>, DataStorageError> {
424 let sanitized_key = self.sanitize_key(key);
425 match self
426 .storage
427 .get(
428 &self.sanitized_store,
429 &self.sanitized_partition,
430 &sanitized_key,
431 )
432 .await
433 {
434 Ok((data, cas)) => {
435 let deserialized: T = self.format.deserialize(&data)?;
436 Ok(Some((deserialized, cas)))
437 }
438 Err(crate::distributed::DistributedStorageError::StoreNotFound) => {
439 logger::debug!("Store not found for key {key}, returning None");
440 Ok(None)
441 }
442 Err(crate::distributed::DistributedStorageError::KeyNotFound) => {
443 logger::debug!("Key not found: {key}");
444 Ok(None)
445 }
446 Err(e) => {
447 logger::error!("Error getting value for key {key}: {e:?}");
448 Err(e.into())
449 }
450 }
451 }
452
453 async fn delete(&self, key: &str) -> Result<(), DataStorageError> {
454 let sanitized_key = self.sanitize_key(key);
456 if let Err(e) = self
457 .storage
458 .delete(
459 &self.sanitized_store,
460 &self.sanitized_partition,
461 &sanitized_key,
462 )
463 .await
464 {
465 logger::warn!("Error deleting key {key}: {e}");
466 }
467 Ok(())
468 }
469
470 async fn delete_all(&self) -> Result<(), DataStorageError> {
471 if let Err(e) = self
473 .storage
474 .delete_partition(&self.sanitized_store, &self.sanitized_partition)
475 .await
476 {
477 logger::warn!("Error deleting partition: {e}");
478 }
479 Ok(())
480 }
481}
482
483pub struct DataStorageBuilder<F: format::Format = format::DefaultFormat> {
502 prefix: String,
503 shared_data: Rc<crate::local::SharedData>,
504 distributed_storage: Option<Rc<crate::distributed::DistributedStorageClient>>,
505 format_wrapper: FormatWrapper<F>,
506}
507
508impl FromContext<ConfigureContext> for DataStorageBuilder<DefaultFormat> {
519 type Error = DataStorageBuilderError;
520
521 fn from_context(context: &ConfigureContext) -> Result<Self, Self::Error> {
522 let shared_data: crate::local::SharedData = context
524 .extract()
525 .map_err(|_| DataStorageBuilderError::LocalStorageRequired)?;
526 let distributed_storage: Result<crate::distributed::DistributedStorageClient, _> =
528 context.extract();
529 let metadata: pdk_core::policy_context::api::Metadata = context
531 .extract()
532 .map_err(|_| DataStorageBuilderError::MetadataRequired)?;
533
534 let prefix = format!(
535 "isolated-storage-{}-{}",
536 metadata.policy_metadata.policy_name, metadata.policy_metadata.policy_namespace
537 );
538
539 pdk_core::logger::debug!(
540 "DataStorageBuilder: creating prefix '{}' for policy '{}' in namespace '{}'",
541 prefix,
542 metadata.policy_metadata.policy_name,
543 metadata.policy_metadata.policy_namespace
544 );
545
546 Ok(DataStorageBuilder {
547 prefix,
548 shared_data: Rc::new(shared_data),
549 distributed_storage: distributed_storage.ok().map(Rc::new),
550 format_wrapper: FormatWrapper::Default(DefaultFormat::default(), PhantomData),
551 })
552 }
553}
554
555impl<F: format::Format + Clone> DataStorageBuilder<F> {
556 pub fn shared(mut self) -> Self {
560 self.prefix = "shared-storage".to_string();
561 self
562 }
563
564 #[cfg(feature = "experimental_datastorage_formats")]
566 pub fn format<H: Format + Clone>(self, format: H) -> DataStorageBuilder<H> {
567 DataStorageBuilder {
568 prefix: self.prefix,
569 shared_data: self.shared_data,
570 distributed_storage: self.distributed_storage,
571 format_wrapper: FormatWrapper::Custom(format),
572 }
573 }
574
575 pub fn local<T: Into<String>>(&self, key: T) -> LocalDataStorage<F> {
577 let key_str = key.into();
578 let namespace = format!("{}-{}", self.prefix, key_str);
581
582 pdk_core::logger::debug!(
583 "DataStorageBuilder::local: creating namespace '{}' with prefix '{}' and key '{}'",
584 namespace,
585 self.prefix,
586 key_str
587 );
588
589 LocalDataStorage::new(
590 (*self.shared_data).clone(),
591 namespace,
592 self.format_wrapper.clone(),
593 )
594 }
595
596 pub fn remote<T: Into<String>>(&self, key: T, ttl_millis: u32) -> RemoteDataStorage<F> {
605 let key_str = key.into();
606 let storage = self
607 .distributed_storage
608 .as_ref()
609 .expect("Distributed storage not available - check if it's configured");
610 RemoteDataStorage::new(
611 Rc::clone(storage),
612 key_str.clone(),
613 key_str,
614 ttl_millis,
615 self.format_wrapper.clone(),
616 )
617 }
618}