spacegate_plugin/plugins/
static_resource.rs1use std::path::PathBuf;
2
3use hyper::{
4 body::Bytes,
5 header::{HeaderValue, CONTENT_TYPE},
6 Response, StatusCode,
7};
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use spacegate_kernel::{extension::Reflect, BoxError, SgBody};
11
12use crate::{Plugin, PluginError};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
17pub struct StaticResourceConfig {
18 pub code: u16,
20 pub content_type: String,
22 pub body: BodyEnum,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
28#[serde(tag = "kind", content = "value")]
29pub enum BodyEnum {
30 Json(Value),
32 Text(String),
34 File(PathBuf),
36}
37
38#[derive(Debug, Clone)]
39pub struct StaticResource {
40 pub code: StatusCode,
41 pub content_type: HeaderValue,
42 pub body: Bytes,
43}
44
45pub struct StaticResourcePlugin {
46 pub code: StatusCode,
47 pub content_type: HeaderValue,
48 pub body: Bytes,
49}
50
51impl Plugin for StaticResourcePlugin {
52 const CODE: &'static str = "static-resource";
53 #[cfg(feature = "schema")]
54 fn schema_opt() -> Option<schemars::schema::RootSchema> {
55 Some(<Self as crate::PluginSchemaExt>::schema())
56 }
57
58 async fn call(&self, req: hyper::Request<SgBody>, _inner: spacegate_kernel::helper_layers::function::Inner) -> Result<Response<SgBody>, BoxError> {
59 let mut resp = Response::builder()
60 .header(CONTENT_TYPE, self.content_type.clone())
61 .status(self.code)
62 .body(SgBody::full(self.body.clone()))
63 .map_err(PluginError::internal_error::<StaticResourcePlugin>)?;
64 if let Some(reflect) = req.into_parts().0.extensions.remove::<Reflect>() {
65 resp.extensions_mut().extend(reflect.into_inner());
66 }
67 Ok(resp)
68 }
69
70 fn create(config: crate::PluginConfig) -> Result<Self, spacegate_kernel::BoxError> {
71 let plugin_config: StaticResourceConfig = serde_json::from_value(config.spec)?;
72 let content_type = plugin_config.content_type.clone();
73 let content_type = HeaderValue::from_maybe_shared(content_type)?;
74 let body = match &plugin_config.body {
75 BodyEnum::Json(value) => Bytes::copy_from_slice(value.to_string().as_bytes()),
76 BodyEnum::Text(text) => Bytes::copy_from_slice(text.as_bytes()),
77 BodyEnum::File(path) => Bytes::copy_from_slice(&std::fs::read(path)?),
78 };
79 let code = StatusCode::from_u16(plugin_config.code)?;
80 Ok(Self { content_type, code, body })
81 }
82}
83
84#[cfg(feature = "schema")]
85crate::schema!(StaticResourcePlugin, StaticResourceConfig);