boundless_market/request_builder/
storage_layer.rs1use super::{Adapt, Layer, RequestParams};
16use crate::{
17 contracts::RequestInput, input::GuestEnv, storage::StorageUploader, util::NotProvided,
18 StandardUploader,
19};
20use anyhow::{bail, Context};
21use derive_builder::Builder;
22use url::Url;
23
24#[non_exhaustive]
28#[derive(Clone, Builder)]
29pub struct StorageLayerConfig {
30 #[builder(setter(into), default = "Some(2048)")]
35 pub inline_input_max_bytes: Option<usize>,
36}
37
38#[non_exhaustive]
44#[derive(Clone)]
45pub struct StorageLayer<U = StandardUploader> {
46 pub uploader: Option<U>,
51
52 pub config: StorageLayerConfig,
54}
55
56impl StorageLayerConfig {
57 pub fn builder() -> StorageLayerConfigBuilder {
62 Default::default()
63 }
64}
65
66impl<S: Clone> From<Option<S>> for StorageLayer<S> {
67 fn from(uploader: Option<S>) -> Self {
73 Self { uploader, config: Default::default() }
74 }
75}
76
77impl<S> From<StorageLayerConfig> for StorageLayer<S>
78where
79 S: StorageUploader + Default,
80{
81 fn from(config: StorageLayerConfig) -> Self {
82 Self { uploader: Some(Default::default()), config }
83 }
84}
85
86impl<S> Default for StorageLayer<S> {
87 fn default() -> Self {
88 Self { uploader: None, config: Default::default() }
89 }
90}
91
92impl From<StorageLayerConfig> for StorageLayer<NotProvided> {
93 fn from(config: StorageLayerConfig) -> Self {
94 Self { uploader: None, config }
95 }
96}
97
98impl Default for StorageLayerConfig {
99 fn default() -> Self {
100 Self::builder().build().expect("implementation error in Default for StorageLayerConfig")
101 }
102}
103
104impl<S> StorageLayer<S>
105where
106 S: StorageUploader,
107{
108 pub async fn process_program(&self, program: &[u8]) -> anyhow::Result<Url> {
113 let storage_uploader = self
114 .uploader
115 .as_ref()
116 .context("cannot upload program using StorageLayer with no storage_uploader")?;
117 let program_url = storage_uploader.upload_program(program).await?;
118 Ok(program_url)
119 }
120
121 pub async fn process_env(&self, env: &GuestEnv) -> anyhow::Result<RequestInput> {
126 let input_data = env.encode().context("failed to encode guest environment")?;
127 let request_input = match self.config.inline_input_max_bytes {
128 Some(limit) if input_data.len() > limit => {
129 let storage_uploader = self.uploader.as_ref().with_context( || {
130 format!("cannot upload input using StorageLayer with no storage_uploader; input length of {} bytes exceeds inline limit of {limit} bytes", input_data.len())
131 })?;
132 RequestInput::url(storage_uploader.upload_input(&input_data).await?)
133 }
134 _ => RequestInput::inline(input_data),
135 };
136 Ok(request_input)
137 }
138}
139
140impl<S> StorageLayer<S> {
141 pub fn new(uploader: Option<S>, config: StorageLayerConfig) -> Self {
145 Self { uploader, config }
146 }
147
148 pub(crate) async fn process_env_no_provider(
149 &self,
150 env: &GuestEnv,
151 ) -> anyhow::Result<RequestInput> {
152 let input_data = env.encode().context("failed to encode guest environment")?;
153 let request_input = match self.config.inline_input_max_bytes {
154 Some(limit) if input_data.len() > limit => {
155 bail!("cannot upload input using StorageLayer with no storage_uploader; input length of {} bytes exceeds inline limit of {limit} bytes", input_data.len());
156 }
157 _ => RequestInput::inline(input_data),
158 };
159 Ok(request_input)
160 }
161}
162
163impl<S> Layer<(&[u8], &GuestEnv)> for StorageLayer<S>
164where
165 S: StorageUploader,
166{
167 type Output = (Url, RequestInput);
168 type Error = anyhow::Error;
169
170 async fn process(
171 &self,
172 (program, env): (&[u8], &GuestEnv),
173 ) -> Result<Self::Output, Self::Error> {
174 let program_url = self.process_program(program).await?;
175 let request_input = self.process_env(env).await?;
176 Ok((program_url, request_input))
177 }
178}
179
180impl Layer<&GuestEnv> for StorageLayer<NotProvided> {
181 type Output = RequestInput;
182 type Error = anyhow::Error;
183
184 async fn process(&self, env: &GuestEnv) -> Result<Self::Output, Self::Error> {
185 let request_input = self.process_env_no_provider(env).await?;
186 Ok(request_input)
187 }
188}
189
190impl<S> Adapt<StorageLayer<S>> for RequestParams
191where
192 S: StorageUploader,
193{
194 type Output = RequestParams;
195 type Error = anyhow::Error;
196
197 async fn process_with(self, layer: &StorageLayer<S>) -> Result<Self::Output, Self::Error> {
198 tracing::trace!("Processing {self:?} with StorageLayer");
199
200 let mut params = self;
201 if params.program_url.is_none() {
202 let program_url = layer.process_program(params.require_program()?).await?;
203 params = params.with_program_url(program_url)?;
204 }
205 if params.request_input.is_none() {
206 let input = layer.process_env(params.require_env()?).await?;
207 params = params.with_request_input(input);
208 }
209 Ok(params)
210 }
211}
212
213impl Adapt<StorageLayer<NotProvided>> for RequestParams {
214 type Output = RequestParams;
215 type Error = anyhow::Error;
216
217 async fn process_with(
218 self,
219 layer: &StorageLayer<NotProvided>,
220 ) -> Result<Self::Output, Self::Error> {
221 tracing::trace!("Processing {self:?} with StorageLayer");
222
223 let mut params = self;
224 params
225 .require_program_url()
226 .context("program_url must be set when storage uploader is not provided")?;
227 if params.request_input.is_none() {
228 let input = layer.process_env_no_provider(params.require_env()?).await?;
229 params = params.with_request_input(input);
230 }
231 Ok(params)
232 }
233}