harmos_runtime/storage/
capture.rs1use super::manager::encoded_size;
22use super::state::Permit;
23use super::{ReadLimits, RecordInput};
24use crate::{Error, Result, state::Envelope};
25use std::{future::Future, pin::Pin};
26
27pub struct ProjectedRecords<'a> {
36 permit: &'a mut Permit,
37 limits: ReadLimits,
38 refused: bool,
39}
40
41pub(crate) struct Projection {
42 pub(crate) limits: ReadLimits,
43 pub(crate) handler: Box<dyn RecordProjection>,
44}
45
46pub trait RecordProjection: Send + Sync + 'static {
51 fn project<'a>(
52 &'a self, record: &'a Envelope<&serde_json::Value>, output: &'a mut ProjectedRecords<'_>,
53 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>;
54}
55
56impl<'a> ProjectedRecords<'a> {
59 pub(crate) fn new(permit: &'a mut Permit, limits: ReadLimits) -> Self {
60 Self { permit, limits, refused: false }
61 }
62
63 #[tracing::instrument(target = "harmos::storage", name = "storage.push", level = "debug", skip_all)]
64 pub async fn push(&mut self, record: RecordInput) -> Result<()> {
65 if self.refused {
66 return Err(limit());
67 }
68 self.refused = true;
69 let result = self.permit.push(record, self.limits).await;
70 self.refused = result.is_err();
71 result.map_err(Into::into)
72 }
73
74 pub(crate) fn check(&self, maximum: std::num::NonZeroUsize) -> Result<()> {
75 if self.refused {
76 return Err(limit());
77 }
78 for entry in self.permit.projected() {
79 if encoded_size(entry)? > maximum.get() {
80 return Err(Error::InvalidInput { field: "record projection", reason: "one entry exceeds max_batch_bytes" });
81 }
82 }
83 Ok(())
84 }
85}
86
87fn limit() -> Error {
92 Error::InvalidInput { field: "record projection", reason: "capture limit exceeded" }
93}
94
95